mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn.git
synced 2025-11-03 05:33:23 +00:00
Compare commits
110 Commits
pespin/dis
...
1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5a268a96d | ||
|
|
6da888c5d0 | ||
|
|
33c537e5a7 | ||
|
|
aa69034c00 | ||
|
|
bf47f71785 | ||
|
|
2b7a860ffb | ||
|
|
932eeec240 | ||
|
|
6f7aabf6a3 | ||
|
|
a491e42129 | ||
|
|
1ce111f72b | ||
|
|
e010dea56e | ||
|
|
f4530447f6 | ||
|
|
e7361067ac | ||
|
|
606837597f | ||
|
|
5f8b332e6b | ||
|
|
43001cbc7a | ||
|
|
65d61c347b | ||
|
|
c8ca02b937 | ||
|
|
3ce5a3648a | ||
|
|
a4cb02699e | ||
|
|
f0fb2c2ddd | ||
|
|
8a1e7b8658 | ||
|
|
b7782d4d41 | ||
|
|
b0b9c28284 | ||
|
|
3730c550cd | ||
|
|
cc8181fefe | ||
|
|
7327360d10 | ||
|
|
e405c2f196 | ||
|
|
411ff3b984 | ||
|
|
aee905b790 | ||
|
|
fb75adfeda | ||
|
|
7b9230acfe | ||
|
|
5662cb2152 | ||
|
|
e1412d9493 | ||
|
|
d1e2342f91 | ||
|
|
381b723543 | ||
|
|
ee44b82b96 | ||
|
|
b5f93346df | ||
|
|
8e8c7ef3c7 | ||
|
|
57238889eb | ||
|
|
d70ab97fa4 | ||
|
|
d1bd6fce9c | ||
|
|
a32e4c4fb8 | ||
|
|
3b84e92ab3 | ||
|
|
3e0baa6146 | ||
|
|
b673d1c438 | ||
|
|
6a2856bab5 | ||
|
|
0d95ca59f9 | ||
|
|
906c2099da | ||
|
|
ac07625086 | ||
|
|
36c4fac9c9 | ||
|
|
a06b2d3877 | ||
|
|
546884d9a1 | ||
|
|
f2286395e9 | ||
|
|
9eebe15cd1 | ||
|
|
31e1dab2c0 | ||
|
|
db0366c9e4 | ||
|
|
47adad0817 | ||
|
|
c5efb5bccb | ||
|
|
9a6da455b9 | ||
|
|
b4c0828039 | ||
|
|
df3dcac439 | ||
|
|
0757504a86 | ||
|
|
042a445cf3 | ||
|
|
a16c7501a4 | ||
|
|
9f98822255 | ||
|
|
fc8357a2db | ||
|
|
3e443ca502 | ||
|
|
2c10211d60 | ||
|
|
5fdda13f89 | ||
|
|
dbeaa044f8 | ||
|
|
7ad4d5e8cb | ||
|
|
ab4db10750 | ||
|
|
dddbbaaee1 | ||
|
|
134855c45e | ||
|
|
a4942e6566 | ||
|
|
4e43ef5ab0 | ||
|
|
4ae8d8232d | ||
|
|
0bdd8bf5bc | ||
|
|
5b1ef9589c | ||
|
|
7d54ed48e7 | ||
|
|
07730bb9cc | ||
|
|
7b38af5cd3 | ||
|
|
85ef5833cb | ||
|
|
a2a08f7602 | ||
|
|
282d4e3dda | ||
|
|
42d3250d17 | ||
|
|
5aed8de11d | ||
|
|
5f5fcff5f3 | ||
|
|
a884a95a7b | ||
|
|
a4aada0b5f | ||
|
|
732131d4d0 | ||
|
|
36b940d1fe | ||
|
|
e661277b48 | ||
|
|
6f539aa259 | ||
|
|
1c8c62667f | ||
|
|
e5a082d64a | ||
|
|
37c45e3998 | ||
|
|
f5e40b7011 | ||
|
|
02e21af657 | ||
|
|
bffc3f9012 | ||
|
|
7c4de0776b | ||
|
|
077b903e11 | ||
|
|
2d6a69e69a | ||
|
|
4f0343233b | ||
|
|
bcab7fb4af | ||
|
|
427699e6eb | ||
|
|
9c0f4f49e9 | ||
|
|
ac51c7e68e | ||
|
|
55d639f0fb |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -69,3 +69,14 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
SUBDIRS = lib gtp ggsn sgsnemu doc tests
|
||||
SUBDIRS = lib gtp ggsn sgsnemu doc contrib tests
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libgtp.pc
|
||||
@@ -12,4 +12,7 @@ dist-hook:
|
||||
|
||||
EXTRA_DIST = git-version-gen .version README.md README.FreeBSD README.MacOSX
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
@@ -263,8 +263,7 @@ following:
|
||||
|
||||
1. Install sgsnemu on a Linux Box. See under installation above.
|
||||
2. Connect your Linux box with sgsnemu installed to the GPRS core
|
||||
network. Use the same LAN switch as the one your SGSN is connected
|
||||
to. You also need a free IP address that can be used by sgsnemu.
|
||||
network. You also need a free IP address that can be used by sgsnemu.
|
||||
3. You need to configure networking in terms of interface address,
|
||||
subnet mask and default route. See the Linux Networking HOWTO for
|
||||
details.
|
||||
|
||||
97
configure.ac
97
configure.ac
@@ -18,7 +18,6 @@ AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_AWK
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
LT_INIT
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
@@ -39,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
|
||||
|
||||
@@ -66,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.0.0])
|
||||
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.2.0])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
|
||||
@@ -127,7 +126,7 @@ AC_EGREP_HEADER(struct iphdr, netinet/ip.h,
|
||||
# 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)
|
||||
|
||||
@@ -136,9 +135,9 @@ adl_FUNC_GETOPT_LONG
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -152,6 +151,85 @@ then
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
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"])
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
@@ -161,6 +239,9 @@ AC_CONFIG_FILES([Makefile
|
||||
intl/Makefile
|
||||
po/Makefile
|
||||
sgsnemu/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
tests/Makefile
|
||||
tests/lib/Makefile
|
||||
tests/gtp/Makefile
|
||||
|
||||
1
contrib/Makefile.am
Normal file
1
contrib/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
SUBDIRS = systemd
|
||||
@@ -1,5 +1,11 @@
|
||||
#!/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 !"
|
||||
@@ -18,12 +24,23 @@ osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
|
||||
if [ "x$GTP" == "x--enable-gtp-linux" ]; then
|
||||
osmo-build-dep.sh libgtpnl
|
||||
fi
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
|
||||
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
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo
|
||||
@@ -35,8 +52,12 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure CFLAGS="-Werror" CPPFLAGS="-Werror" $GTP
|
||||
./configure --enable-sanitize --enable-werror $GTP $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE distcheck
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
6
contrib/systemd/Makefile.am
Normal file
6
contrib/systemd/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
EXTRA_DIST = osmo-ggsn.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
osmo-ggsn.service
|
||||
endif
|
||||
@@ -5,7 +5,7 @@ After=networking.service
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg -f
|
||||
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
||||
185
debian/changelog
vendored
185
debian/changelog
vendored
@@ -1,3 +1,188 @@
|
||||
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
|
||||
|
||||
8
debian/control
vendored
8
debian/control
vendored
@@ -22,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: libgtp2
|
||||
Package: libgtp4
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
@@ -41,7 +41,7 @@ Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libdevel
|
||||
Depends: ${misc:Depends},
|
||||
libgtp2 (= ${binary:Version})
|
||||
libgtp4 (= ${binary:Version})
|
||||
Description: Development files for libgtp
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
operators as the interface between the Internet and the rest of the
|
||||
@@ -54,7 +54,7 @@ Package: osmo-ggsn-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp4 (= ${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
|
||||
@@ -65,7 +65,7 @@ Package: libgtp-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp4 (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for OsmoGGSN
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
|
||||
2
debian/osmo-ggsn.install
vendored
2
debian/osmo-ggsn.install
vendored
@@ -1,3 +1,5 @@
|
||||
/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
1
debian/osmo-ggsn.service
vendored
@@ -1 +0,0 @@
|
||||
../contrib/osmo-ggsn.service
|
||||
7
debian/rules
vendored
7
debian/rules
vendored
@@ -16,8 +16,7 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
|
||||
dh_strip -plibgtp2 --dbg-package=libgtp-dbg
|
||||
dh_strip -plibgtp4 --dbg-package=libgtp-dbg
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||
|
||||
@@ -4,4 +4,5 @@ EXTRA_DIST = $(man_MANS)
|
||||
|
||||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
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:
|
||||
|
||||
@@ -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
|
||||
!
|
||||
|
||||
16
doc/manuals/Makefile.am
Normal file
16
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,16 @@
|
||||
EXTRA_DIST = osmoggsn-usermanual.adoc \
|
||||
osmoggsn-usermanual-docinfo.xml \
|
||||
osmoggsn-vty-reference.xml \
|
||||
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
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
endif
|
||||
335
doc/manuals/chapters/configuration.adoc
Normal file
335
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,335 @@
|
||||
== 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.0/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 <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
|
||||
----
|
||||
145
doc/manuals/chapters/overview.adoc
Normal file
145
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,145 @@
|
||||
[[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.
|
||||
82
doc/manuals/chapters/running.adoc
Normal file
82
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,82 @@
|
||||
== 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 OsmoBTS 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
|
||||
----
|
||||
46
doc/manuals/osmoggsn-usermanual-docinfo.xml
Normal file
46
doc/manuals/osmoggsn-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<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>
|
||||
29
doc/manuals/osmoggsn-usermanual.adoc
Normal file
29
doc/manuals/osmoggsn-usermanual.adoc
Normal file
@@ -0,0 +1,29 @@
|
||||
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/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
|
||||
include::./common/chapters/glossary.adoc[]
|
||||
|
||||
include::./common/chapters/gfdl.adoc[]
|
||||
38
doc/manuals/osmoggsn-vty-reference.xml
Normal file
38
doc/manuals/osmoggsn-vty-reference.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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>
|
||||
|
||||
30
doc/manuals/vty/ggsn_vty_additions.xml
Normal file
30
doc/manuals/vty/ggsn_vty_additions.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<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>
|
||||
1447
doc/manuals/vty/ggsn_vty_reference.xml
Normal file
1447
doc/manuals/vty/ggsn_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,13 +7,9 @@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb
|
||||
osmo_ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
AM_CFLAGS += -DGTP_KERNEL
|
||||
osmo_ggsn_LDADD += -lgtpnl
|
||||
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||
osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
|
||||
endif
|
||||
|
||||
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
|
||||
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
osmo_ggsn_SOURCES += gtp-kernel.c
|
||||
endif
|
||||
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h
|
||||
|
||||
487
ggsn/ggsn.c
487
ggsn/ggsn.c
@@ -56,15 +56,16 @@
|
||||
#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"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../lib/gtp-kernel.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
#include "gtp-kernel.h"
|
||||
#include "icmpv6.h"
|
||||
#include "ggsn.h"
|
||||
|
||||
@@ -124,13 +125,14 @@ int apn_stop(struct apn_ctx *apn, bool force)
|
||||
LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
|
||||
tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
|
||||
}
|
||||
/* release tun device */
|
||||
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
|
||||
osmo_fd_unregister(&apn->tun.fd);
|
||||
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
|
||||
/* release tun device */
|
||||
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
|
||||
osmo_fd_unregister(&apn->tun.fd);
|
||||
}
|
||||
tun_free(apn->tun.tun);
|
||||
apn->tun.tun = NULL;
|
||||
}
|
||||
gtp_kernel_stop(apn->tun.cfg.dev_name);
|
||||
|
||||
if (apn->v4.pool) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Releasing IPv4 pool\n");
|
||||
@@ -194,6 +196,8 @@ int apn_start(struct apn_ctx *apn)
|
||||
struct in46_prefix ipv6_tun_linklocal_ip;
|
||||
struct in46_prefix *blacklist;
|
||||
int blacklist_size;
|
||||
struct gsn_t *gsn = apn->ggsn->gsn;
|
||||
int rc;
|
||||
|
||||
if (apn->started)
|
||||
return 0;
|
||||
@@ -202,7 +206,7 @@ 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)) {
|
||||
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -214,50 +218,6 @@ int apn_start(struct apn_ctx *apn)
|
||||
|
||||
/* Set TUN library callback */
|
||||
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
|
||||
|
||||
if (apn->v4.cfg.ifconfig_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
|
||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
|
||||
if (tun_setaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
|
||||
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
|
||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->v6.cfg.ifconfig_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
|
||||
if (tun_setaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
|
||||
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
|
||||
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->tun.cfg.ipup_script) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
|
||||
apn->tun.cfg.ipup_script);
|
||||
tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
|
||||
}
|
||||
|
||||
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
|
||||
if (tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK) < 1) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of "
|
||||
"interface: %s\n", strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
|
||||
}
|
||||
|
||||
/* set back-pointer from TUN device to APN */
|
||||
apn->tun.tun->priv = apn;
|
||||
break;
|
||||
case APN_GTPU_MODE_KERNEL_GTP:
|
||||
LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
|
||||
@@ -266,9 +226,17 @@ int apn_start(struct apn_ctx *apn)
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
if (gsn == NULL) {
|
||||
/* skip bringing up the APN now if the GSN is not initialized yet.
|
||||
* This happens during initial load of the config file, as the
|
||||
* "no shutdown" in the ggsn node only happens after the "apn" nodes
|
||||
* are brought up */
|
||||
LOGPAPN(LOGL_NOTICE, apn, "Skipping APN start\n");
|
||||
return 0;
|
||||
}
|
||||
/* use GTP kernel module for data packet encapsulation */
|
||||
if (gtp_kernel_init(apn->ggsn->gsn, apn->tun.cfg.dev_name,
|
||||
&apn->v4.cfg.ifconfig_prefix, apn->tun.cfg.ipup_script) < 0) {
|
||||
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
@@ -277,6 +245,68 @@ int apn_start(struct apn_ctx *apn)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* common initialization below */
|
||||
|
||||
/* set back-pointer from TUN device to APN */
|
||||
apn->tun.tun->priv = apn;
|
||||
|
||||
if (apn->v4.cfg.ifconfig_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
|
||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
|
||||
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
|
||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->v6.cfg.ifconfig_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
|
||||
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
|
||||
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->v6.cfg.ll_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 link-local address %s\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ll_prefix));
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr, NULL,
|
||||
apn->v6.cfg.ll_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
|
||||
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
|
||||
}
|
||||
|
||||
if (apn->tun.cfg.ipup_script) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
|
||||
apn->tun.cfg.ipup_script);
|
||||
tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
|
||||
}
|
||||
|
||||
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
|
||||
apn->v6.cfg.ll_prefix.addr.len == 0) {
|
||||
rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
|
||||
if (rc < 1) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
|
||||
rc ? strerror(errno) : "tun interface has no link-local IP assigned");
|
||||
apn_stop(apn, false);
|
||||
return -1;
|
||||
}
|
||||
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
|
||||
}
|
||||
|
||||
/* Create IPv4 pool */
|
||||
if (apn->v4.cfg.dynamic_prefix.addr.len) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
|
||||
@@ -335,21 +365,26 @@ 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 ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
|
||||
struct apn_ctx *apn = pdp->priv;
|
||||
struct ippoolm_t *member;
|
||||
int i;
|
||||
|
||||
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
|
||||
struct ippoolm_t *member = pdp->peer;
|
||||
|
||||
if (pdp->peer) {
|
||||
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
|
||||
ippool_freeip(ipp->pool, ipp);
|
||||
} else
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (pdp->peer[i]) {
|
||||
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)
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
||||
}
|
||||
|
||||
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
|
||||
strerror(errno));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -357,6 +392,44 @@ static int delete_context(struct pdp_t *pdp)
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* RFC 1332 */
|
||||
enum ipcp_options {
|
||||
IPCP_OPT_IPADDR = 3,
|
||||
IPCP_OPT_PRIMARY_DNS = 129,
|
||||
IPCP_OPT_SECONDARY_DNS = 131,
|
||||
};
|
||||
|
||||
struct ipcp_option_hdr {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipcp_hdr {
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* determine if IPCP contains given option */
|
||||
static uint8_t *ipcp_contains_option(uint8_t *ipcp, size_t ipcp_len, enum ipcp_options opt, size_t opt_minlen)
|
||||
{
|
||||
uint8_t *cur_opt = ipcp + sizeof(struct ipcp_hdr);
|
||||
|
||||
/* iterate over Options and check if protocol contained */
|
||||
while (cur_opt + 2 <= ipcp + ipcp_len) {
|
||||
uint8_t type = cur_opt[0];
|
||||
uint8_t len = cur_opt[1]; /* length value includes 2 bytes type/length */
|
||||
if (len < 2)
|
||||
return NULL;
|
||||
if (type == opt && len >= 2 + opt_minlen)
|
||||
return cur_opt;
|
||||
cur_opt += len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 10.6.5.3 */
|
||||
enum pco_protocols {
|
||||
PCO_P_LCP = 0xC021,
|
||||
@@ -390,85 +463,115 @@ enum pco_protocols {
|
||||
};
|
||||
|
||||
/* determine if PCO contains given protocol */
|
||||
static bool pco_contains_proto(struct ul255_t *pco, uint16_t prot)
|
||||
static uint8_t *pco_contains_proto(struct ul255_t *pco, size_t offset, uint16_t prot, size_t prot_minlen)
|
||||
{
|
||||
uint8_t *cur = pco->v + 1;
|
||||
uint8_t *cur = pco->v + 1 + offset;
|
||||
|
||||
/* iterate over PCO and check if protocol contained */
|
||||
while (cur + 3 <= pco->v + pco->l) {
|
||||
uint16_t cur_prot = osmo_load16be(cur);
|
||||
uint8_t cur_len = cur[2];
|
||||
if (cur_prot == prot)
|
||||
return true;
|
||||
if (cur_len == 0)
|
||||
break;
|
||||
if (cur_prot == prot && cur_len >= prot_minlen)
|
||||
return cur;
|
||||
cur += cur_len + 3;
|
||||
}
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* determine if PDP context has IPv6 support */
|
||||
static bool pdp_has_v4(struct pdp_t *pdp)
|
||||
{
|
||||
if (pdp->eua.l == 4+2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
/*! 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 from up to two given DNS addreses */
|
||||
static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *dns1,
|
||||
const struct in46_addr *dns2)
|
||||
/* construct an IPCP PCO response from request*/
|
||||
static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg)
|
||||
{
|
||||
uint8_t *len1, *len2;
|
||||
uint8_t *start = msg->tail;
|
||||
const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
|
||||
const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
|
||||
uint8_t *ipcp;
|
||||
uint16_t ipcp_len;
|
||||
uint8_t *len1, *len2, *pco_ipcp;
|
||||
unsigned int len_appended;
|
||||
ptrdiff_t consumed;
|
||||
size_t remain, offset = 0;
|
||||
|
||||
/* Three byte T16L header */
|
||||
msgb_put_u16(msg, 0x8021); /* IPCP */
|
||||
len1 = msgb_put(msg, 1); /* Length of contents: delay */
|
||||
/* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */
|
||||
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||
while (pco_ipcp) {
|
||||
uint8_t *start = msg->tail;
|
||||
|
||||
msgb_put_u8(msg, 0x02); /* ACK */
|
||||
msgb_put_u8(msg, id); /* ID: Needs to match request */
|
||||
msgb_put_u8(msg, 0x00); /* Length MSB */
|
||||
len2 = msgb_put(msg, 1); /* Length LSB: delay */
|
||||
ipcp = (pco_ipcp + 3); /* 2=type + 1=len */
|
||||
consumed = (ipcp - &pdp->pco_req.v[0]);
|
||||
remain = sizeof(pdp->pco_req.v) - consumed;
|
||||
ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */
|
||||
if (remain < 0 || remain < ipcp_len)
|
||||
return;
|
||||
|
||||
if (dns1 && dns1->len == 4) {
|
||||
msgb_put_u8(msg, 0x81); /* DNS1 Tag */
|
||||
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
|
||||
msgb_put_u32(msg, dns1->v4.s_addr);
|
||||
/* Three byte T16L header */
|
||||
msgb_put_u16(msg, 0x8021); /* IPCP */
|
||||
len1 = msgb_put(msg, 1); /* Length of contents: delay */
|
||||
|
||||
msgb_put_u8(msg, 0x02); /* ACK */
|
||||
msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */
|
||||
msgb_put_u8(msg, 0x00); /* Length MSB */
|
||||
len2 = msgb_put(msg, 1); /* Length LSB: delay */
|
||||
|
||||
if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
|
||||
msgb_put_u8(msg, 0x81); /* DNS1 Tag */
|
||||
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
|
||||
msgb_put_u32(msg, ntohl(dns1->v4.s_addr));
|
||||
}
|
||||
|
||||
if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
|
||||
msgb_put_u8(msg, 0x83); /* DNS2 Tag */
|
||||
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
|
||||
msgb_put_u32(msg, ntohl(dns2->v4.s_addr));
|
||||
}
|
||||
|
||||
/* patch in length values */
|
||||
len_appended = msg->tail - start;
|
||||
*len1 = len_appended - 3;
|
||||
*len2 = len_appended - 3;
|
||||
|
||||
offset += 3 + ipcp_len;
|
||||
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||
}
|
||||
|
||||
if (dns2 && dns2->len == 4) {
|
||||
msgb_put_u8(msg, 0x83); /* DNS2 Tag */
|
||||
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
|
||||
msgb_put_u32(msg, 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 */
|
||||
|
||||
/* FIXME: also check if primary / secondary DNS was requested */
|
||||
if (pdp_has_v4(pdp) && pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)) {
|
||||
/* FIXME: properly implement this for IPCP */
|
||||
build_ipcp_pco(msg, 0, &apn->v4.cfg.dns[0], &apn->v4.cfg.dns[1]);
|
||||
}
|
||||
if (peer_v4)
|
||||
build_ipcp_pco(apn, pdp, msg);
|
||||
|
||||
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR)) {
|
||||
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv6_ADDR, 0)) {
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
||||
struct in46_addr *i46a = &apn->v6.cfg.dns[i];
|
||||
if (i46a->len != 16)
|
||||
@@ -477,7 +580,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
}
|
||||
}
|
||||
|
||||
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv4_ADDR)) {
|
||||
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv4_ADDR, 0)) {
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
|
||||
struct in46_addr *i46a = &apn->v4.cfg.dns[i];
|
||||
if (i46a->len != 4)
|
||||
@@ -514,10 +617,11 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
static char name_buf[256];
|
||||
struct gsn_t *gsn = pdp->gsn;
|
||||
struct ggsn_ctx *ggsn = gsn->priv;
|
||||
struct in46_addr addr;
|
||||
struct ippoolm_t *member;
|
||||
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;
|
||||
int rc;
|
||||
int rc, num_addr, i;
|
||||
|
||||
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||
|
||||
@@ -552,56 +656,69 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
|
||||
pdp->qos_neg.l = pdp->qos_req.l;
|
||||
|
||||
if (in46a_from_eua(&pdp->eua, &addr)) {
|
||||
memset(addr, 0, sizeof(addr));
|
||||
if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
|
||||
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (addr.len == sizeof(struct in_addr)) {
|
||||
/* does this APN actually have an IPv4 pool? */
|
||||
if (!apn_supports_ipv4(apn))
|
||||
goto err_wrong_af;
|
||||
/* Allocate dynamic addresses from the pool */
|
||||
for (i = 0; i < num_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;
|
||||
|
||||
rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
|
||||
if (rc < 0)
|
||||
goto err_pool_full;
|
||||
in46a_to_eua(&member->addr, &pdp->eua);
|
||||
rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
|
||||
if (rc < 0)
|
||||
goto err_pool_full;
|
||||
/* copy back */
|
||||
memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
|
||||
|
||||
addrv4 = member;
|
||||
|
||||
} else if (addr[i].len == sizeof(struct in6_addr)) {
|
||||
|
||||
/* does this APN actually have an IPv6 pool? */
|
||||
if (!apn_supports_ipv6(apn))
|
||||
goto err_wrong_af;
|
||||
|
||||
rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
|
||||
if (rc < 0)
|
||||
goto err_pool_full;
|
||||
|
||||
/* IPv6 doesn't really send the real/allocated address at this point, but just
|
||||
* the link-identifier which the MS shall use for router solicitation */
|
||||
/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
|
||||
memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
|
||||
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
|
||||
memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
|
||||
|
||||
addrv6 = member;
|
||||
} else
|
||||
OSMO_ASSERT(0);
|
||||
|
||||
pdp->peer[i] = member;
|
||||
member->peer = pdp;
|
||||
}
|
||||
|
||||
in46a_to_eua(addr, num_addr, &pdp->eua);
|
||||
|
||||
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP && apn_supports_ipv4(apn)) {
|
||||
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
|
||||
if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr.len == sizeof(struct in6_addr)) {
|
||||
struct in46_addr tmp;
|
||||
}
|
||||
|
||||
/* does this APN actually have an IPv6 pool? */
|
||||
if (!apn_supports_ipv6(apn))
|
||||
goto err_wrong_af;
|
||||
|
||||
rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
|
||||
if (rc < 0)
|
||||
goto err_pool_full;
|
||||
|
||||
/* IPv6 doesn't really send the real/allocated address at this point, but just
|
||||
* the link-identifier which the MS shall use for router solicitation */
|
||||
tmp.len = addr.len;
|
||||
/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
|
||||
memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
|
||||
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
|
||||
memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
|
||||
in46a_to_eua(&tmp, &pdp->eua);
|
||||
} else
|
||||
OSMO_ASSERT(0);
|
||||
|
||||
pdp->peer = member;
|
||||
pdp->ipif = apn->tun.tun; /* TODO */
|
||||
pdp->priv = apn;
|
||||
member->peer = pdp;
|
||||
|
||||
/* TODO: change trap to send 2 IPs */
|
||||
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
|
||||
return 0;
|
||||
@@ -612,8 +729,10 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
/* Transmit G-PDU sequence numbers (only) if configured in APN */
|
||||
pdp->tx_gpdu_seq = apn->cfg.tx_gpdu_seq;
|
||||
|
||||
LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IP=%s\n",
|
||||
name_buf, apn->cfg.name, pdp->teic_own, in46a_ntoa(&member->addr));
|
||||
LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IPv4=%s, IPv6=%s\n",
|
||||
name_buf, apn->cfg.name, pdp->teic_own,
|
||||
addrv4 ? inet_ntop(AF_INET, &addrv4->addr.v4, straddrv4, sizeof(straddrv4)) : "none",
|
||||
addrv6 ? inet_ntop(AF_INET6, &addrv6->addr.v6, straddrv6, sizeof(straddrv6)) : "none");
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
|
||||
return 0; /* Success */
|
||||
|
||||
@@ -637,23 +756,31 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
struct iphdr *iph = (struct iphdr *)pack;
|
||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
struct ippool_t *pool;
|
||||
char straddr[INET6_ADDRSTRLEN];
|
||||
uint8_t pref_offset;
|
||||
|
||||
if (iph->version == 4) {
|
||||
switch (iph->version) {
|
||||
case 4:
|
||||
if (len < sizeof(*iph) || len < 4*iph->ihl)
|
||||
return -1;
|
||||
dst.len = 4;
|
||||
dst.v4.s_addr = iph->daddr;
|
||||
pool = apn->v4.pool;
|
||||
} else if (iph->version == 6) {
|
||||
break;
|
||||
case 6:
|
||||
/* Due to the fact that 3GPP requires an allocation of a
|
||||
* /64 prefix to each MS, we must instruct
|
||||
* ippool_getip() below to match only the leading /64
|
||||
* prefix, i.e. the first 8 bytes of the address */
|
||||
* prefix, i.e. the first 8 bytes of the address. If the ll addr
|
||||
* is used, then the match should be done on the trailing 64
|
||||
* bits. */
|
||||
dst.len = 8;
|
||||
dst.v6 = ip6h->ip6_dst;
|
||||
pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
|
||||
memcpy(&dst.v6, ((uint8_t*)&ip6h->ip6_dst) + pref_offset, 8);
|
||||
pool = apn->v6.pool;
|
||||
} else {
|
||||
LOGP(DTUN, LOGL_NOTICE, "non-IPv packet received from tun\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -661,12 +788,15 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
if (!pool)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DTUN, "Received packet from tun!\n");
|
||||
DEBUGP(DTUN, "Received packet for APN(%s) from tun %s", apn->cfg.name, tun->devname);
|
||||
|
||||
if (ippool_getip(pool, &ipm, &dst)) {
|
||||
DEBUGP(DTUN, "Received packet with no PDP contex!!\n");
|
||||
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;
|
||||
}
|
||||
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);
|
||||
@@ -685,19 +815,53 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
struct tun_t *tun = (struct tun_t *)pdp->ipif;
|
||||
struct apn_ctx *apn = tun->priv;
|
||||
char straddr[INET6_ADDRSTRLEN];
|
||||
struct ippoolm_t *peer;
|
||||
uint8_t pref_offset;
|
||||
|
||||
OSMO_ASSERT(tun);
|
||||
OSMO_ASSERT(apn);
|
||||
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n");
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Packet received on APN(%s): forwarding to tun %s\n", apn->cfg.name, tun->devname);
|
||||
|
||||
switch (iph->version) {
|
||||
case 6:
|
||||
peer = pdp_get_peer_ipv(pdp, true);
|
||||
if (!peer) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv6 with unassigned EUA: %s\n",
|
||||
osmo_hexdump(pack, len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Validate packet comes from IPaddr assigned to the pdp ctx.
|
||||
If packet is a LL addr, then EUA is in the lower 64 bits,
|
||||
otherwise it's used as the 64 prefix */
|
||||
pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ? 8 : 0;
|
||||
if (memcmp(((uint8_t*)&ip6h->ip6_src) + pref_offset, &peer->addr.v6, 8)) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv6: %s\n",
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* daddr: all-routers multicast addr */
|
||||
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
|
||||
return handle_router_mcast(pdp->gsn, pdp, &apn->v6_lladdr, pack, len);
|
||||
return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
|
||||
&apn->v6_lladdr, pack, len);
|
||||
break;
|
||||
case 4:
|
||||
peer = pdp_get_peer_ipv(pdp, false);
|
||||
if (!peer) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv4 with unassigned EUA: %s\n",
|
||||
osmo_hexdump(pack, len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Validate packet comes from IPaddr assigned to the pdp ctx */
|
||||
if (memcmp(&iph->saddr, &peer->addr.v4, sizeof(peer->addr.v4))) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv4: %s\n",
|
||||
inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
|
||||
@@ -929,6 +1093,7 @@ int main(int argc, char **argv)
|
||||
|
||||
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);
|
||||
@@ -938,11 +1103,12 @@ int main(int argc, char **argv)
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging(&log_info);
|
||||
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);
|
||||
@@ -961,7 +1127,8 @@ int main(int argc, char **argv)
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
|
||||
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);
|
||||
|
||||
@@ -22,6 +22,7 @@ struct ggsn_ctx;
|
||||
struct apn_ctx_ip {
|
||||
struct {
|
||||
struct in46_prefix ifconfig_prefix;
|
||||
struct in46_prefix ll_prefix;
|
||||
struct in46_prefix static_prefix;
|
||||
struct in46_prefix dynamic_prefix;
|
||||
/* v4 DNS server names */
|
||||
|
||||
@@ -513,6 +513,24 @@ DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
|
||||
"ipv6 link-local X:X::X:X/M",
|
||||
IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
|
||||
"no ipv6 link-local",
|
||||
NO_STR IP6_STR IFCONFIG_STR)
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
|
||||
|
||||
DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
|
||||
@@ -668,6 +686,8 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
|
||||
}
|
||||
if (apn->v6.cfg.ifconfig_prefix.addr.len)
|
||||
vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
|
||||
if (apn->v6.cfg.ll_prefix.addr.len)
|
||||
vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
|
||||
|
||||
/* must be last */
|
||||
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
|
||||
@@ -893,6 +913,8 @@ int ggsn_vty_init(void)
|
||||
install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
|
||||
|
||||
@@ -926,6 +948,10 @@ 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;
|
||||
|
||||
@@ -180,17 +180,15 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
||||
}
|
||||
|
||||
/* 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 *own_ll_addr,
|
||||
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 ippoolm_t *member = pdp->peer;
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
||||
struct msgb *msg;
|
||||
|
||||
OSMO_ASSERT(pdp);
|
||||
OSMO_ASSERT(member);
|
||||
|
||||
if (len < sizeof(*ip6h)) {
|
||||
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
|
||||
return -1;
|
||||
@@ -221,7 +219,7 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_a
|
||||
/* Send router advertisement from GGSN link-local
|
||||
* address to MS link-local address, including prefix
|
||||
* allocated to this PDP context */
|
||||
msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, &member->addr.v6);
|
||||
msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, pdp_prefix);
|
||||
/* Send the constructed RA to the MS */
|
||||
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
|
||||
msgb_free(msg);
|
||||
|
||||
@@ -3,5 +3,7 @@
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
|
||||
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);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# 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
|
||||
LIBVERSION=2:0:0
|
||||
# If major=current-age is increased, remember to update the dh_strip line in debian/rules!
|
||||
LIBVERSION=4:0:0
|
||||
|
||||
lib_LTLIBRARIES = libgtp.la
|
||||
|
||||
include_HEADERS = gtp.h pdp.h gtpie.h
|
||||
@@ -11,7 +13,3 @@ 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
399
gtp/gtp.c
399
gtp/gtp.c
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2010-2011, 2016-2017 Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2015-2017 sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
*
|
||||
* The contents of this file may be used under the terms of the GNU
|
||||
* General Public License Version 2, provided that the above copyright
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* gtp.c: Contains all GTP functionality. Should be able to handle multiple
|
||||
* tunnels in the same program.
|
||||
* tunnels in the same program.
|
||||
*
|
||||
* TODO:
|
||||
* - Do we need to handle fragmentation?
|
||||
@@ -86,6 +86,51 @@ const char *gtp_version()
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
const struct value_string gtp_type_names[] = {
|
||||
{ GTP_ECHO_REQ, "Echo Request" },
|
||||
{ GTP_ECHO_RSP, "Echo Response" },
|
||||
{ GTP_NOT_SUPPORTED, "Version Not Supported" },
|
||||
{ GTP_ALIVE_REQ, "Node Alive Request" },
|
||||
{ GTP_ALIVE_RSP, "Node Alive Response" },
|
||||
{ GTP_REDIR_REQ, "Redirection Request" },
|
||||
{ GTP_REDIR_RSP, "Redirection Response" },
|
||||
{ GTP_CREATE_PDP_REQ, "Create PDP Context Request" },
|
||||
{ GTP_CREATE_PDP_RSP, "Create PDP Context Response" },
|
||||
{ GTP_UPDATE_PDP_REQ, "Update PDP Context Request" },
|
||||
{ GTP_UPDATE_PDP_RSP, "Update PDP Context Response" },
|
||||
{ GTP_DELETE_PDP_REQ, "Delete PDP Context Request" },
|
||||
{ GTP_DELETE_PDP_RSP, "Delete PDP Context Response" },
|
||||
{ GTP_ERROR, "Error Indication" },
|
||||
{ GTP_PDU_NOT_REQ, "PDU Notification Request" },
|
||||
{ GTP_PDU_NOT_RSP, "PDU Notification Response" },
|
||||
{ GTP_PDU_NOT_REJ_REQ, "PDU Notification Reject Request" },
|
||||
{ GTP_PDU_NOT_REJ_RSP, "PDU Notification Reject Response" },
|
||||
{ GTP_SUPP_EXT_HEADER, "Supported Extension Headers Notification" },
|
||||
{ GTP_SND_ROUTE_REQ, "Send Routeing Information for GPRS Request" },
|
||||
{ GTP_SND_ROUTE_RSP, "Send Routeing Information for GPRS Response" },
|
||||
{ GTP_FAILURE_REQ, "Failure Report Request" },
|
||||
{ GTP_FAILURE_RSP, "Failure Report Response" },
|
||||
{ GTP_MS_PRESENT_REQ, "Note MS GPRS Present Request" },
|
||||
{ GTP_MS_PRESENT_RSP, "Note MS GPRS Present Response" },
|
||||
{ GTP_IDEN_REQ, "Identification Request" },
|
||||
{ GTP_IDEN_RSP, "Identification Response" },
|
||||
{ GTP_SGSN_CONTEXT_REQ,"SGSN Context Request" },
|
||||
{ GTP_SGSN_CONTEXT_RSP,"SGSN Context Response" },
|
||||
{ GTP_SGSN_CONTEXT_ACK,"SGSN Context Acknowledge" },
|
||||
{ GTP_FWD_RELOC_REQ, "Forward Relocation Request" },
|
||||
{ GTP_FWD_RELOC_RSP, "Forward Relocation Response" },
|
||||
{ GTP_FWD_RELOC_COMPL, "Forward Relocation Complete" },
|
||||
{ GTP_RELOC_CANCEL_REQ,"Relocation Cancel Request" },
|
||||
{ GTP_RELOC_CANCEL_RSP,"Relocation Cancel Response" },
|
||||
{ GTP_FWD_SRNS, "Forward SRNS Context" },
|
||||
{ GTP_FWD_RELOC_ACK, "Forward Relocation Complete Acknowledge" },
|
||||
{ GTP_FWD_SRNS_ACK, "Forward SRNS Context Acknowledge" },
|
||||
{ GTP_DATA_TRAN_REQ, "Data Record Transfer Request" },
|
||||
{ GTP_DATA_TRAN_RSP, "Data Record Transfer Response" },
|
||||
{ GTP_GPDU, "G-PDU" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* gtp_new */
|
||||
/* gtp_free */
|
||||
|
||||
@@ -145,6 +190,15 @@ int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp, uint8_t recovery)
|
||||
{
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
if (gsn->cb_recovery2)
|
||||
gsn->cb_recovery2(peer, pdp, recovery);
|
||||
}
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
|
||||
{
|
||||
@@ -152,7 +206,21 @@ int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
/* cb_recovery()
|
||||
* pdp may be NULL if Recovery IE was received from a message independent
|
||||
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
|
||||
* local setup. In case pdp is known, caller may want to keep that pdp alive to
|
||||
* handle subsequent msg cb as this specific pdp ctx is still valid according to
|
||||
* specs.
|
||||
*/
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery2 = cb_recovery2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len))
|
||||
{
|
||||
@@ -168,7 +236,7 @@ extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
* to hold the packet header.
|
||||
* returns the length of the header. 0 on error.
|
||||
**/
|
||||
static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
|
||||
static unsigned int get_default_gtp(uint8_t version, uint8_t type, void *packet)
|
||||
{
|
||||
struct gtp0_header *gtp0_default = (struct gtp0_header *)packet;
|
||||
struct gtp1_header_long *gtp1_default =
|
||||
@@ -281,19 +349,19 @@ static uint32_t get_tei(void *pack)
|
||||
|
||||
/* ***********************************************************
|
||||
* Reliable delivery of signalling messages
|
||||
*
|
||||
*
|
||||
* Sequence numbers are used for both signalling messages and
|
||||
* data messages.
|
||||
*
|
||||
* For data messages each tunnel maintains a sequence counter,
|
||||
* which is incremented by one each time a new data message
|
||||
* is sent. The sequence number starts at (0) zero at tunnel
|
||||
* establishment, and wraps around at 65535 (29.060 9.3.1.1
|
||||
* establishment, and wraps around at 65535 (29.060 9.3.1.1
|
||||
* and 09.60 8.1.1.1). The sequence numbers are either ignored,
|
||||
* or can be used to check the validity of the message in the
|
||||
* receiver, or for reordering af packets.
|
||||
*
|
||||
* For signalling messages the sequence number is used by
|
||||
* For signalling messages the sequence number is used by
|
||||
* signalling messages for which a response is defined. A response
|
||||
* message should copy the sequence from the corresponding request
|
||||
* message. The sequence number "unambiguously" identifies a request
|
||||
@@ -311,7 +379,7 @@ static uint32_t get_tei(void *pack)
|
||||
* with path setup and teardown.
|
||||
*
|
||||
* If a response message is lost, the request will be retransmitted, and
|
||||
* the receiving GSN will receive a "duplicated" request. The standard
|
||||
* the receiving GSN will receive a "duplicated" request. The standard
|
||||
* requires the receiving GSN to send a response, with the same information
|
||||
* as in the original response. For most messages this happens automatically:
|
||||
*
|
||||
@@ -326,22 +394,22 @@ static uint32_t get_tei(void *pack)
|
||||
* a nonexist reply message.
|
||||
*
|
||||
* The correct solution will be to make a queue containing response messages.
|
||||
* This queue should be checked whenever a request is received. If the
|
||||
* This queue should be checked whenever a request is received. If the
|
||||
* response is allready in the queue that response should be transmitted.
|
||||
* It should be possible to find messages in this queue on the basis of
|
||||
* the sequence number and peer GSN IP address (The sequense number is unique
|
||||
* within each path). This need to be implemented by a hash table. Furthermore
|
||||
* it should be possibly to delete messages based on a timeout. This can be
|
||||
* achieved by means of a linked list. The timeout value need to be larger
|
||||
* than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
|
||||
* than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
|
||||
* set in the peer GSN, so there is no way to know these parameters. On the
|
||||
* other hand the timeout value need to be so small that we do not receive
|
||||
* wraparound sequence numbere before the message is deleted. 60 seconds is
|
||||
* probably not a bad choise.
|
||||
*
|
||||
*
|
||||
* This queue however is first really needed from gtp1.
|
||||
*
|
||||
* gtp_req:
|
||||
* gtp_req:
|
||||
* Send off a signalling message with appropiate sequence
|
||||
* number. Store packet in queue.
|
||||
* gtp_conf:
|
||||
@@ -357,7 +425,7 @@ static uint32_t get_tei(void *pack)
|
||||
* a predefined timeout.
|
||||
*************************************************************/
|
||||
|
||||
int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
||||
static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
|
||||
union gtp_packet *packet, int len,
|
||||
struct in_addr *inetaddr, void *cbp)
|
||||
{
|
||||
@@ -428,11 +496,29 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief clear the request and response queue. Useful for debugging to reset "some" state.
|
||||
* @param gsn The GGSN instance
|
||||
*/
|
||||
void gtp_clear_queues(struct gsn_t *gsn)
|
||||
{
|
||||
struct qmsg_t *qmsg;
|
||||
|
||||
while (!queue_getfirst(gsn->queue_req, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_req, qmsg);
|
||||
}
|
||||
|
||||
while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_resp, qmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/* gtp_conf
|
||||
* Remove signalling packet from retransmission queue.
|
||||
* return 0 on success, EOF if packet was not found */
|
||||
|
||||
int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
||||
static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
|
||||
union gtp_packet *packet, int len, uint8_t * type, void **cbp)
|
||||
{
|
||||
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||
@@ -523,7 +609,7 @@ int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
union gtp_packet *packet, int len,
|
||||
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
|
||||
{
|
||||
@@ -581,7 +667,7 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_notification(struct gsn_t *gsn, int version,
|
||||
static int gtp_notification(struct gsn_t *gsn, uint8_t version,
|
||||
union gtp_packet *packet, int len,
|
||||
struct sockaddr_in *peer, int fd, uint16_t seq)
|
||||
{
|
||||
@@ -626,7 +712,7 @@ int gtp_notification(struct gsn_t *gsn, int version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_dublicate(struct gsn_t *gsn, int version,
|
||||
static int gtp_dublicate(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, uint16_t seq)
|
||||
{
|
||||
struct qmsg_t *qmsg;
|
||||
@@ -852,7 +938,7 @@ int gtp_free(struct gsn_t *gsn)
|
||||
* For response messages we need to be able to respond to
|
||||
* the relevant src port even if it is locally allocated by
|
||||
* the peer.
|
||||
*
|
||||
*
|
||||
* The need for path management!
|
||||
* We might need to keep a list of active paths. This might
|
||||
* be in the form of remote IP address + UDP port numbers.
|
||||
@@ -932,8 +1018,7 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, recovery, NULL, cbp);
|
||||
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
emit_cb_recovery(gsn, peer, NULL, recovery);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -942,9 +1027,9 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
||||
/* This message is somewhat special in that it actually is a
|
||||
* response to some other message with unsupported GTP version
|
||||
* For this reason it has parameters like a response, and does
|
||||
* its own message transmission. No signalling queue is used
|
||||
* its own message transmission. No signalling queue is used
|
||||
* The reply is sent to the peer IP and peer UDP. This means that
|
||||
* the peer will be receiving a GTP0 message on a GTP1 port!
|
||||
* the peer will be receiving a GTP0 message on a GTP1 port!
|
||||
* In practice however this will never happen as a GTP0 GSN will
|
||||
* only listen to the GTP0 port, and therefore will never receive
|
||||
* anything else than GTP0 */
|
||||
@@ -971,7 +1056,7 @@ int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
}
|
||||
|
||||
/* Send off an Supported Extension Headers Notification */
|
||||
int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
||||
static int gtp_extheader_req(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
|
||||
int fd, void *pack, unsigned len)
|
||||
{
|
||||
union gtp_packet packet;
|
||||
@@ -992,7 +1077,7 @@ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
||||
}
|
||||
|
||||
/* Handle a Supported Extension Headers Notification */
|
||||
int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
static int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
void *pack, unsigned len)
|
||||
{
|
||||
|
||||
@@ -1007,7 +1092,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
* Messages: create, update and delete PDP context
|
||||
*
|
||||
* Information storage
|
||||
* Information storage for each PDP context is defined in
|
||||
* Information storage for each PDP context is defined in
|
||||
* 23.060 section 13.3. Includes IMSI, MSISDN, APN, PDP-type,
|
||||
* PDP-address (IP address), sequence numbers, charging ID.
|
||||
* For the SGSN it also includes radio related mobility
|
||||
@@ -1015,7 +1100,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
*************************************************************/
|
||||
|
||||
/* API: Send Create PDP Context Request (7.3.1) */
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp)
|
||||
{
|
||||
union gtp_packet packet;
|
||||
@@ -1093,7 +1178,7 @@ extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
pdp->cch_pdp);
|
||||
}
|
||||
|
||||
/* TODO
|
||||
/* TODO
|
||||
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
|
||||
pdp->traceref);
|
||||
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
|
||||
@@ -1265,6 +1350,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
struct pdp_t pdp_buf;
|
||||
union gtpie_member *ie[GTPIE_SIZE];
|
||||
uint8_t recovery;
|
||||
bool recovery_recvd = false;
|
||||
int rc;
|
||||
|
||||
uint16_t seq = get_seq(pack);
|
||||
int hlen = get_hlen(pack);
|
||||
@@ -1365,8 +1452,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
|
||||
/* Recovery (optional) */
|
||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
/* we use recovery futher down after announcing new pdp ctx to user */
|
||||
recovery_recvd = true;
|
||||
}
|
||||
|
||||
/* Selection mode (conditional) */
|
||||
@@ -1537,7 +1624,7 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
(!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l)))
|
||||
{
|
||||
/* OK! We are dealing with the same APN. We will copy new
|
||||
* parameters to the old pdp and send off confirmation
|
||||
* parameters to the old pdp and send off confirmation
|
||||
* We ignore the following information elements:
|
||||
* QoS: MS will get originally negotiated QoS.
|
||||
* End user address (EUA). MS will get old EUA anyway.
|
||||
@@ -1567,6 +1654,9 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
/* Switch to using the old pdp context */
|
||||
pdp = pdp_old;
|
||||
|
||||
if (recovery_recvd)
|
||||
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||
|
||||
/* Confirm to peer that things were "successful" */
|
||||
return gtp_create_pdp_resp(gsn, version, pdp,
|
||||
GTPCAUSE_ACC_REQ);
|
||||
@@ -1588,13 +1678,16 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
|
||||
/* Callback function to validata login */
|
||||
if (gsn->cb_create_context_ind != 0)
|
||||
return gsn->cb_create_context_ind(pdp);
|
||||
rc = gsn->cb_create_context_ind(pdp);
|
||||
else {
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"No create_context_ind callback defined\n");
|
||||
return gtp_create_pdp_resp(gsn, version, pdp,
|
||||
rc = gtp_create_pdp_resp(gsn, version, pdp,
|
||||
GTPCAUSE_NOT_SUPPORTED);
|
||||
}
|
||||
if (recovery_recvd)
|
||||
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Handle Create PDP Context Response */
|
||||
@@ -1651,8 +1744,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
|
||||
|
||||
/* Extract recovery (optional) */
|
||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||
}
|
||||
|
||||
/* Extract protocol configuration options (optional) */
|
||||
@@ -1853,14 +1945,14 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||
|
||||
gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi);
|
||||
|
||||
/* TODO
|
||||
/* TODO
|
||||
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
|
||||
pdp->traceref);
|
||||
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
|
||||
pdp->tracetype); */
|
||||
|
||||
/* TODO if ggsn update message
|
||||
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
|
||||
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
|
||||
pdp->eua.l, pdp->eua.v);
|
||||
*/
|
||||
|
||||
@@ -1891,7 +1983,7 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||
}
|
||||
|
||||
/* Send Update PDP Context Response */
|
||||
int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
|
||||
static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len,
|
||||
struct pdp_t *pdp, uint8_t cause)
|
||||
@@ -1932,8 +2024,8 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
|
||||
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID,
|
||||
pdp->teid_own);
|
||||
|
||||
/* If ggsn
|
||||
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
|
||||
/* If ggsn
|
||||
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
|
||||
pdp->eua.l, pdp->eua.v); */
|
||||
|
||||
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
|
||||
@@ -1953,7 +2045,7 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
|
||||
}
|
||||
|
||||
/* Handle Update PDP Context Request */
|
||||
int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
||||
static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len)
|
||||
{
|
||||
@@ -2061,8 +2153,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
||||
|
||||
/* Recovery (optional) */
|
||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||
}
|
||||
|
||||
if (version == 0) {
|
||||
@@ -2127,7 +2218,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"Missing mandatory information field");
|
||||
memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
|
||||
return gtp_update_pdp_resp(gsn, version, pdp,
|
||||
return gtp_update_pdp_resp(gsn, version, pdp,
|
||||
GTPCAUSE_MAN_IE_MISSING);
|
||||
} */
|
||||
|
||||
@@ -2181,7 +2272,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
||||
}
|
||||
|
||||
/* Handle Update PDP Context Response */
|
||||
int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
|
||||
static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, void *pack, unsigned len)
|
||||
{
|
||||
struct pdp_t *pdp;
|
||||
@@ -2222,8 +2313,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
|
||||
|
||||
/* Extract recovery (optional) */
|
||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||
if (gsn->cb_recovery)
|
||||
gsn->cb_recovery(peer, recovery);
|
||||
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||
}
|
||||
|
||||
/* Check all conditional information elements */
|
||||
@@ -2292,16 +2382,66 @@ err_out:
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* API: Send Delete PDP Context Request */
|
||||
/* API: Deprecated. Send Delete PDP Context Request And free pdp ctx. */
|
||||
int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||
int teardown)
|
||||
{
|
||||
struct pdp_t *linked_pdp;
|
||||
struct pdp_t *secondary_pdp;
|
||||
int n;
|
||||
|
||||
if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown linked PDP context: %u\n", pdp->teic_own);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (gtp_delete_context_req2(gsn, pdp, cbp, teardown) == EOF)
|
||||
return EOF;
|
||||
|
||||
if (teardown) { /* Remove all contexts */
|
||||
for (n = 0; n < PDP_MAXNSAPI; n++) {
|
||||
if (linked_pdp->secondary_tei[n]) {
|
||||
if (pdp_getgtp1
|
||||
(&secondary_pdp,
|
||||
linked_pdp->secondary_tei[n])) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown secondary PDP context\n");
|
||||
return EOF;
|
||||
}
|
||||
if (linked_pdp != secondary_pdp) {
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context
|
||||
(secondary_pdp);
|
||||
pdp_freepdp(secondary_pdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(linked_pdp);
|
||||
pdp_freepdp(linked_pdp);
|
||||
} else {
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(pdp);
|
||||
if (pdp == linked_pdp) {
|
||||
linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0;
|
||||
linked_pdp->nodata = 1;
|
||||
} else
|
||||
pdp_freepdp(pdp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Send Delete PDP Context Request. PDP CTX shall be free'd by user at cb_conf(GTP_DELETE_PDP_RSP) */
|
||||
int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||
int teardown)
|
||||
{
|
||||
union gtp_packet packet;
|
||||
unsigned int length =
|
||||
get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
|
||||
struct in_addr addr;
|
||||
struct pdp_t *linked_pdp;
|
||||
struct pdp_t *secondary_pdp;
|
||||
int n;
|
||||
int count = 0;
|
||||
|
||||
@@ -2338,37 +2478,6 @@ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||
|
||||
gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp);
|
||||
|
||||
if (teardown) { /* Remove all contexts */
|
||||
for (n = 0; n < PDP_MAXNSAPI; n++) {
|
||||
if (linked_pdp->secondary_tei[n]) {
|
||||
if (pdp_getgtp1
|
||||
(&secondary_pdp,
|
||||
linked_pdp->secondary_tei[n])) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown secondary PDP context\n");
|
||||
return EOF;
|
||||
}
|
||||
if (linked_pdp != secondary_pdp) {
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context
|
||||
(secondary_pdp);
|
||||
pdp_freepdp(secondary_pdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(linked_pdp);
|
||||
pdp_freepdp(linked_pdp);
|
||||
} else {
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(pdp);
|
||||
if (pdp == linked_pdp) {
|
||||
linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0;
|
||||
linked_pdp->nodata = 1;
|
||||
} else
|
||||
pdp_freepdp(pdp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2397,7 +2506,7 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
|
||||
if (pdp_getgtp1
|
||||
(&secondary_pdp,
|
||||
linked_pdp->secondary_tei[n])) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown secondary PDP context\n");
|
||||
return EOF;
|
||||
}
|
||||
@@ -2508,6 +2617,9 @@ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
|
||||
if (linked_pdp->secondary_tei[n])
|
||||
count++;
|
||||
if (count <= 1) {
|
||||
GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
|
||||
"Ignoring CTX DEL without teardown and count=%d\n",
|
||||
count);
|
||||
return 0; /* 29.060 7.3.5 Ignore message */
|
||||
}
|
||||
}
|
||||
@@ -2525,19 +2637,32 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
||||
uint8_t cause;
|
||||
void *cbp = NULL;
|
||||
uint8_t type = 0;
|
||||
struct pdp_t *pdp = NULL;
|
||||
int hlen = get_hlen(pack);
|
||||
|
||||
/* Remove packet from queue */
|
||||
if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp))
|
||||
return EOF;
|
||||
|
||||
/* Find the context in question. It may not be available if gtp_delete_context_req
|
||||
* was used and as a result the PDP ctx was already freed */
|
||||
if (pdp_getgtp1(&pdp, get_tei(pack))) {
|
||||
gsn->err_unknownpdp++;
|
||||
GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
|
||||
"Unknown PDP context: %u (expected if gtp_delete_context_req is used)\n",
|
||||
get_tei(pack));
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, EOF, NULL, cbp);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* Decode information elements */
|
||||
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
|
||||
gsn->invalid++;
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"Invalid message format\n");
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, EOF, NULL, cbp);
|
||||
gsn->cb_conf(type, EOF, pdp, cbp);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
@@ -2547,7 +2672,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"Missing mandatory information field\n");
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, EOF, NULL, cbp);
|
||||
gsn->cb_conf(type, EOF, pdp, cbp);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
@@ -2557,19 +2682,19 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"Unexpected cause value received: %d\n", cause);
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, cause, NULL, cbp);
|
||||
gsn->cb_conf(type, cause, pdp, cbp);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* Callback function to notify application */
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(type, cause, NULL, cbp);
|
||||
gsn->cb_conf(type, cause, pdp, cbp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send Error Indication (response to a GPDU message) - 3GPP TS 29.060 7.3.7 */
|
||||
int gtp_error_ind_resp(struct gsn_t *gsn, int version,
|
||||
static int gtp_error_ind_resp(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len)
|
||||
{
|
||||
@@ -2591,7 +2716,7 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version,
|
||||
}
|
||||
|
||||
/* Handle Error Indication */
|
||||
int gtp_error_ind_conf(struct gsn_t *gsn, int version,
|
||||
static int gtp_error_ind_conf(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, void *pack, unsigned len)
|
||||
{
|
||||
union gtpie_member *ie[GTPIE_SIZE];
|
||||
@@ -2647,16 +2772,16 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
||||
static int gtp_gpdu_ind(struct gsn_t *gsn, uint8_t version,
|
||||
struct sockaddr_in *peer, int fd, void *pack, unsigned len)
|
||||
{
|
||||
|
||||
int hlen = GTP1_HEADER_SIZE_SHORT;
|
||||
int hlen;
|
||||
|
||||
/* Need to include code to verify packet src and dest addresses */
|
||||
struct pdp_t *pdp;
|
||||
|
||||
if (version == 0) {
|
||||
switch (version) {
|
||||
case 0:
|
||||
if (pdp_getgtp0
|
||||
(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) {
|
||||
gsn->err_unknownpdp++;
|
||||
@@ -2666,7 +2791,8 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
||||
len);
|
||||
}
|
||||
hlen = GTP0_HEADER_SIZE;
|
||||
} else if (version == 1) {
|
||||
break;
|
||||
case 1:
|
||||
if (pdp_getgtp1
|
||||
(&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) {
|
||||
gsn->err_unknownpdp++;
|
||||
@@ -2681,9 +2807,11 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
||||
hlen = GTP1_HEADER_SIZE_LONG;
|
||||
else
|
||||
hlen = GTP1_HEADER_SIZE_SHORT;
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||
"Unknown version: %d\n", version);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* If the GPDU was not from the peer GSN tell him to delete context */
|
||||
@@ -2700,10 +2828,10 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receives GTP packet and sends off for further processing
|
||||
/* Receives GTP packet and sends off for further processing
|
||||
* Function will check the validity of the header. If the header
|
||||
* is not valid the packet is either dropped or a version not
|
||||
* supported is returned to the peer.
|
||||
* is not valid the packet is either dropped or a version not
|
||||
* supported is returned to the peer.
|
||||
* TODO: Need to decide on return values! */
|
||||
int gtp_decaps0(struct gsn_t *gsn)
|
||||
{
|
||||
@@ -2712,7 +2840,7 @@ int gtp_decaps0(struct gsn_t *gsn)
|
||||
socklen_t peerlen;
|
||||
int status;
|
||||
struct gtp0_header *pheader;
|
||||
int version = 0; /* GTP version should be determined from header! */
|
||||
uint8_t version;
|
||||
int fd = gsn->fd0;
|
||||
|
||||
/* TODO: Need strategy of userspace buffering and blocking */
|
||||
@@ -2748,15 +2876,17 @@ int gtp_decaps0(struct gsn_t *gsn)
|
||||
|
||||
pheader = (struct gtp0_header *)(buffer);
|
||||
|
||||
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||
|
||||
/* Version should be gtp0 (or earlier) */
|
||||
/* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */
|
||||
/* GTP 0 messages. If other version message is received we reply that we */
|
||||
/* only support version 0, implying that this is the only version */
|
||||
/* supported on this port */
|
||||
if (GTPHDR_F_GET_VER(pheader->flags) > 0) {
|
||||
if (version > 0) {
|
||||
gsn->unsup++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status, "Unsupported GTP version\n");
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||
"Unsupported GTP version %"PRIu8"\n", version);
|
||||
gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
|
||||
continue;
|
||||
}
|
||||
@@ -2780,23 +2910,23 @@ int gtp_decaps0(struct gsn_t *gsn)
|
||||
|
||||
if ((gsn->mode == GTP_MODE_GGSN) &&
|
||||
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
||||
(pheader->type == GTP_UPDATE_PDP_RSP) ||
|
||||
(pheader->type == GTP_DELETE_PDP_RSP))) {
|
||||
(pheader->type == GTP_UPDATE_PDP_RSP))) {
|
||||
gsn->unexpect++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status,
|
||||
"Unexpected GTPv0 Signalling Message\n");
|
||||
"Unexpected GTPv0 Signalling Message '%s'\n",
|
||||
get_value_string(gtp_type_names, pheader->type));
|
||||
continue; /* Silently discard 29.60: 11.1.4 */
|
||||
}
|
||||
|
||||
if ((gsn->mode == GTP_MODE_SGSN) &&
|
||||
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
||||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
|
||||
(pheader->type == GTP_DELETE_PDP_REQ))) {
|
||||
(pheader->type == GTP_UPDATE_PDP_REQ))) {
|
||||
gsn->unexpect++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status,
|
||||
"Unexpected GTPv0 Signalling Message\n");
|
||||
"Unexpected GTPv0 Signalling Message '%s'\n",
|
||||
get_value_string(gtp_type_names, pheader->type));
|
||||
continue; /* Silently discard 29.60: 11.1.4 */
|
||||
}
|
||||
|
||||
@@ -2857,7 +2987,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
||||
socklen_t peerlen;
|
||||
int status;
|
||||
struct gtp1_header_short *pheader;
|
||||
int version = 1; /* TODO GTP version should be determined from header! */
|
||||
uint8_t version;
|
||||
int fd = gsn->fd1c;
|
||||
|
||||
/* TODO: Need strategy of userspace buffering and blocking */
|
||||
@@ -2893,11 +3023,13 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
||||
|
||||
pheader = (struct gtp1_header_short *)(buffer);
|
||||
|
||||
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||
|
||||
/* Version must be no larger than GTP 1 */
|
||||
if (GTPHDR_F_GET_VER(pheader->flags) > 1) {
|
||||
if (version > 1) {
|
||||
gsn->unsup++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status, "Unsupported GTP version\n");
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||
"Unsupported GTP version %"PRIu8"\n", version);
|
||||
gtp_unsup_req(gsn, version, &peer, fd, buffer, status);
|
||||
/*29.60: 11.1.1 */
|
||||
continue;
|
||||
@@ -2907,10 +3039,10 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
||||
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
|
||||
/* GTP 1 messages. If GTP 0 message is received we silently discard */
|
||||
/* the message */
|
||||
if (GTPHDR_F_GET_VER(pheader->flags) < 1) {
|
||||
if (version < 1) {
|
||||
gsn->unsup++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status, "Unsupported GTP version\n");
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||
"Unsupported GTP version %"PRIu8"\n", version);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2955,23 +3087,23 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
||||
|
||||
if ((gsn->mode == GTP_MODE_GGSN) &&
|
||||
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
||||
(pheader->type == GTP_UPDATE_PDP_RSP) ||
|
||||
(pheader->type == GTP_DELETE_PDP_RSP))) {
|
||||
(pheader->type == GTP_UPDATE_PDP_RSP))) {
|
||||
gsn->unexpect++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status,
|
||||
"Unexpected GTPv1 Signalling Message\n");
|
||||
"Unexpected GTPv1 Signalling Message '%s'\n",
|
||||
get_value_string(gtp_type_names, pheader->type));
|
||||
continue; /* Silently discard 29.60: 11.1.4 */
|
||||
}
|
||||
|
||||
if ((gsn->mode == GTP_MODE_SGSN) &&
|
||||
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
||||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
|
||||
(pheader->type == GTP_DELETE_PDP_REQ))) {
|
||||
(pheader->type == GTP_UPDATE_PDP_REQ))) {
|
||||
gsn->unexpect++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status,
|
||||
"Unexpected GTPv1 Signalling Message\n");
|
||||
"Unexpected GTPv1 Signalling Message '%s'\n",
|
||||
get_value_string(gtp_type_names, pheader->type));
|
||||
continue; /* Silently discard 29.60: 11.1.4 */
|
||||
}
|
||||
|
||||
@@ -3032,7 +3164,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
||||
socklen_t peerlen;
|
||||
int status;
|
||||
struct gtp1_header_short *pheader;
|
||||
int version = 1; /* GTP version should be determined from header! */
|
||||
uint8_t version;
|
||||
int fd = gsn->fd1u;
|
||||
|
||||
/* TODO: Need strategy of userspace buffering and blocking */
|
||||
@@ -3069,11 +3201,13 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
||||
|
||||
pheader = (struct gtp1_header_short *)(buffer);
|
||||
|
||||
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||
|
||||
/* Version must be no larger than GTP 1 */
|
||||
if (GTPHDR_F_GET_VER(pheader->flags) > 1) {
|
||||
if (version > 1) {
|
||||
gsn->unsup++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status, "Unsupported GTP version\n");
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||
"Unsupported GTP version %"PRIu8"\n", version);
|
||||
gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */
|
||||
continue;
|
||||
}
|
||||
@@ -3082,10 +3216,10 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
||||
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
|
||||
/* GTP 1 messages. If GTP 0 message is received we silently discard */
|
||||
/* the message */
|
||||
if (GTPHDR_F_GET_VER(pheader->flags) < 1) {
|
||||
if (version < 1) {
|
||||
gsn->unsup++;
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||
status, "Unsupported GTP version\n");
|
||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||
"Unsupported GTP version %"PRIu8"\n", version);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3241,19 +3375,10 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
|
||||
* Conversion functions
|
||||
*************************************************************/
|
||||
|
||||
int char2ul_t(char *src, struct ul_t dst)
|
||||
{
|
||||
dst.l = strlen(src) + 1;
|
||||
dst.v = malloc(dst.l);
|
||||
dst.v[0] = dst.l - 1;
|
||||
memcpy(&dst.v[1], src, dst.v[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ***********************************************************
|
||||
* IP address conversion functions
|
||||
* There exist several types of address representations:
|
||||
* - eua: End User Address. (29.060, 7.7.27, message type 128)
|
||||
* - eua: End User Address. (29.060, 7.7.27, message type 128)
|
||||
* Used for signalling address to mobile station. Supports IPv4
|
||||
* IPv6 x.25 etc. etc.
|
||||
* - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address
|
||||
|
||||
20
gtp/gtp.h
20
gtp/gtp.h
@@ -12,6 +12,9 @@
|
||||
#ifndef _GTP_H
|
||||
#define _GTP_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
|
||||
@@ -85,6 +88,10 @@
|
||||
/* 242-254 For future use. */
|
||||
#define GTP_GPDU 255 /* G-PDU */
|
||||
|
||||
extern const struct value_string gtp_type_names[];
|
||||
static inline const char *gtp_type_name(uint8_t val)
|
||||
{ return get_value_string(gtp_type_names, val); }
|
||||
|
||||
/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
|
||||
/* */
|
||||
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
|
||||
@@ -264,6 +271,7 @@ 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);
|
||||
|
||||
/* Counters */
|
||||
|
||||
@@ -317,7 +325,10 @@ 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);
|
||||
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);
|
||||
|
||||
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *pack, unsigned len);
|
||||
@@ -351,8 +362,15 @@ 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));
|
||||
|
||||
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,
|
||||
|
||||
62
gtp/pdp.c
62
gtp/pdp.c
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017 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
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* pdp.c:
|
||||
* pdp.c:
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -44,17 +44,17 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
* Functions related to PDP storage
|
||||
*
|
||||
* Lifecycle
|
||||
* For a GGSN pdp context life begins with the reception of a
|
||||
* For a GGSN pdp context life begins with the reception of a
|
||||
* create pdp context request. It normally ends with the reception
|
||||
* of a delete pdp context request, but will also end with the
|
||||
* reception of an error indication message.
|
||||
* reception of an error indication message.
|
||||
* Provisions should probably be made for terminating pdp contexts
|
||||
* based on either idle timeout, or by sending downlink probe
|
||||
* based on either idle timeout, or by sending downlink probe
|
||||
* messages (ping?) to see if the MS is still responding.
|
||||
*
|
||||
*
|
||||
* For an SGSN pdp context life begins with the application just
|
||||
* before sending off a create pdp context request. It normally
|
||||
* ends when a delete pdp context response message is received
|
||||
* ends when a delete pdp context response message is received
|
||||
* from the GGSN, but should also end when with the reception of
|
||||
* an error indication message.
|
||||
*
|
||||
@@ -64,15 +64,15 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
* Downlink packets received in the GGSN are identified only by their
|
||||
* network interface together with their destination IP address (Two
|
||||
* network interfaces can use the same private IP address). Each IMSI
|
||||
* (mobile station) can have several PDP contexts using the same IP
|
||||
* (mobile station) can have several PDP contexts using the same IP
|
||||
* address. In this case the traffic flow template (TFT) is used to
|
||||
* determine the correct PDP context for a particular IMSI. Also it
|
||||
* determine the correct PDP context for a particular IMSI. Also it
|
||||
* should be possible for each PDP context to use several IP adresses
|
||||
* For fixed wireless access a mobile station might need a full class
|
||||
* C network. Even in the case of several IP adresses the PDP context
|
||||
* should be determined on the basis of the network IP address.
|
||||
* Thus we need a hash table based on network interface + IP address.
|
||||
*
|
||||
*
|
||||
* Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
|
||||
* is collectively called the tunnel identifier. There is also a 16 bit
|
||||
* flow label that can be used for identification of uplink packets. This
|
||||
@@ -85,7 +85,7 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
* Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
|
||||
* be used for directly addressing the PDP context.
|
||||
|
||||
* pdp_newpdp
|
||||
* pdp_newpdp
|
||||
* Gives you a pdp context with no hash references In some way
|
||||
* this should have a limited lifetime.
|
||||
*
|
||||
@@ -296,7 +296,7 @@ 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;
|
||||
@@ -304,7 +304,7 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
|
||||
|
||||
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[2], eua->v[3],
|
||||
eua->v[4], eua->v[5]);
|
||||
|
||||
pdp->ipnext = NULL;
|
||||
@@ -316,9 +316,9 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
|
||||
|
||||
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
|
||||
pdp_prev = pdp2;
|
||||
if (!pdp_prev)
|
||||
if (!pdp_prev)
|
||||
haship[hash] = pdp;
|
||||
else
|
||||
else
|
||||
pdp_prev->ipnext = pdp;
|
||||
if (PDP_DEBUG) printf("End pdp_ipset\n");
|
||||
return 0;
|
||||
@@ -331,9 +331,9 @@ int pdp_ipdel(struct pdp_t *pdp) {
|
||||
if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
|
||||
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
|
||||
if (pdp2 == pdp) {
|
||||
if (!pdp_prev)
|
||||
if (!pdp_prev)
|
||||
haship[hash] = pdp2->ipnext;
|
||||
else
|
||||
else
|
||||
pdp_prev->ipnext = pdp2->ipnext;
|
||||
if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
|
||||
return 0;
|
||||
@@ -347,41 +347,23 @@ int pdp_ipdel(struct pdp_t *pdp) {
|
||||
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,
|
||||
/#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) &&
|
||||
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",
|
||||
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 */
|
||||
|
||||
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
|
||||
{
|
||||
eua->l = 6;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||
memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst)
|
||||
{
|
||||
if ((eua->l != 6) || (eua->v[0] != PDP_EUA_ORG_IETF) || (eua->v[1] != PDP_EUA_TYPE_v4)) {
|
||||
return EOF;
|
||||
}
|
||||
memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
|
||||
|
||||
21
gtp/pdp.h
21
gtp/pdp.h
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003 Mondru AB.
|
||||
* Copyright (C) 2017 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
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PDP_H
|
||||
@@ -26,6 +26,7 @@ struct gsn_t;
|
||||
#define PDP_EUA_ORG_IETF 0xF1
|
||||
#define PDP_EUA_TYPE_v4 0x21
|
||||
#define PDP_EUA_TYPE_v6 0x57
|
||||
#define PDP_EUA_TYPE_v4v6 0x8D
|
||||
|
||||
/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
|
||||
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
|
||||
@@ -71,16 +72,16 @@ struct ul255_t {
|
||||
* and 09.60.
|
||||
* 31 * 4 + 15 structs + = 120 + 15 structs ~ 2k / context
|
||||
* Structs: IP address 16+4 bytes (6), APN 255 bytes (2)
|
||||
* QOS: 255 bytes (3), msisdn 16 bytes (1),
|
||||
* QOS: 255 bytes (3), msisdn 16 bytes (1),
|
||||
*
|
||||
* TODO: We need to consider who manages the pdp_t hash tables
|
||||
* Is it gtp_lib, or is it the application?
|
||||
* I suppose that it will be gtp_lib.
|
||||
* I suppose that it will be gtp_lib.
|
||||
* SGSN will ask gtplib for new pdp_t. Fill out the fields,
|
||||
* and pass it on to gtp_create_pdp_req.
|
||||
* GGSN will receive gtp_create_pdp_ind, create new pdp_t and
|
||||
* send responce to SGSN.
|
||||
* SGSN will receive response and gtplib will find the
|
||||
* SGSN will receive response and gtplib will find the
|
||||
* original pdp_t corresponding to the request. This will be
|
||||
* passed on to the application.
|
||||
* Eventually the SGSN will close the connection, and the
|
||||
@@ -88,10 +89,10 @@ struct ul255_t {
|
||||
* This means that gtplib need to have functions to
|
||||
* allocate, free, sort and find pdp_t
|
||||
* (newpdp, freepdp, getpdp)
|
||||
* Hash tables: TID, IMSI, IP etc.)
|
||||
* Hash tables: TID, IMSI, IP etc.)
|
||||
*
|
||||
*
|
||||
* Secondary PDP Context Activation Procedure
|
||||
* Secondary PDP Context Activation Procedure
|
||||
*
|
||||
* With GTP version 1 it is possible to establish multiple PDP
|
||||
* contexts with the same IP address. With this scheme the first
|
||||
@@ -121,7 +122,7 @@ struct pdp_t {
|
||||
/* Parameters shared by all PDP context belonging to the same MS */
|
||||
|
||||
void *ipif; /* IP network interface */
|
||||
void *peer; /* Pointer to peer protocol */
|
||||
void *peer[2]; /* Pointer to peer protocol */
|
||||
void *asap; /* Application specific service access point */
|
||||
|
||||
uint64_t imsi; /* International Mobile Subscriber Identity. */
|
||||
@@ -266,8 +267,6 @@ int pdp_ipdel(struct pdp_t *pdp);
|
||||
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua);
|
||||
*/
|
||||
|
||||
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
|
||||
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
|
||||
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
|
||||
|
||||
#endif /* !_PDP_H */
|
||||
|
||||
@@ -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 %d, *PEER %d\n", sizeof(peer),
|
||||
printf("SIZEOF PEER %zu, *PEER %zu\n", sizeof(peer),
|
||||
sizeof(*peer));
|
||||
|
||||
qmsg->seq = seq;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
noinst_LIBRARIES = libmisc.a
|
||||
|
||||
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.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
|
||||
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)
|
||||
libmisc_a_SOURCES += gtp-kernel.c
|
||||
endif
|
||||
|
||||
@@ -77,56 +77,20 @@ static int gtp_kernel_init_once(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup)
|
||||
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
|
||||
{
|
||||
struct in_addr net;
|
||||
const char *net_arg;
|
||||
|
||||
if (!gtp_nl.nl)
|
||||
gtp_kernel_init_once();
|
||||
|
||||
if (prefix->addr.len != 4) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "we only support IPv4 in this path :/");
|
||||
if (gtp_kernel_init_once() < 0)
|
||||
return -1;
|
||||
}
|
||||
net = prefix->addr.v4;
|
||||
|
||||
if (gtp_dev_create(-1, devname, gsn->fd0, gsn->fd1u) < 0) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
|
||||
strerror(errno));
|
||||
return gtp_dev_create(dest_ns, devname, fd0, fd1u);
|
||||
}
|
||||
|
||||
int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u)
|
||||
{
|
||||
if (gtp_kernel_init_once() < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
net_arg = in46p_ntoa(prefix);
|
||||
|
||||
DEBUGP(DGGSN, "Setting route to reach %s via %s\n", net_arg, devname);
|
||||
|
||||
if (gtp_dev_config(devname, &net, prefix->prefixlen) < 0) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "Cannot add route to reach network %s\n", net_arg);
|
||||
}
|
||||
|
||||
/* launch script if it is set to bring up the route to reach
|
||||
* the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
|
||||
* using native rtnetlink interface given that we know the
|
||||
* MS network mask, later.
|
||||
*/
|
||||
if (ipup) {
|
||||
char cmd[1024];
|
||||
int err;
|
||||
|
||||
/* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
|
||||
snprintf(cmd, sizeof(cmd), "%s %s %s", ipup, devname, net_arg);
|
||||
cmd[sizeof(cmd)-1] = '\0';
|
||||
|
||||
err = system(cmd);
|
||||
if (err < 0) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "Failed to launch script `%s'\n", ipup);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
LOGP(DGGSN, LOGL_NOTICE, "GTP kernel configured\n");
|
||||
|
||||
return 0;
|
||||
return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u);
|
||||
}
|
||||
|
||||
void gtp_kernel_stop(const char *devname)
|
||||
@@ -7,18 +7,20 @@ extern int debug;
|
||||
extern char *ipup;
|
||||
|
||||
#ifdef GTP_KERNEL
|
||||
int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup);
|
||||
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u);
|
||||
int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u);
|
||||
void gtp_kernel_stop(const char *devname);
|
||||
|
||||
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname);
|
||||
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname);
|
||||
|
||||
#else
|
||||
static inline int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup)
|
||||
static inline int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
|
||||
{
|
||||
SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n");
|
||||
return -1;
|
||||
}
|
||||
#define gtp_kernel_create_sgsn gtp_kernel_create
|
||||
|
||||
static inline void gtp_kernel_stop(const char *devname) {}
|
||||
|
||||
109
lib/in46_addr.c
109
lib/in46_addr.c
@@ -253,33 +253,66 @@ unsigned int in46a_netmasklen(const struct in46_addr *netmask)
|
||||
}
|
||||
}
|
||||
|
||||
/*! Convert given PDP End User Address to in46_addr
|
||||
* \returns 0 on success; negative on error */
|
||||
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
|
||||
/*! Convert given array of in46_addr to PDP End User Address
|
||||
* \param[in] src Array containing 1 or 2 in46_addr
|
||||
* \param[out] eua End User Address structure to fill
|
||||
* \returns 0 on success; negative on error
|
||||
*
|
||||
* In case size is 2, this function expects to find exactly one IPv4 and one
|
||||
* IPv6 addresses in src. */
|
||||
int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua)
|
||||
{
|
||||
switch (src->len) {
|
||||
case 4:
|
||||
eua->l = 6;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
||||
break;
|
||||
case 8:
|
||||
case 16:
|
||||
eua->l = 18;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v6;
|
||||
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
return -1;
|
||||
const struct in46_addr *src_v4, *src_v6;
|
||||
if (size == 1) {
|
||||
switch (src->len) {
|
||||
case 4:
|
||||
eua->l = 6;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
||||
break;
|
||||
case 8:
|
||||
case 16:
|
||||
eua->l = 18;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v6;
|
||||
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src[0].len == src[1].len)
|
||||
return -1; /* we should have a v4 and a v6 address */
|
||||
|
||||
src_v4 = (src[0].len == 4) ? &src[0] : &src[1];
|
||||
src_v6 = (src[0].len == 4) ? &src[1] : &src[0];
|
||||
|
||||
eua->l = 22;
|
||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||
eua->v[1] = PDP_EUA_TYPE_v4v6;
|
||||
memcpy(&eua->v[2], &src_v4->v4, 4);
|
||||
memcpy(&eua->v[6], &src_v6->v6, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Convert given in46_addr to PDP End User Address
|
||||
* \returns 0 on success; negative on error */
|
||||
/*! Convert given PDP End User Address to an array of in46_addr
|
||||
* \param[in] eua End User Address structure to parse
|
||||
* \param[out] dst Array containing 2 in46_addr
|
||||
* \returns number of parsed addresses (1 or 2) on success; negative on error
|
||||
*
|
||||
* This function expects to receive an End User Address struct together with an
|
||||
* array of 2 zeroed in46_addr structs. The in46_addr structs are filled in
|
||||
* order, hence if the function returns 1 the parsed address will be stored in
|
||||
* the first struct and the second one will be left intact. If 2 is returned, it
|
||||
* is guaranteed that one of them is an IPv4 and the other one is an IPv6, but
|
||||
* the order in which they are presented is not specified and must be
|
||||
* discovered for instance by checking the len field of each address.
|
||||
*/
|
||||
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
|
||||
{
|
||||
if (eua->l < 2)
|
||||
@@ -295,22 +328,46 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
|
||||
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
|
||||
else
|
||||
dst->v4.s_addr = 0;
|
||||
break;
|
||||
return 1;
|
||||
case PDP_EUA_TYPE_v6:
|
||||
dst->len = 16;
|
||||
if (eua->l >= 18)
|
||||
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
|
||||
else
|
||||
memset(&dst->v6, 0, 16);
|
||||
break;
|
||||
return 1;
|
||||
case PDP_EUA_TYPE_v4v6:
|
||||
/* 3GPP TS 29.060, section 7.7.27 */
|
||||
switch (eua->l) {
|
||||
case 2: /* v4 & v6 dynamic */
|
||||
dst[0].v4.s_addr = 0;
|
||||
memset(&dst[1].v6, 0, 16);
|
||||
break;
|
||||
case 6: /* v4 static, v6 dynamic */
|
||||
memcpy(&dst[0].v4, &eua->v[2], 4);
|
||||
memset(&dst[1].v6, 0, 16);
|
||||
break;
|
||||
case 18: /* v4 dynamic, v6 static */
|
||||
dst[0].v4.s_addr = 0;
|
||||
memcpy(&dst[1].v6, &eua->v[2], 16);
|
||||
break;
|
||||
case 22: /* v4 & v6 static */
|
||||
memcpy(&dst[0].v4, &eua->v[2], 4);
|
||||
memcpy(&dst[1].v6, &eua->v[6], 16);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
dst[0].len = 4;
|
||||
dst[1].len = 16;
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
default_to_dyn_v4:
|
||||
/* assume dynamic IPv4 by default */
|
||||
dst->len = 4;
|
||||
dst->v4.s_addr = 0;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr
|
||||
extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
|
||||
unsigned int in46a_netmasklen(const struct in46_addr *netmask);
|
||||
|
||||
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua);
|
||||
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);
|
||||
|
||||
29
lib/ippool.c
29
lib/ippool.c
@@ -26,16 +26,16 @@ int ippool_printaddr(struct ippool_t *this)
|
||||
{
|
||||
unsigned int n;
|
||||
printf("ippool_printaddr\n");
|
||||
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);
|
||||
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);
|
||||
|
||||
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 %d next %d addr %s\n",
|
||||
printf("Unit %d inuse %d prev %td next %td 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;
|
||||
struct in46_addr addr = { 0 };
|
||||
size_t addrprefixlen;
|
||||
struct in46_addr stataddr;
|
||||
size_t stataddrprefixlen;
|
||||
@@ -276,9 +276,8 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
|
||||
(*this)->hashmask = (*this)->hashsize - 1;
|
||||
|
||||
/* Allocate hash table */
|
||||
if (!
|
||||
((*this)->hash =
|
||||
calloc(sizeof(struct ippoolm_t), (*this)->hashsize))) {
|
||||
(*this)->hash = calloc((*this)->hashsize, sizeof(struct ippoolm_t *));
|
||||
if (!(*this)->hash) {
|
||||
SYS_ERR(DIP, LOGL_ERROR, 0,
|
||||
"Failed to allocate memory for hash members in ippool");
|
||||
return -1;
|
||||
@@ -513,7 +512,15 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
|
||||
p2->next = NULL;
|
||||
p2->prev = NULL;
|
||||
p2->inuse = 2; /* Static address in use */
|
||||
memcpy(&p2->addr, addr, sizeof(addr));
|
||||
/* 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;
|
||||
}
|
||||
*member = p2;
|
||||
(void)ippool_hashadd(this, *member);
|
||||
if (0)
|
||||
|
||||
727
lib/netdev.c
Normal file
727
lib/netdev.c
Normal file
@@ -0,0 +1,727 @@
|
||||
/*
|
||||
* TUN interface functions.
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017-2018 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
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* netdev.c: Contains generic network device related functionality.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#elif defined (__FreeBSD__)
|
||||
#include <net/if_var.h>
|
||||
#include <netinet/in_var.h>
|
||||
|
||||
#elif defined (__APPLE__)
|
||||
#include <net/if.h>
|
||||
|
||||
#else
|
||||
#error "Unknown platform!"
|
||||
#endif
|
||||
|
||||
#include "netdev.h"
|
||||
#include "syserr.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <linux/ipv6.h>
|
||||
|
||||
static int netdev_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
|
||||
{
|
||||
int len = RTA_LENGTH(dlen);
|
||||
int alen = NLMSG_ALIGN(n->nlmsg_len);
|
||||
struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
|
||||
if (alen + len > nsize)
|
||||
return -1;
|
||||
rta->rta_len = len;
|
||||
rta->rta_type = type;
|
||||
memcpy(RTA_DATA(rta), d, dlen);
|
||||
n->nlmsg_len = alen + len;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int netdev_sifflags(const char *devname, int flags)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ifr, '\0', sizeof(ifr));
|
||||
ifr.ifr_flags = flags;
|
||||
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFFLAGS) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_setaddr4(const char *devname, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ifr, '\0', sizeof(ifr));
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
ifr.ifr_dstaddr.sa_family = AF_INET;
|
||||
|
||||
#if defined(__linux__)
|
||||
ifr.ifr_netmask.sa_family = AF_INET;
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
|
||||
sizeof(struct sockaddr_in);
|
||||
((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
|
||||
sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
|
||||
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr) { /* Set the interface address */
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
|
||||
sizeof(*addr));
|
||||
if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFADDR) failed");
|
||||
} else {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, errno,
|
||||
"ioctl(SIOCSIFADDR): Address already exists");
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dstaddr) { /* Set the destination address */
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
|
||||
dstaddr, sizeof(*dstaddr));
|
||||
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFDSTADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (netmask) { /* Set the netmask */
|
||||
#if defined(__linux__)
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
|
||||
netmask, sizeof(*netmask));
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
|
||||
netmask->s_addr;
|
||||
#endif
|
||||
|
||||
if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFNETMASK) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
|
||||
|
||||
/* On linux the route to the interface is set automatically
|
||||
on FreeBSD we have to do this manually */
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
netdev_addroute(dstaddr, addr, &this->netmask);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr *dstaddr,
|
||||
size_t prefixlen)
|
||||
{
|
||||
struct in6_ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
#if defined(__linux__)
|
||||
ifr.ifr6_prefixlen = prefixlen;
|
||||
ifr.ifr6_ifindex = if_nametoindex(devname);
|
||||
if (ifr.ifr6_ifindex == 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", devname);
|
||||
return -1;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
|
||||
#endif
|
||||
|
||||
/* Create a channel to the NET kernel */
|
||||
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (addr) {
|
||||
memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
|
||||
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
|
||||
} else {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: looks like this is not possible/necessary for IPv6? */
|
||||
if (dstaddr) {
|
||||
memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
|
||||
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
if (addr)
|
||||
memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
|
||||
if (dstaddr)
|
||||
memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
|
||||
|
||||
if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
|
||||
netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
|
||||
|
||||
/* On linux the route to the interface is set automatically
|
||||
on FreeBSD we have to do this manually */
|
||||
#if 0 /* FIXME */
|
||||
//#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
netdev_addroute6(dstaddr, addr, prefixlen);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_addaddr4(const char *devname, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
int fd;
|
||||
#if defined(__linux__)
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifaddrmsg i;
|
||||
char buf[TUN_NLBUFSIZE];
|
||||
} req;
|
||||
|
||||
struct sockaddr_nl local;
|
||||
socklen_t addr_len;
|
||||
int status;
|
||||
|
||||
struct sockaddr_nl nladdr;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
req.n.nlmsg_type = RTM_NEWADDR;
|
||||
req.i.ifa_family = AF_INET;
|
||||
req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
|
||||
req.i.ifa_flags = 0;
|
||||
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
|
||||
req.i.ifa_index = if_nametoindex(devname);
|
||||
if (!req.i.ifa_index) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
|
||||
if (dstaddr)
|
||||
netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
|
||||
|
||||
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = 0;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local);
|
||||
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"getsockname() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_len != sizeof(local)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address length %d", addr_len);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (local.nl_family != AF_NETLINK) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address family %d", local.nl_family);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov.iov_base = (void *)&req.n;
|
||||
iov.iov_len = req.n.nlmsg_len;
|
||||
|
||||
msg.msg_name = (void *)&nladdr;
|
||||
msg.msg_namelen = sizeof(nladdr);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
memset(&nladdr, 0, sizeof(nladdr));
|
||||
nladdr.nl_family = AF_NETLINK;
|
||||
nladdr.nl_pid = 0;
|
||||
nladdr.nl_groups = 0;
|
||||
|
||||
req.n.nlmsg_seq = 0;
|
||||
req.n.nlmsg_flags |= NLM_F_ACK;
|
||||
|
||||
status = sendmsg(fd, &msg, 0);
|
||||
if (status != req.n.nlmsg_len) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
|
||||
if (status == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#elif defined (__FreeBSD__) || defined (__APPLE__)
|
||||
struct ifaliasreq areq;
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
/* Set up interface name */
|
||||
strncpy(areq.ifra_name, devname, IFNAMSIZ);
|
||||
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
|
||||
sizeof(areq.ifra_addr);
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
|
||||
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
|
||||
sizeof(areq.ifra_mask);
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
|
||||
netmask->s_addr;
|
||||
|
||||
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
|
||||
sizeof(areq.ifra_broadaddr);
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
|
||||
dstaddr->s_addr;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCAIFADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_addaddr6(const char *devname, struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen)
|
||||
{
|
||||
int fd;
|
||||
#if defined(__linux__)
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifaddrmsg i;
|
||||
char buf[TUN_NLBUFSIZE];
|
||||
} req;
|
||||
|
||||
struct sockaddr_nl local;
|
||||
socklen_t addr_len;
|
||||
int status;
|
||||
|
||||
struct sockaddr_nl nladdr;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
req.n.nlmsg_type = RTM_NEWADDR;
|
||||
req.i.ifa_family = AF_INET6;
|
||||
req.i.ifa_prefixlen = 64; /* 64 FOR IPv6 */
|
||||
req.i.ifa_flags = 0;
|
||||
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
|
||||
req.i.ifa_index = if_nametoindex(devname);
|
||||
if (!req.i.ifa_index) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
|
||||
if (dstaddr)
|
||||
netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
|
||||
|
||||
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = 0;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local);
|
||||
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"getsockname() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_len != sizeof(local)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address length %d", addr_len);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (local.nl_family != AF_NETLINK) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address family %d", local.nl_family);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov.iov_base = (void *)&req.n;
|
||||
iov.iov_len = req.n.nlmsg_len;
|
||||
|
||||
msg.msg_name = (void *)&nladdr;
|
||||
msg.msg_namelen = sizeof(nladdr);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
memset(&nladdr, 0, sizeof(nladdr));
|
||||
nladdr.nl_family = AF_NETLINK;
|
||||
nladdr.nl_pid = 0;
|
||||
nladdr.nl_groups = 0;
|
||||
|
||||
req.n.nlmsg_seq = 0;
|
||||
req.n.nlmsg_flags |= NLM_F_ACK;
|
||||
|
||||
status = sendmsg(fd, &msg, 0);
|
||||
if (status != req.n.nlmsg_len) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
|
||||
if (status == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#elif defined (__FreeBSD__) || defined (__APPLE__)
|
||||
struct ifaliasreq areq;
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
/* Set up interface name */
|
||||
strncpy(areq.ifra_name, devname, IFNAMSIZ);
|
||||
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
|
||||
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
|
||||
|
||||
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCAIFADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_route(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
{
|
||||
int fd;
|
||||
#if defined(__linux__)
|
||||
struct rtentry r;
|
||||
|
||||
memset(&r, '\0', sizeof(r));
|
||||
r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r.rt_dst.sa_family = AF_INET;
|
||||
r.rt_gateway.sa_family = AF_INET;
|
||||
r.rt_genmask.sa_family = AF_INET;
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
|
||||
sizeof(*gateway));
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
|
||||
sizeof(*mask));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
struct {
|
||||
struct rt_msghdr rt;
|
||||
struct sockaddr_in dst;
|
||||
struct sockaddr_in gate;
|
||||
struct sockaddr_in mask;
|
||||
} req;
|
||||
struct rt_msghdr *rtm;
|
||||
|
||||
if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&req, 0x00, sizeof(req));
|
||||
|
||||
rtm = &req.rt;
|
||||
|
||||
rtm->rtm_msglen = sizeof(req);
|
||||
rtm->rtm_version = RTM_VERSION;
|
||||
if (delete) {
|
||||
rtm->rtm_type = RTM_DELETE;
|
||||
} else {
|
||||
rtm->rtm_type = RTM_ADD;
|
||||
}
|
||||
rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
|
||||
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
|
||||
rtm->rtm_pid = getpid();
|
||||
rtm->rtm_seq = 0044; /* TODO */
|
||||
|
||||
req.dst.sin_family = AF_INET;
|
||||
req.dst.sin_len = sizeof(req.dst);
|
||||
req.mask.sin_family = AF_INET;
|
||||
req.mask.sin_len = sizeof(req.mask);
|
||||
req.gate.sin_family = AF_INET;
|
||||
req.gate.sin_len = sizeof(req.gate);
|
||||
|
||||
req.dst.sin_addr.s_addr = dst->s_addr;
|
||||
req.mask.sin_addr.s_addr = mask->s_addr;
|
||||
req.gate.sin_addr.s_addr = gateway->s_addr;
|
||||
|
||||
if (write(fd, rtm, rtm->rtm_msglen) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return netdev_route(dst, gateway, mask, 0);
|
||||
}
|
||||
|
||||
int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return netdev_route(dst, gateway, mask, 1);
|
||||
}
|
||||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
/*! Obtain the local address of a network device
|
||||
* \param[in] devname Target device owning the IP
|
||||
* \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
|
||||
* \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
|
||||
* \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
|
||||
* \returns The number of ips found following the criteria specified by flags, -1 on error.
|
||||
*
|
||||
* This function will fill prefix_list with up to prefix_size IPs following the
|
||||
* criteria specified by flags parameter. It returns the number of IPs matching
|
||||
* the criteria. As a result, the number returned can be bigger than
|
||||
* prefix_size. It can be used with prefix_size=0 to get an estimate of the size
|
||||
* needed for prefix_list.
|
||||
*/
|
||||
int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
|
||||
{
|
||||
static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct in46_addr netmask;
|
||||
size_t count = 0;
|
||||
bool is_ipv6_ll;
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(ifa->ifa_name, devname))
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
|
||||
struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
|
||||
struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
|
||||
|
||||
if (count < prefix_size) {
|
||||
netmask.len = sizeof(netmask4->sin_addr);
|
||||
netmask.v4 = netmask4->sin_addr;
|
||||
prefix_list[count].addr.len = sizeof(sin4->sin_addr);
|
||||
prefix_list[count].addr.v4 = sin4->sin_addr;
|
||||
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
|
||||
struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
|
||||
|
||||
is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
|
||||
if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
|
||||
continue;
|
||||
if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
|
||||
continue;
|
||||
|
||||
if (count < prefix_size) {
|
||||
netmask.len = sizeof(netmask6->sin6_addr);
|
||||
netmask.v6 = netmask6->sin6_addr;
|
||||
prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
|
||||
prefix_list[count].addr.v6 = sin6->sin6_addr;
|
||||
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
return count;
|
||||
}
|
||||
72
lib/netdev.h
Normal file
72
lib/netdev.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
/*
|
||||
* TUN interface functions.
|
||||
* Copyright (C) 2002, 2003 Mondru AB.
|
||||
* Copyright (C) 2017-2018 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
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include "../lib/in46_addr.h"
|
||||
|
||||
#define TUN_NLBUFSIZE 1024
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* ipv6 ip type flags for tun_ipv6_local_get() */
|
||||
enum {
|
||||
IP_TYPE_IPv4 = 1,
|
||||
IP_TYPE_IPv6_LINK = 2,
|
||||
IP_TYPE_IPv6_NONLINK = 4,
|
||||
};
|
||||
#define IP_TYPE_IPv6 (IP_TYPE_IPv6_LINK | IP_TYPE_IPv6_NONLINK)
|
||||
|
||||
|
||||
#ifndef HAVE_IPHDR
|
||||
struct iphdr
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned int ihl:4;
|
||||
unsigned int version:4;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned int version:4;
|
||||
unsigned int ihl:4;
|
||||
#else
|
||||
# error "Please fix <bits/endian.h>"
|
||||
#endif
|
||||
u_int8_t tos;
|
||||
u_int16_t tot_len;
|
||||
u_int16_t id;
|
||||
u_int16_t frag_off;
|
||||
u_int8_t ttl;
|
||||
u_int8_t protocol;
|
||||
u_int16_t check;
|
||||
u_int32_t saddr;
|
||||
u_int32_t daddr;
|
||||
/*The options start here. */
|
||||
};
|
||||
#endif /* !HAVE_IPHDR */
|
||||
|
||||
extern int netdev_setaddr4(const char *devname, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
||||
|
||||
extern int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr *dstaddr,
|
||||
size_t prefixlen);
|
||||
|
||||
extern int netdev_addaddr4(const char *devname, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
||||
|
||||
extern int netdev_addaddr6(const char *devname, struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen);
|
||||
|
||||
extern int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
|
||||
extern int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
|
||||
size_t prefix_size, int flags);
|
||||
681
lib/tun.c
681
lib/tun.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* TUN interface functions.
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2017-2018 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
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -42,8 +43,6 @@
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#elif defined (__FreeBSD__)
|
||||
#include <net/if_tun.h>
|
||||
@@ -59,527 +58,104 @@
|
||||
|
||||
#include "tun.h"
|
||||
#include "syserr.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <linux/ipv6.h>
|
||||
|
||||
static int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
|
||||
{
|
||||
int len = RTA_LENGTH(dlen);
|
||||
int alen = NLMSG_ALIGN(n->nlmsg_len);
|
||||
struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
|
||||
if (alen + len > nsize)
|
||||
return -1;
|
||||
rta->rta_len = len;
|
||||
rta->rta_type = type;
|
||||
memcpy(RTA_DATA(rta), d, dlen);
|
||||
n->nlmsg_len = alen + len;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tun_sifflags(struct tun_t *this, int flags)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ifr, '\0', sizeof(ifr));
|
||||
ifr.ifr_flags = flags;
|
||||
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFFLAGS) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
#include "gtp-kernel.h"
|
||||
|
||||
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
int rc;
|
||||
rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
memset(&ifr, '\0', sizeof(ifr));
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
ifr.ifr_dstaddr.sa_family = AF_INET;
|
||||
|
||||
#if defined(__linux__)
|
||||
ifr.ifr_netmask.sa_family = AF_INET;
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
|
||||
sizeof(struct sockaddr_in);
|
||||
((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
|
||||
sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
|
||||
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
if (addr) {
|
||||
this->addr.len = sizeof(struct in_addr);
|
||||
this->addr.v4.s_addr = addr->s_addr;
|
||||
}
|
||||
|
||||
if (addr) { /* Set the interface address */
|
||||
this->addr.s_addr = addr->s_addr;
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
|
||||
sizeof(*addr));
|
||||
if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFADDR) failed");
|
||||
} else {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, errno,
|
||||
"ioctl(SIOCSIFADDR): Address already exists");
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (dstaddr) {
|
||||
this->dstaddr.len = sizeof(struct in_addr);
|
||||
this->dstaddr.v4.s_addr = dstaddr->s_addr;
|
||||
}
|
||||
|
||||
if (dstaddr) { /* Set the destination address */
|
||||
this->dstaddr.s_addr = dstaddr->s_addr;
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
|
||||
dstaddr, sizeof(*dstaddr));
|
||||
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFDSTADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (netmask) { /* Set the netmask */
|
||||
if (netmask)
|
||||
this->netmask.s_addr = netmask->s_addr;
|
||||
#if defined(__linux__)
|
||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
|
||||
netmask, sizeof(*netmask));
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
|
||||
netmask->s_addr;
|
||||
#endif
|
||||
|
||||
if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCSIFNETMASK) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
|
||||
/* On linux the route to the interface is set automatically
|
||||
on FreeBSD we have to do this manually */
|
||||
|
||||
/* TODO: How does it work on Solaris? */
|
||||
|
||||
tun_sifflags(this, IFF_UP | IFF_RUNNING);
|
||||
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
tun_addroute(this, dstaddr, addr, &this->netmask);
|
||||
this->routes = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
|
||||
size_t prefixlen)
|
||||
{
|
||||
struct in6_ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
#if defined(__linux__)
|
||||
ifr.ifr6_prefixlen = prefixlen;
|
||||
ifr.ifr6_ifindex = if_nametoindex(this->devname);
|
||||
if (ifr.ifr6_ifindex == 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
|
||||
return -1;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
|
||||
#endif
|
||||
|
||||
/* Create a channel to the NET kernel */
|
||||
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (addr) {
|
||||
memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
|
||||
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
|
||||
} else {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: looks like this is not possible/necessary for IPv6? */
|
||||
int rc;
|
||||
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (dstaddr) {
|
||||
memcpy(&this->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");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
this->dstaddr.len = sizeof(*dstaddr);
|
||||
memcpy(&this->dstaddr.v6, dstaddr, sizeof(*dstaddr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
if (addr)
|
||||
memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
|
||||
if (dstaddr)
|
||||
memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
|
||||
|
||||
if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
|
||||
/* On linux the route to the interface is set automatically
|
||||
on FreeBSD we have to do this manually */
|
||||
|
||||
/* TODO: How does it work on Solaris? */
|
||||
|
||||
tun_sifflags(this, IFF_UP | IFF_RUNNING);
|
||||
|
||||
#if 0 /* FIXME */
|
||||
//#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
tun_addroute6(this, dstaddr, addr, prefixlen);
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
this->routes = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
|
||||
static int tun_addaddr4(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
struct in_addr netmask;
|
||||
switch (addr->len) {
|
||||
case 4:
|
||||
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||
return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
||||
case 16:
|
||||
return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int tun_addaddr(struct tun_t *this,
|
||||
struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
|
||||
#if defined(__linux__)
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifaddrmsg i;
|
||||
char buf[TUN_NLBUFSIZE];
|
||||
} req;
|
||||
|
||||
struct sockaddr_nl local;
|
||||
socklen_t addr_len;
|
||||
int fd;
|
||||
int status;
|
||||
|
||||
struct sockaddr_nl nladdr;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr4(this, addr, dstaddr, netmask);
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
req.n.nlmsg_type = RTM_NEWADDR;
|
||||
req.i.ifa_family = AF_INET;
|
||||
req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
|
||||
req.i.ifa_flags = 0;
|
||||
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
|
||||
req.i.ifa_index = if_nametoindex(this->devname);
|
||||
if (!req.i.ifa_index) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
|
||||
tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
|
||||
|
||||
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = 0;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local);
|
||||
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"getsockname() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_len != sizeof(local)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address length %d", addr_len);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (local.nl_family != AF_NETLINK) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address family %d", local.nl_family);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov.iov_base = (void *)&req.n;
|
||||
iov.iov_len = req.n.nlmsg_len;
|
||||
|
||||
msg.msg_name = (void *)&nladdr;
|
||||
msg.msg_namelen = sizeof(nladdr);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
memset(&nladdr, 0, sizeof(nladdr));
|
||||
nladdr.nl_family = AF_NETLINK;
|
||||
nladdr.nl_pid = 0;
|
||||
nladdr.nl_groups = 0;
|
||||
|
||||
req.n.nlmsg_seq = 0;
|
||||
req.n.nlmsg_flags |= NLM_F_ACK;
|
||||
|
||||
status = sendmsg(fd, &msg, 0);
|
||||
if (status != req.n.nlmsg_len) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
|
||||
if (status == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
return 0;
|
||||
|
||||
#elif defined (__FreeBSD__) || defined (__APPLE__)
|
||||
|
||||
int fd;
|
||||
struct ifaliasreq areq;
|
||||
int rc;
|
||||
|
||||
/* TODO: Is this needed on FreeBSD? */
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Set up interface name */
|
||||
strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
|
||||
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
|
||||
sizeof(areq.ifra_addr);
|
||||
((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
|
||||
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
|
||||
sizeof(areq.ifra_mask);
|
||||
((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
|
||||
netmask->s_addr;
|
||||
|
||||
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
|
||||
sizeof(areq.ifra_broadaddr);
|
||||
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
|
||||
dstaddr->s_addr;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCAIFADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tun_route(struct tun_t *this,
|
||||
struct in_addr *dst,
|
||||
struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
static int tun_addaddr6(struct tun_t *this,
|
||||
struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen)
|
||||
{
|
||||
int rc;
|
||||
|
||||
#if defined(__linux__)
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr6(this, addr, dstaddr, prefixlen);
|
||||
|
||||
struct rtentry r;
|
||||
int fd;
|
||||
rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
memset(&r, '\0', sizeof(r));
|
||||
r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
|
||||
this->addrs++;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
|
||||
{
|
||||
struct in_addr netmask;
|
||||
switch (addr->len) {
|
||||
case 4:
|
||||
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
||||
case 16:
|
||||
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
r.rt_dst.sa_family = AF_INET;
|
||||
r.rt_gateway.sa_family = AF_INET;
|
||||
r.rt_genmask.sa_family = AF_INET;
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
|
||||
sizeof(*gateway));
|
||||
memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
|
||||
sizeof(*mask));
|
||||
|
||||
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);
|
||||
return 0;
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
|
||||
struct {
|
||||
struct rt_msghdr rt;
|
||||
struct sockaddr_in dst;
|
||||
struct sockaddr_in gate;
|
||||
struct sockaddr_in mask;
|
||||
} req;
|
||||
|
||||
int fd;
|
||||
struct rt_msghdr *rtm;
|
||||
|
||||
if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&req, 0x00, sizeof(req));
|
||||
|
||||
rtm = &req.rt;
|
||||
|
||||
rtm->rtm_msglen = sizeof(req);
|
||||
rtm->rtm_version = RTM_VERSION;
|
||||
if (delete) {
|
||||
rtm->rtm_type = RTM_DELETE;
|
||||
} else {
|
||||
rtm->rtm_type = RTM_ADD;
|
||||
}
|
||||
rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
|
||||
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
|
||||
rtm->rtm_pid = getpid();
|
||||
rtm->rtm_seq = 0044; /* TODO */
|
||||
|
||||
req.dst.sin_family = AF_INET;
|
||||
req.dst.sin_len = sizeof(req.dst);
|
||||
req.mask.sin_family = AF_INET;
|
||||
req.mask.sin_len = sizeof(req.mask);
|
||||
req.gate.sin_family = AF_INET;
|
||||
req.gate.sin_len = sizeof(req.gate);
|
||||
|
||||
req.dst.sin_addr.s_addr = dst->s_addr;
|
||||
req.mask.sin_addr.s_addr = mask->s_addr;
|
||||
req.gate.sin_addr.s_addr = gateway->s_addr;
|
||||
|
||||
if (write(fd, rtm, rtm->rtm_msglen) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int tun_addroute(struct tun_t *this,
|
||||
struct in_addr *dst,
|
||||
struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return tun_route(this, dst, gateway, mask, 0);
|
||||
}
|
||||
|
||||
int tun_delroute(struct tun_t *this,
|
||||
struct in_addr *dst,
|
||||
struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return tun_route(this, dst, gateway, mask, 1);
|
||||
}
|
||||
|
||||
int tun_new(struct tun_t **tun, const char *dev_name)
|
||||
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
|
||||
{
|
||||
|
||||
#if defined(__linux__)
|
||||
@@ -602,31 +178,53 @@ int tun_new(struct tun_t **tun, const char *dev_name)
|
||||
(*tun)->routes = 0;
|
||||
|
||||
#if defined(__linux__)
|
||||
/* Open the actual tun device */
|
||||
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
|
||||
goto err_free;
|
||||
if (!use_kernel) {
|
||||
/* Open the actual tun device */
|
||||
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* Set device flags. For some weird reason this is also the method
|
||||
used to obtain the network interface name */
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
if (dev_name)
|
||||
strcpy(ifr.ifr_name, dev_name);
|
||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
|
||||
if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
strncpy((*tun)->devname, dev_name, IFNAMSIZ);
|
||||
(*tun)->devname[IFNAMSIZ - 1] = 0;
|
||||
(*tun)->fd = -1;
|
||||
|
||||
if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
|
||||
LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set device flags. For some weird reason this is also the method
|
||||
used to obtain the network interface name */
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
if (dev_name)
|
||||
strcpy(ifr.ifr_name, dev_name);
|
||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
|
||||
if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
|
||||
(*tun)->devname[IFNAMSIZ - 1] = 0;
|
||||
|
||||
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
|
||||
return 0;
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
|
||||
if (use_kernel) {
|
||||
LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find suitable device */
|
||||
for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
|
||||
snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
|
||||
@@ -678,13 +276,17 @@ int tun_free(struct tun_t *tun)
|
||||
{
|
||||
|
||||
if (tun->routes) {
|
||||
tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
|
||||
netdev_delroute(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
|
||||
}
|
||||
|
||||
if (close(tun->fd)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
|
||||
if (tun->fd >= 0) {
|
||||
if (close(tun->fd)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
|
||||
}
|
||||
}
|
||||
|
||||
gtp_kernel_stop(tun->devname);
|
||||
|
||||
/* TODO: For solaris we need to unlink streams */
|
||||
|
||||
free(tun);
|
||||
@@ -727,7 +329,7 @@ int tun_runscript(struct tun_t *tun, char *script)
|
||||
char smask[TUN_ADDRSIZE];
|
||||
int rc;
|
||||
|
||||
strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
|
||||
strncpy(snet, inet_ntoa(tun->addr.v4), sizeof(snet));
|
||||
snet[sizeof(snet) - 1] = 0;
|
||||
strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
|
||||
smask[sizeof(smask) - 1] = 0;
|
||||
@@ -745,79 +347,6 @@ int tun_runscript(struct tun_t *tun, char *script)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
/*! Obtain the local address of a network device
|
||||
* \param[in] devname Target device owning the IP
|
||||
* \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
|
||||
* \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
|
||||
* \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
|
||||
* \returns The number of ips found following the criteria specified by flags, -1 on error.
|
||||
*
|
||||
* This function will fill prefix_list with up to prefix_size IPs following the
|
||||
* criteria specified by flags parameter. It returns the number of IPs matching
|
||||
* the criteria. As a result, the number returned can be bigger than
|
||||
* prefix_size. It can be used with prefix_size=0 to get an estimate of the size
|
||||
* needed for prefix_list.
|
||||
*/
|
||||
int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
|
||||
{
|
||||
static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct in46_addr netmask;
|
||||
size_t count = 0;
|
||||
bool is_ipv6_ll;
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(ifa->ifa_name, devname))
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
|
||||
struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
|
||||
struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
|
||||
|
||||
if (count < prefix_size) {
|
||||
netmask.len = sizeof(netmask4->sin_addr);
|
||||
netmask.v4 = netmask4->sin_addr;
|
||||
prefix_list[count].addr.len = sizeof(sin4->sin_addr);
|
||||
prefix_list[count].addr.v4 = sin4->sin_addr;
|
||||
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
|
||||
struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
|
||||
|
||||
is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
|
||||
if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
|
||||
continue;
|
||||
if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
|
||||
continue;
|
||||
|
||||
if (count < prefix_size) {
|
||||
netmask.len = sizeof(netmask6->sin6_addr);
|
||||
netmask.v6 = netmask6->sin6_addr;
|
||||
prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
|
||||
prefix_list[count].addr.v6 = sin6->sin6_addr;
|
||||
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*! Obtain the local address of the tun device.
|
||||
* \param[in] tun Target device owning the IP
|
||||
* \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
|
||||
|
||||
58
lib/tun.h
58
lib/tun.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* TUN interface functions.
|
||||
* Copyright (C) 2002, 2003 Mondru AB.
|
||||
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2017-2018 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
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef _TUN_H
|
||||
#define _TUN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "../lib/in46_addr.h"
|
||||
@@ -20,43 +21,9 @@
|
||||
#define PACKET_MAX 8196 /* Maximum packet size we receive */
|
||||
#define TUN_SCRIPTSIZE 256
|
||||
#define TUN_ADDRSIZE 128
|
||||
#define TUN_NLBUFSIZE 1024
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* ipv6 ip type flags for tun_ipv6_local_get() */
|
||||
enum {
|
||||
IP_TYPE_IPv4 = 1,
|
||||
IP_TYPE_IPv6_LINK = 2,
|
||||
IP_TYPE_IPv6_NONLINK = 4,
|
||||
};
|
||||
#define IP_TYPE_IPv6 (IP_TYPE_IPv6_LINK | IP_TYPE_IPv6_NONLINK)
|
||||
|
||||
|
||||
#ifndef HAVE_IPHDR
|
||||
struct iphdr
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned int ihl:4;
|
||||
unsigned int version:4;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned int version:4;
|
||||
unsigned int ihl:4;
|
||||
#else
|
||||
# error "Please fix <bits/endian.h>"
|
||||
#endif
|
||||
u_int8_t tos;
|
||||
u_int16_t tot_len;
|
||||
u_int16_t id;
|
||||
u_int16_t frag_off;
|
||||
u_int8_t ttl;
|
||||
u_int8_t protocol;
|
||||
u_int16_t check;
|
||||
u_int32_t saddr;
|
||||
u_int32_t daddr;
|
||||
/*The options start here. */
|
||||
};
|
||||
#endif /* !HAVE_IPHDR */
|
||||
#include "netdev.h"
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each tun instance
|
||||
@@ -64,8 +31,8 @@ struct iphdr
|
||||
|
||||
struct tun_t {
|
||||
int fd; /* File descriptor to tun interface */
|
||||
struct in_addr addr;
|
||||
struct in_addr dstaddr;
|
||||
struct in46_addr addr;
|
||||
struct in46_addr dstaddr;
|
||||
struct in_addr netmask;
|
||||
int addrs; /* Number of allocated IP addresses */
|
||||
int routes; /* One if we allocated an automatic route */
|
||||
@@ -75,19 +42,13 @@ struct tun_t {
|
||||
void *priv;
|
||||
};
|
||||
|
||||
extern int tun_new(struct tun_t **tun, const char *dev_name);
|
||||
extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u);
|
||||
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);
|
||||
|
||||
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
||||
|
||||
extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
|
||||
struct in46_addr *his_adr, size_t prefixlen);
|
||||
|
||||
int tun_addroute(struct tun_t *this, struct in_addr *dst,
|
||||
struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
|
||||
struct in46_addr *dstaddr, size_t prefixlen);
|
||||
|
||||
extern int tun_set_cb_ind(struct tun_t *this,
|
||||
int (*cb_ind) (struct tun_t * tun, void *pack,
|
||||
@@ -95,9 +56,6 @@ extern int tun_set_cb_ind(struct tun_t *this,
|
||||
|
||||
extern int tun_runscript(struct tun_t *tun, char *script);
|
||||
|
||||
int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
|
||||
size_t prefix_size, int flags);
|
||||
|
||||
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
|
||||
size_t prefix_size, int flags);
|
||||
|
||||
|
||||
@@ -5,5 +5,11 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
|
||||
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
sgsnemu_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS)
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||
sgsnemu_LDADD += $(LIBGTPNL_LIBS)
|
||||
endif
|
||||
|
||||
sgsnemu_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
|
||||
sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h
|
||||
|
||||
@@ -41,7 +41,7 @@ const char *gengetopt_args_info_help[] = {
|
||||
" --pidfile=STRING Filename of process id file\n (default=`./sgsnemu.pid')",
|
||||
" --statedir=STRING Directory of nonvolatile data (default=`./')",
|
||||
" --dns=STRING DNS Server to use",
|
||||
" -l, --listen=STRING Local interface",
|
||||
" -l, --listen=STRING Local host",
|
||||
" -r, --remote=STRING Remote host",
|
||||
" --contexts=INT Number of contexts (default=`1')",
|
||||
" --timelimit=INT Exit after timelimit seconds (default=`0')",
|
||||
@@ -65,12 +65,14 @@ const char *gengetopt_args_info_help[] = {
|
||||
" --charging=INT Charging characteristics (default=`0x0800')",
|
||||
" -u, --uid=STRING Login user ID (default=`mig')",
|
||||
" -p, --pwd=STRING Login password (default=`hemmelig')",
|
||||
"\n Mode: createif\n any option of this mode is related to tun interface, all payload going in and\n out via tunN interface",
|
||||
" --createif Create local network interface (default=off)",
|
||||
" -n, --net=STRING Network address for local interface",
|
||||
" --defaultroute Create default route (default=off)",
|
||||
" --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",
|
||||
"\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')",
|
||||
" --pingsize=INT Number of ping data bytes (default=`56')",
|
||||
@@ -168,6 +170,8 @@ void clear_given(struct gengetopt_args_info *args_info)
|
||||
args_info->pingquiet_given = 0;
|
||||
args_info->no_tx_gpdu_seq_given = 0;
|
||||
args_info->pdp_type_given = 0;
|
||||
args_info->createif_mode_counter = 0;
|
||||
args_info->pinghost_mode_counter = 0;
|
||||
}
|
||||
|
||||
static
|
||||
@@ -290,19 +294,19 @@ void init_args_info(struct gengetopt_args_info *args_info)
|
||||
args_info->charging_help = gengetopt_args_info_help[28];
|
||||
args_info->uid_help = gengetopt_args_info_help[29];
|
||||
args_info->pwd_help = gengetopt_args_info_help[30];
|
||||
args_info->createif_help = gengetopt_args_info_help[31];
|
||||
args_info->net_help = gengetopt_args_info_help[32];
|
||||
args_info->defaultroute_help = gengetopt_args_info_help[33];
|
||||
args_info->ipup_help = gengetopt_args_info_help[34];
|
||||
args_info->ipdown_help = gengetopt_args_info_help[35];
|
||||
args_info->tun_device_help = gengetopt_args_info_help[36];
|
||||
args_info->pinghost_help = gengetopt_args_info_help[37];
|
||||
args_info->pingrate_help = gengetopt_args_info_help[38];
|
||||
args_info->pingsize_help = gengetopt_args_info_help[39];
|
||||
args_info->pingcount_help = gengetopt_args_info_help[40];
|
||||
args_info->pingquiet_help = gengetopt_args_info_help[41];
|
||||
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[42];
|
||||
args_info->pdp_type_help = gengetopt_args_info_help[43];
|
||||
args_info->createif_help = gengetopt_args_info_help[32];
|
||||
args_info->net_help = gengetopt_args_info_help[33];
|
||||
args_info->defaultroute_help = gengetopt_args_info_help[34];
|
||||
args_info->ipup_help = gengetopt_args_info_help[35];
|
||||
args_info->ipdown_help = gengetopt_args_info_help[36];
|
||||
args_info->tun_device_help = gengetopt_args_info_help[37];
|
||||
args_info->pinghost_help = gengetopt_args_info_help[39];
|
||||
args_info->pingrate_help = gengetopt_args_info_help[40];
|
||||
args_info->pingsize_help = gengetopt_args_info_help[41];
|
||||
args_info->pingcount_help = gengetopt_args_info_help[42];
|
||||
args_info->pingquiet_help = gengetopt_args_info_help[43];
|
||||
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[44];
|
||||
args_info->pdp_type_help = gengetopt_args_info_help[45];
|
||||
|
||||
}
|
||||
|
||||
@@ -361,8 +365,7 @@ 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 *)
|
||||
struct cmdline_parser_params *params = (struct cmdline_parser_params *)
|
||||
malloc(sizeof(struct cmdline_parser_params));
|
||||
cmdline_parser_params_init(params);
|
||||
return params;
|
||||
@@ -853,6 +856,30 @@ int update_arg(void *field, char **orig_field,
|
||||
return 0; /* OK */
|
||||
}
|
||||
|
||||
static int check_modes(int given1[], const char *options1[],
|
||||
int given2[], const char *options2[])
|
||||
{
|
||||
int i = 0, j = 0, errors = 0;
|
||||
|
||||
while (given1[i] >= 0) {
|
||||
if (given1[i]) {
|
||||
while (given2[j] >= 0) {
|
||||
if (given2[j]) {
|
||||
++errors;
|
||||
fprintf(stderr,
|
||||
"%s: option %s conflicts with option %s\n",
|
||||
package_name, options1[i],
|
||||
options2[j]);
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
int
|
||||
cmdline_parser_internal(int argc, char **argv,
|
||||
struct gengetopt_args_info *args_info,
|
||||
@@ -976,7 +1003,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
goto failure;
|
||||
|
||||
break;
|
||||
case 'l': /* Local interface. */
|
||||
case 'l': /* Local host. */
|
||||
|
||||
if (update_arg((void *)&(args_info->listen_arg),
|
||||
&(args_info->listen_orig),
|
||||
@@ -1073,6 +1100,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
|
||||
break;
|
||||
case 'n': /* Network address for local interface. */
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg((void *)&(args_info->net_arg),
|
||||
&(args_info->net_orig),
|
||||
@@ -1391,6 +1419,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"createif") == 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->createif_flag), 0,
|
||||
@@ -1405,6 +1434,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"defaultroute") == 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->defaultroute_flag), 0,
|
||||
@@ -1419,6 +1449,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
/* Script to run after link-up. */
|
||||
else if (strcmp(long_options[option_index].name, "ipup")
|
||||
== 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg((void *)&(args_info->ipup_arg),
|
||||
&(args_info->ipup_orig),
|
||||
@@ -1434,6 +1465,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"ipdown") == 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg((void *)&(args_info->ipdown_arg),
|
||||
&(args_info->ipdown_orig),
|
||||
@@ -1449,6 +1481,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"tun-device") == 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->tun_device_arg),
|
||||
@@ -1465,6 +1498,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"pinghost") == 0) {
|
||||
args_info->pinghost_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->pinghost_arg),
|
||||
@@ -1481,6 +1515,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"pingrate") == 0) {
|
||||
args_info->pinghost_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->pingrate_arg),
|
||||
@@ -1496,6 +1531,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"pingsize") == 0) {
|
||||
args_info->pinghost_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->pingsize_arg),
|
||||
@@ -1512,6 +1548,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"pingcount") == 0) {
|
||||
args_info->pinghost_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->pingcount_arg),
|
||||
@@ -1527,6 +1564,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"pingquiet") == 0) {
|
||||
args_info->pinghost_mode_counter += 1;
|
||||
|
||||
if (update_arg
|
||||
((void *)&(args_info->pingquiet_flag), 0,
|
||||
@@ -1566,6 +1604,31 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
} /* switch */
|
||||
} /* while */
|
||||
|
||||
if (args_info->createif_mode_counter
|
||||
&& args_info->pinghost_mode_counter) {
|
||||
int createif_given[] =
|
||||
{ args_info->createif_given, args_info->net_given,
|
||||
args_info->defaultroute_given, args_info->ipup_given,
|
||||
args_info->ipdown_given, args_info->tun_device_given, -1
|
||||
};
|
||||
const char *createif_desc[] =
|
||||
{ "--createif", "--net", "--defaultroute", "--ipup",
|
||||
"--ipdown", "--tun-device", 0
|
||||
};
|
||||
int pinghost_given[] =
|
||||
{ args_info->pinghost_given, args_info->pingrate_given,
|
||||
args_info->pingsize_given, args_info->pingcount_given,
|
||||
args_info->pingquiet_given, -1
|
||||
};
|
||||
const char *pinghost_desc[] =
|
||||
{ "--pinghost", "--pingrate", "--pingsize", "--pingcount",
|
||||
"--pingquiet", 0
|
||||
};
|
||||
error_occurred +=
|
||||
check_modes(createif_given, createif_desc, pinghost_given,
|
||||
pinghost_desc);
|
||||
}
|
||||
|
||||
if (check_required) {
|
||||
error_occurred +=
|
||||
cmdline_parser_required2(args_info, argv[0],
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
package "sgsnemu"
|
||||
|
||||
defmode "createif" modedesc="any option of this mode is related to tun interface, \
|
||||
all payload going in and out via tunN interface"
|
||||
defmode "pinghost" modedesc="generate ICMP payload inside G-PDU without setting up tun interface"
|
||||
|
||||
option "debug" d "Run in debug mode" flag off
|
||||
|
||||
option "conf" c "Read configuration file" string no
|
||||
@@ -22,7 +26,7 @@ option "pidfile" - "Filename of process id file" string default="./sgsn
|
||||
option "statedir" - "Directory of nonvolatile data" string default="./" no
|
||||
|
||||
option "dns" - "DNS Server to use" string no
|
||||
option "listen" l "Local interface" string no
|
||||
option "listen" l "Local host" string no
|
||||
option "remote" r "Remote host" string no
|
||||
|
||||
option "contexts" - "Number of contexts" int default="1" no
|
||||
@@ -49,18 +53,18 @@ option "charging" - "Charging characteristics" int default="0x0800
|
||||
option "uid" u "Login user ID" string default="mig" no
|
||||
option "pwd" p "Login password" string default="hemmelig" no
|
||||
|
||||
option "createif" - "Create local network interface" flag off
|
||||
option "net" n "Network address for local interface" string dependon="createif" no
|
||||
option "defaultroute" - "Create default route" flag dependon="createif" off
|
||||
option "ipup" - "Script to run after link-up" string dependon="createif" no
|
||||
option "ipdown" - "Script to run after link-down" string dependon="createif" no
|
||||
option "tun-device" - "Name of the local network interface" string dependon="createif" no
|
||||
modeoption "createif" - "Create local network interface" flag off mode="createif"
|
||||
modeoption "net" n "Network address for local interface" string dependon="createif" no mode="createif"
|
||||
modeoption "defaultroute" - "Create default route" flag dependon="createif" off mode="createif"
|
||||
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"
|
||||
|
||||
option "pinghost" - "Ping remote host" string no
|
||||
option "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no
|
||||
option "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no
|
||||
option "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no
|
||||
option "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" off
|
||||
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"
|
||||
modeoption "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no mode="pinghost"
|
||||
modeoption "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no mode="pinghost"
|
||||
modeoption "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" off mode="pinghost"
|
||||
|
||||
option "no-tx-gpdu-seq" - "Don't transmit G-PDU sequence nums" flag off
|
||||
option "pdp-type" t "PDP Type" string default="v4" no typestr="(v4|v6)"
|
||||
|
||||
@@ -69,11 +69,11 @@ extern "C" {
|
||||
const char *dns_help;
|
||||
/**< @brief DNS Server to use help description. */
|
||||
char *listen_arg;
|
||||
/**< @brief Local interface. */
|
||||
/**< @brief Local host. */
|
||||
char *listen_orig;
|
||||
/**< @brief Local interface original value given at command line. */
|
||||
/**< @brief Local host original value given at command line. */
|
||||
const char *listen_help;
|
||||
/**< @brief Local interface help description. */
|
||||
/**< @brief Local host help description. */
|
||||
char *remote_arg;
|
||||
/**< @brief Remote host. */
|
||||
char *remote_orig;
|
||||
@@ -370,6 +370,10 @@ extern "C" {
|
||||
unsigned int pdp_type_given;
|
||||
/**< @brief Whether pdp-type was given. */
|
||||
|
||||
int createif_mode_counter;
|
||||
/**< @brief Counter for mode createif */
|
||||
int pinghost_mode_counter;
|
||||
/**< @brief Counter for mode pinghost */
|
||||
};
|
||||
|
||||
/** @brief The additional parameters to pass to parser functions */
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017 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
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -20,6 +20,7 @@
|
||||
#endif
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
@@ -79,13 +80,14 @@ struct gsn_t *gsn = NULL; /* GSN instance */
|
||||
struct tun_t *tun = NULL; /* TUN instance */
|
||||
int maxfd = 0; /* For select() */
|
||||
int echoversion = 1; /* First try this version */
|
||||
void *tall_sgsnemu_ctx; /* root talloc ctx */
|
||||
|
||||
/* Struct with local versions of gengetopt options */
|
||||
struct {
|
||||
int debug; /* Print debug messages */
|
||||
int createif; /* Create local network interface */
|
||||
char *tun_dev_name;
|
||||
struct in_addr netaddr, destaddr, net; /* Network interface */
|
||||
struct in46_addr netaddr, destaddr, net; /* Network interface */
|
||||
size_t prefixlen;
|
||||
char *ipup, *ipdown; /* Filename of scripts */
|
||||
int defaultroute; /* Set up default route */
|
||||
@@ -291,7 +293,7 @@ static int process_options(int argc, char **argv)
|
||||
printf("timelimit: %d\n", args_info.timelimit_arg);
|
||||
printf("createif: %d\n", args_info.createif_flag);
|
||||
if (args_info.tun_device_arg)
|
||||
printf("tun-device: %d\n", args_info.tun_device_arg);
|
||||
printf("tun-device: %s\n", args_info.tun_device_arg);
|
||||
if (args_info.ipup_arg)
|
||||
printf("ipup: %s\n", args_info.ipup_arg);
|
||||
if (args_info.ipdown_arg)
|
||||
@@ -372,10 +374,10 @@ static int process_options(int argc, char **argv)
|
||||
|
||||
/* foreground */
|
||||
/* If fg flag not given run as a daemon */
|
||||
/* Do not allow sgsnemu to run as deamon
|
||||
/* Do not allow sgsnemu to run as deamon
|
||||
if (!args_info.fg_flag)
|
||||
{
|
||||
closelog();
|
||||
closelog();
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
freopen("/dev/null", "r", stdin);
|
||||
@@ -873,23 +875,21 @@ static int process_options(int argc, char **argv)
|
||||
/* net */
|
||||
/* Store net as in_addr net and mask */
|
||||
if (args_info.net_arg) {
|
||||
struct in46_addr in46;
|
||||
if (ippool_aton
|
||||
(&in46, &options.prefixlen, args_info.net_arg, 0)) {
|
||||
(&options.net, &options.prefixlen, args_info.net_arg, 0)) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"Invalid network address: %s!",
|
||||
args_info.net_arg);
|
||||
exit(1);
|
||||
}
|
||||
options.net.s_addr = in46.v4.s_addr;
|
||||
options.netaddr.s_addr = options.net.s_addr;
|
||||
options.destaddr.s_addr = options.net.s_addr;
|
||||
options.netaddr = options.net;
|
||||
options.destaddr = options.net;
|
||||
|
||||
} else {
|
||||
options.net.s_addr = 0;
|
||||
memset(&options.net, 0, sizeof(options.net));
|
||||
options.prefixlen = 0;
|
||||
options.netaddr.s_addr = 0;
|
||||
options.destaddr.s_addr = 0;
|
||||
memset(&options.netaddr, 0, sizeof(options.netaddr));
|
||||
memset(&options.destaddr, 0, sizeof(options.destaddr));
|
||||
}
|
||||
|
||||
/* ipup */
|
||||
@@ -1314,8 +1314,8 @@ static int delete_context(struct pdp_t *pdp)
|
||||
if (tun && options.ipdown)
|
||||
tun_runscript(tun, options.ipdown);
|
||||
|
||||
ipdel((struct iphash_t *)pdp->peer);
|
||||
memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */
|
||||
ipdel((struct iphash_t *)pdp->peer[0]);
|
||||
memset(pdp->peer[0], 0, sizeof(struct iphash_t)); /* To be sure */
|
||||
|
||||
if (1 == options.contexts)
|
||||
state = 5; /* Disconnected */
|
||||
@@ -1402,7 +1402,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
return EOF; /* Not what we expected */
|
||||
}
|
||||
|
||||
if (in46a_from_eua(&pdp->eua, &addr)) {
|
||||
if (in46a_from_eua(&pdp->eua, &addr) < 1) {
|
||||
printf
|
||||
("Received create PDP context response. Cause value: %d\n",
|
||||
cause);
|
||||
@@ -1427,17 +1427,16 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((options.createif) && (!options.net.s_addr)) {
|
||||
if ((options.createif) && (!options.net.len)) {
|
||||
size_t prefixlen = 32;
|
||||
if (addr.len == 16)
|
||||
prefixlen = 64;
|
||||
/* printf("Setting up interface and routing\n"); */
|
||||
/* FIXME: use tun_addattr() not tun_setaddr() */
|
||||
tun_setaddr(tun, &addr, &addr, prefixlen);
|
||||
tun_addaddr(tun, &addr, &addr, prefixlen);
|
||||
if (options.defaultroute) {
|
||||
struct in_addr rm;
|
||||
rm.s_addr = 0;
|
||||
tun_addroute(tun, &rm, &addr.v4, &rm);
|
||||
netdev_addroute(&rm, &addr.v4, &rm);
|
||||
}
|
||||
if (options.ipup)
|
||||
tun_runscript(tun, options.ipup);
|
||||
@@ -1464,7 +1463,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
free(forwarding);
|
||||
}
|
||||
|
||||
ipset((struct iphash_t *)pdp->peer, &addr);
|
||||
ipset(iph, &addr);
|
||||
|
||||
state = 2; /* Connected */
|
||||
|
||||
@@ -1542,7 +1541,9 @@ int main(int argc, char **argv)
|
||||
signal(SIGHUP, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
osmo_init_logging(&log_info);
|
||||
tall_sgsnemu_ctx = talloc_named_const(NULL, 0, "sgsnemu");
|
||||
msgb_talloc_ctx_init(tall_sgsnemu_ctx, 0);
|
||||
osmo_init_logging2(tall_sgsnemu_ctx, &log_info);
|
||||
|
||||
/* Process options given in configuration file and command line */
|
||||
if (process_options(argc, argv))
|
||||
@@ -1570,7 +1571,7 @@ int main(int argc, char **argv)
|
||||
if (options.createif) {
|
||||
printf("Setting up interface\n");
|
||||
/* Create a tunnel interface */
|
||||
if (tun_new((struct tun_t **)&tun, options.tun_dev_name)) {
|
||||
if (tun_new((struct tun_t **)&tun, options.tun_dev_name, false, -1, -1)) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"Failed to create tun");
|
||||
exit(1);
|
||||
@@ -1580,15 +1581,13 @@ int main(int argc, char **argv)
|
||||
maxfd = tun->fd;
|
||||
}
|
||||
|
||||
if ((options.createif) && (options.net.s_addr)) {
|
||||
struct in_addr mask;
|
||||
mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
|
||||
if ((options.createif) && (options.net.len)) {
|
||||
/* printf("Setting up interface and routing\n"); */
|
||||
tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
|
||||
tun_addaddr(tun, &options.netaddr, &options.destaddr, options.prefixlen);
|
||||
if (options.defaultroute) {
|
||||
struct in_addr rm;
|
||||
rm.s_addr = 0;
|
||||
tun_addroute(tun, &rm, &options.destaddr, &rm);
|
||||
netdev_addroute(&rm, &options.destaddr.v4, &rm);
|
||||
}
|
||||
if (options.ipup)
|
||||
tun_runscript(tun, options.ipup);
|
||||
@@ -1617,7 +1616,7 @@ int main(int argc, char **argv)
|
||||
/* Otherwise it is deallocated by gtplib */
|
||||
pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
|
||||
|
||||
pdp->peer = &iparr[n];
|
||||
pdp->peer[0] = &iparr[n]; /* FIXME: support v4v6, have 2 peers */
|
||||
pdp->ipif = tun; /* TODO */
|
||||
iparr[n].pdp = pdp;
|
||||
|
||||
@@ -1700,7 +1699,7 @@ int main(int argc, char **argv)
|
||||
pdp->hisaddr0 = options.remote;
|
||||
pdp->hisaddr1 = options.remote;
|
||||
|
||||
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
|
||||
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
|
||||
512 = Flat rate, 256 = Hot billing */
|
||||
|
||||
pdp->tx_gpdu_seq = options.tx_gpdu_seq;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@@ -108,7 +109,9 @@ static void test_gtpie_tv8()
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&log_info);
|
||||
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_filename(osmo_stderr_target, 0);
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||
|
||||
EXTRA_DIST = ippool_test.ok ippool_test.err \
|
||||
in46a_test.ok
|
||||
EXTRA_DIST = ippool_test.ok \
|
||||
ippool_test.err \
|
||||
ippool_v6_test.ok \
|
||||
ippool_v6_test.err \
|
||||
in46a_test.ok \
|
||||
in46a_v6_test.ok
|
||||
|
||||
noinst_PROGRAMS = ippool_test in46a_test
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#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/in46_addr.h"
|
||||
@@ -20,41 +21,24 @@ static const struct in46_addr g_ia4 = {
|
||||
.v4.s_addr = 0x0d0c0b0a,
|
||||
};
|
||||
|
||||
static const struct in46_addr g_ia6 = {
|
||||
.len = 16,
|
||||
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
|
||||
};
|
||||
|
||||
static void test_in46a_to_af(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
|
||||
printf("Testing in46a_to_af()\n");
|
||||
printf("Testing in46a_to_af() with IPv4 addresses\n");
|
||||
|
||||
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
|
||||
OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
|
||||
|
||||
ia.len = 8;
|
||||
OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
|
||||
}
|
||||
|
||||
static void test_in46a_to_sas(void)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
|
||||
|
||||
printf("Testing in46a_to_sas()\n");
|
||||
printf("Testing in46a_to_sas() with IPv4 addresses\n");
|
||||
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia4) == 0);
|
||||
OSMO_ASSERT(sin->sin_family == AF_INET);
|
||||
OSMO_ASSERT(sin->sin_addr.s_addr == g_ia4.v4.s_addr);
|
||||
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
|
||||
OSMO_ASSERT(sin6->sin6_family == AF_INET6);
|
||||
OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
|
||||
}
|
||||
|
||||
static void test_in46a_ntop(void)
|
||||
@@ -63,7 +47,7 @@ static void test_in46a_ntop(void)
|
||||
char buf[256];
|
||||
const char *res;
|
||||
|
||||
printf("Testing in46a_ntop()\n");
|
||||
printf("Testing in46a_ntop() with IPv4 addresses\n");
|
||||
|
||||
res = in46a_ntop(NULL, buf, sizeof(buf));
|
||||
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
|
||||
@@ -79,10 +63,6 @@ static void test_in46a_ntop(void)
|
||||
res = in46a_ntop(&ia, buf, sizeof(buf));
|
||||
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
|
||||
printf("res = %s\n", res);
|
||||
|
||||
res = in46a_ntop(&g_ia6, buf, sizeof(buf));
|
||||
OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
|
||||
printf("res = %s\n", res);
|
||||
}
|
||||
|
||||
static void test_in46p_ntoa(void)
|
||||
@@ -101,21 +81,14 @@ static void test_in46a_equal(void)
|
||||
{
|
||||
struct in46_addr b;
|
||||
|
||||
printf("Testing in46a_equal()\n");
|
||||
printf("Testing in46a_equal() with IPv4 addresses\n");
|
||||
|
||||
memset(&b, 0xff, sizeof(b));
|
||||
b.len = g_ia4.len;
|
||||
b.v4.s_addr = g_ia4.v4.s_addr;
|
||||
OSMO_ASSERT(in46a_equal(&g_ia4, &b));
|
||||
|
||||
memset(&b, 0xff, sizeof(b));
|
||||
b.len = g_ia6.len;
|
||||
b.v6 = g_ia6.v6;
|
||||
OSMO_ASSERT(in46a_equal(&g_ia6, &b));
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
|
||||
size_t prefixlen)
|
||||
{
|
||||
@@ -134,7 +107,7 @@ static void test_in46a_within_mask(void)
|
||||
{
|
||||
struct in46_addr addr, mask;
|
||||
|
||||
printf("Testing in46a_within_mask()\n");
|
||||
printf("Testing in46a_within_mask() with IPv4 addresses\n");
|
||||
|
||||
addr = g_ia4;
|
||||
mask = g_ia4;
|
||||
@@ -155,13 +128,9 @@ static void test_in46a_within_mask(void)
|
||||
|
||||
static void test_in46a_to_eua(void)
|
||||
{
|
||||
const struct in46_addr ia_v6_8 = {
|
||||
.len = 8,
|
||||
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
|
||||
};
|
||||
struct ul66_t eua;
|
||||
|
||||
printf("testing in46a_to_eua()\n");
|
||||
printf("testing in46a_to_eua() with IPv4 addresses\n");
|
||||
|
||||
#if 0 /* triggers assert in current implementation */
|
||||
const struct in46_addr ia_invalid = { .len = 3, };
|
||||
@@ -169,22 +138,10 @@ static void test_in46a_to_eua(void)
|
||||
#endif
|
||||
|
||||
/* IPv4 address */
|
||||
OSMO_ASSERT(in46a_to_eua(&g_ia4, &eua) == 0);
|
||||
OSMO_ASSERT(in46a_to_eua(&g_ia4, 1, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
|
||||
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
|
||||
|
||||
/* IPv6 address */
|
||||
OSMO_ASSERT(in46a_to_eua(&g_ia6, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||
OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
|
||||
|
||||
/* IPv6 address with prefix / length 8 */
|
||||
OSMO_ASSERT(in46a_to_eua(&ia_v6_8, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||
OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
|
||||
}
|
||||
|
||||
static void test_in46a_from_eua(void)
|
||||
@@ -193,16 +150,12 @@ static void test_in46a_from_eua(void)
|
||||
struct ul66_t eua;
|
||||
const uint8_t v4_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4 };
|
||||
const uint8_t v4_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4, 1,2,3,4 };
|
||||
const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
|
||||
const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
|
||||
1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
|
||||
|
||||
memset(&eua, 0, sizeof(eua));
|
||||
|
||||
printf("Testing in46a_from_eua()\n");
|
||||
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
||||
|
||||
/* default: v4 unspec */
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
|
||||
@@ -221,30 +174,16 @@ static void test_in46a_from_eua(void)
|
||||
/* unspecified V4 */
|
||||
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
||||
eua.l = sizeof(v4_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
|
||||
/* specified V4 */
|
||||
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
||||
eua.l = sizeof(v4_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
||||
|
||||
/* unspecified V6 */
|
||||
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
|
||||
eua.l = sizeof(v6_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
|
||||
|
||||
/* specified V6 */
|
||||
memcpy(eua.v, v6_spec, sizeof(v6_spec));
|
||||
eua.l = sizeof(v6_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
|
||||
}
|
||||
|
||||
static void test_in46a_netmasklen(void)
|
||||
@@ -274,7 +213,188 @@ static void test_in46a_netmasklen(void)
|
||||
netmask.v4.s_addr = 0x00000000;
|
||||
len = in46a_netmasklen(&netmask);
|
||||
OSMO_ASSERT(len == 0);
|
||||
}
|
||||
|
||||
/* IPv6 specific tests */
|
||||
|
||||
static const struct in46_addr g_ia6 = {
|
||||
.len = 16,
|
||||
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
|
||||
};
|
||||
|
||||
static void test_in46a_to_af_v6(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
|
||||
printf("Testing in46a_to_af() with IPv6 addresses\n");
|
||||
|
||||
OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
|
||||
|
||||
ia.len = 8;
|
||||
OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
|
||||
}
|
||||
|
||||
static void test_in46a_to_sas_v6(void)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
|
||||
|
||||
printf("Testing in46a_to_sas() with IPv6 addresses\n");
|
||||
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
|
||||
OSMO_ASSERT(sin6->sin6_family == AF_INET6);
|
||||
OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
|
||||
}
|
||||
|
||||
static void test_in46a_ntop_v6(void)
|
||||
{
|
||||
char buf[256];
|
||||
const char *res;
|
||||
|
||||
printf("Testing in46a_ntop() with IPv6 addresses\n");
|
||||
|
||||
res = in46a_ntop(&g_ia6, buf, sizeof(buf));
|
||||
OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
|
||||
printf("res = %s\n", res);
|
||||
}
|
||||
|
||||
static void test_in46a_equal_v6(void)
|
||||
{
|
||||
struct in46_addr b;
|
||||
|
||||
printf("Testing in46a_equal() with IPv6 addresses\n");
|
||||
|
||||
memset(&b, 0xff, sizeof(b));
|
||||
b.len = g_ia6.len;
|
||||
b.v6 = g_ia6.v6;
|
||||
OSMO_ASSERT(in46a_equal(&g_ia6, &b));
|
||||
}
|
||||
|
||||
static void test_in46a_to_eua_v6(void)
|
||||
{
|
||||
const struct in46_addr ia_v6_8 = {
|
||||
.len = 8,
|
||||
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
|
||||
};
|
||||
struct ul66_t eua;
|
||||
|
||||
printf("Testing in46a_to_eua() with IPv6 addresses\n");
|
||||
|
||||
/* IPv6 address */
|
||||
OSMO_ASSERT(in46a_to_eua(&g_ia6, 1, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||
OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
|
||||
|
||||
/* IPv6 address with prefix / length 8 */
|
||||
OSMO_ASSERT(in46a_to_eua(&ia_v6_8, 1, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||
OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
|
||||
}
|
||||
|
||||
static void test_in46a_to_eua_v4v6() {
|
||||
const struct in46_addr ia_v4v6[2] = {
|
||||
{
|
||||
.len = 16,
|
||||
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
|
||||
},
|
||||
{
|
||||
.len = 4,
|
||||
.v4.s_addr = 0x0d0c0b0a,
|
||||
}
|
||||
};
|
||||
struct ul66_t eua;
|
||||
printf("Testing in46a_to_eua() with IPv4v6 addresses\n");
|
||||
|
||||
/* IPv4 address */
|
||||
OSMO_ASSERT(in46a_to_eua(ia_v4v6, 2, &eua) == 0);
|
||||
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
|
||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4v6);
|
||||
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
|
||||
OSMO_ASSERT(!memcmp(&eua.v[6], &g_ia6.v6, 16));
|
||||
}
|
||||
|
||||
static void test_in46a_from_eua_v6(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
struct ul66_t eua;
|
||||
const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
|
||||
const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
|
||||
1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
|
||||
|
||||
memset(&eua, 0, sizeof(eua));
|
||||
|
||||
printf("Testing in46a_from_eua() with IPv6 addresses\n");
|
||||
|
||||
/* unspecified V6 */
|
||||
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
|
||||
eua.l = sizeof(v6_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
|
||||
|
||||
/* specified V6 */
|
||||
memcpy(eua.v, v6_spec, sizeof(v6_spec));
|
||||
eua.l = sizeof(v6_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
|
||||
}
|
||||
|
||||
static void test_in46a_from_eua_v4v6(void) {
|
||||
struct in46_addr ia[2];
|
||||
struct ul66_t eua;
|
||||
const uint8_t v4_unspec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6 };
|
||||
const uint8_t v4_spec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4 };
|
||||
const uint8_t v4_unspec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
|
||||
const uint8_t v4_spec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
|
||||
|
||||
memset(&eua, 0, sizeof(eua));
|
||||
|
||||
printf("Testing in46a_from_eua() with IPv4v6 addresses\n");
|
||||
|
||||
/* unspecified V4 & V6 */
|
||||
memcpy(eua.v, v4_unspec_v6_unspec, sizeof(v4_unspec_v6_unspec));
|
||||
eua.l = sizeof(v4_unspec_v6_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[1].len == 16);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
|
||||
|
||||
/* specified V4, unspecified V6 */
|
||||
memcpy(eua.v, v4_spec_v6_unspec, sizeof(v4_spec_v6_unspec));
|
||||
eua.l = sizeof(v4_spec_v6_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[1].len == 16);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
|
||||
|
||||
/* unspecified V4, specified V6 */
|
||||
memcpy(eua.v, v4_unspec_v6_spec, sizeof(v4_unspec_v6_spec));
|
||||
eua.l = sizeof(v4_unspec_v6_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[1].len == 16);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
OSMO_ASSERT(!memcmp(&ia[1].v6, v4_unspec_v6_spec+2, ia[1].len));
|
||||
|
||||
/* specified V4, specified V6 */
|
||||
memcpy(eua.v, v4_spec_v6_spec, sizeof(v4_spec_v6_spec));
|
||||
eua.l = sizeof(v4_spec_v6_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[1].len == 16);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
|
||||
OSMO_ASSERT(!memcmp(&ia[1].v6, v4_spec_v6_spec+6, ia[1].len));
|
||||
}
|
||||
|
||||
static void test_in46a_netmasklen_v6(void)
|
||||
{
|
||||
unsigned int len;
|
||||
printf("Testing in46a_netmasklen() with IPv6 addresses\n");
|
||||
const struct in46_addr netmaskA = {
|
||||
.len = 16,
|
||||
@@ -307,20 +427,34 @@ static void test_in46a_netmasklen(void)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&log_info);
|
||||
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_filename(osmo_stderr_target, 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
test_in46a_to_af();
|
||||
test_in46a_to_sas();
|
||||
test_in46a_ntop();
|
||||
test_in46p_ntoa();
|
||||
test_in46a_equal();
|
||||
test_in46a_within_mask();
|
||||
test_in46a_to_eua();
|
||||
test_in46a_from_eua();
|
||||
test_in46a_netmasklen();
|
||||
if (argc < 2 || strcmp(argv[1], "-v6")) {
|
||||
test_in46a_to_af();
|
||||
test_in46a_to_sas();
|
||||
test_in46a_ntop();
|
||||
test_in46p_ntoa();
|
||||
test_in46a_equal();
|
||||
test_in46a_within_mask();
|
||||
test_in46a_to_eua();
|
||||
test_in46a_from_eua();
|
||||
test_in46a_netmasklen();
|
||||
} else {
|
||||
test_in46a_to_af_v6();
|
||||
test_in46a_to_sas_v6();
|
||||
test_in46a_ntop_v6();
|
||||
test_in46a_equal_v6();
|
||||
test_in46a_to_eua_v6();
|
||||
test_in46a_from_eua_v6();
|
||||
test_in46a_to_eua_v4v6();
|
||||
test_in46a_from_eua_v4v6();
|
||||
test_in46a_netmasklen_v6();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
Testing in46a_to_af()
|
||||
Testing in46a_to_sas()
|
||||
Testing in46a_ntop()
|
||||
Testing in46a_to_af() with IPv4 addresses
|
||||
Testing in46a_to_sas() with IPv4 addresses
|
||||
Testing in46a_ntop() with IPv4 addresses
|
||||
res = UNDEFINED
|
||||
res = UNDEFINED
|
||||
res = 1.2.3.4
|
||||
res = 102:304:506:708:90a:b0c:d0e:f10
|
||||
in46p_ntoa() returns 16.32.48.0/24
|
||||
Testing in46a_equal()
|
||||
Testing in46a_within_mask()
|
||||
Testing in46a_equal() with IPv4 addresses
|
||||
Testing in46a_within_mask() with IPv4 addresses
|
||||
in46a_within_mask(10.11.12.13, 10.11.12.13, 32) = 1
|
||||
in46a_within_mask(10.11.12.13, 10.11.12.12, 30) = 1
|
||||
in46a_within_mask(10.11.12.13, 10.8.0.0, 13) = 1
|
||||
in46a_within_mask(10.11.12.14, 10.11.12.13, 32) = 0
|
||||
in46a_within_mask(10.11.12.14, 10.11.12.12, 30) = 1
|
||||
testing in46a_to_eua()
|
||||
Testing in46a_from_eua()
|
||||
testing in46a_to_eua() with IPv4 addresses
|
||||
Testing in46a_from_eua() with IPv4 addresses
|
||||
Testing in46a_netmasklen() with IPv4 addresses
|
||||
Testing in46a_netmasklen() with IPv6 addresses
|
||||
|
||||
10
tests/lib/in46a_v6_test.ok
Normal file
10
tests/lib/in46a_v6_test.ok
Normal file
@@ -0,0 +1,10 @@
|
||||
Testing in46a_to_af() with IPv6 addresses
|
||||
Testing in46a_to_sas() with IPv6 addresses
|
||||
Testing in46a_ntop() with IPv6 addresses
|
||||
res = 102:304:506:708:90a:b0c:d0e:f10
|
||||
Testing in46a_equal() with IPv6 addresses
|
||||
Testing in46a_to_eua() with IPv6 addresses
|
||||
Testing in46a_from_eua() with IPv6 addresses
|
||||
Testing in46a_to_eua() with IPv4v6 addresses
|
||||
Testing in46a_from_eua() with IPv4v6 addresses
|
||||
Testing in46a_netmasklen() with IPv6 addresses
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include "../../lib/in46_addr.h"
|
||||
#include "../../lib/ippool.h"
|
||||
@@ -113,22 +114,31 @@ static void test_pool_sizes(void)
|
||||
/* 65534 addresses [0.1..255.254] */
|
||||
test_pool_size("192.168.0.0/16", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 65534);
|
||||
|
||||
/* 256 prefixes of /64 each */
|
||||
test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
|
||||
|
||||
/* 253 addresses [1..254] & exclude 192.168.23.1/24 */
|
||||
char *blacklist[] = {"176.16.222.10/24", "192.168.23.1/24", "192.168.38.2/24"};
|
||||
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, blacklist, 3, 253);
|
||||
}
|
||||
|
||||
static void test_pool_sizes_v6(void)
|
||||
{
|
||||
/* 256 prefixes of /64 each */
|
||||
test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&log_info);
|
||||
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_filename(osmo_stderr_target, 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
test_pool_sizes();
|
||||
if (argc < 2 || strcmp(argv[1], "-v6")) {
|
||||
test_pool_sizes();
|
||||
} else {
|
||||
test_pool_sizes_v6();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,5 +8,3 @@ No more IP addresses available
|
||||
No more IP addresses available
|
||||
No more IP addresses available
|
||||
No more IP addresses available
|
||||
No more IP addresses available
|
||||
No more IP addresses available
|
||||
|
||||
@@ -66301,263 +66301,6 @@ allocated address 192.168.255.251
|
||||
allocated address 192.168.255.252
|
||||
allocated address 192.168.255.253
|
||||
allocated address 192.168.255.254
|
||||
testing pool for prefix 2001:DB8::/56, flags=0x0, blacklist_size=0, expected_size=256
|
||||
allocated address 2001:db8::
|
||||
allocated address 2001:db8:0:1::
|
||||
allocated address 2001:db8:0:2::
|
||||
allocated address 2001:db8:0:3::
|
||||
allocated address 2001:db8:0:4::
|
||||
allocated address 2001:db8:0:5::
|
||||
allocated address 2001:db8:0:6::
|
||||
allocated address 2001:db8:0:7::
|
||||
allocated address 2001:db8:0:8::
|
||||
allocated address 2001:db8:0:9::
|
||||
allocated address 2001:db8:0:a::
|
||||
allocated address 2001:db8:0:b::
|
||||
allocated address 2001:db8:0:c::
|
||||
allocated address 2001:db8:0:d::
|
||||
allocated address 2001:db8:0:e::
|
||||
allocated address 2001:db8:0:f::
|
||||
allocated address 2001:db8:0:10::
|
||||
allocated address 2001:db8:0:11::
|
||||
allocated address 2001:db8:0:12::
|
||||
allocated address 2001:db8:0:13::
|
||||
allocated address 2001:db8:0:14::
|
||||
allocated address 2001:db8:0:15::
|
||||
allocated address 2001:db8:0:16::
|
||||
allocated address 2001:db8:0:17::
|
||||
allocated address 2001:db8:0:18::
|
||||
allocated address 2001:db8:0:19::
|
||||
allocated address 2001:db8:0:1a::
|
||||
allocated address 2001:db8:0:1b::
|
||||
allocated address 2001:db8:0:1c::
|
||||
allocated address 2001:db8:0:1d::
|
||||
allocated address 2001:db8:0:1e::
|
||||
allocated address 2001:db8:0:1f::
|
||||
allocated address 2001:db8:0:20::
|
||||
allocated address 2001:db8:0:21::
|
||||
allocated address 2001:db8:0:22::
|
||||
allocated address 2001:db8:0:23::
|
||||
allocated address 2001:db8:0:24::
|
||||
allocated address 2001:db8:0:25::
|
||||
allocated address 2001:db8:0:26::
|
||||
allocated address 2001:db8:0:27::
|
||||
allocated address 2001:db8:0:28::
|
||||
allocated address 2001:db8:0:29::
|
||||
allocated address 2001:db8:0:2a::
|
||||
allocated address 2001:db8:0:2b::
|
||||
allocated address 2001:db8:0:2c::
|
||||
allocated address 2001:db8:0:2d::
|
||||
allocated address 2001:db8:0:2e::
|
||||
allocated address 2001:db8:0:2f::
|
||||
allocated address 2001:db8:0:30::
|
||||
allocated address 2001:db8:0:31::
|
||||
allocated address 2001:db8:0:32::
|
||||
allocated address 2001:db8:0:33::
|
||||
allocated address 2001:db8:0:34::
|
||||
allocated address 2001:db8:0:35::
|
||||
allocated address 2001:db8:0:36::
|
||||
allocated address 2001:db8:0:37::
|
||||
allocated address 2001:db8:0:38::
|
||||
allocated address 2001:db8:0:39::
|
||||
allocated address 2001:db8:0:3a::
|
||||
allocated address 2001:db8:0:3b::
|
||||
allocated address 2001:db8:0:3c::
|
||||
allocated address 2001:db8:0:3d::
|
||||
allocated address 2001:db8:0:3e::
|
||||
allocated address 2001:db8:0:3f::
|
||||
allocated address 2001:db8:0:40::
|
||||
allocated address 2001:db8:0:41::
|
||||
allocated address 2001:db8:0:42::
|
||||
allocated address 2001:db8:0:43::
|
||||
allocated address 2001:db8:0:44::
|
||||
allocated address 2001:db8:0:45::
|
||||
allocated address 2001:db8:0:46::
|
||||
allocated address 2001:db8:0:47::
|
||||
allocated address 2001:db8:0:48::
|
||||
allocated address 2001:db8:0:49::
|
||||
allocated address 2001:db8:0:4a::
|
||||
allocated address 2001:db8:0:4b::
|
||||
allocated address 2001:db8:0:4c::
|
||||
allocated address 2001:db8:0:4d::
|
||||
allocated address 2001:db8:0:4e::
|
||||
allocated address 2001:db8:0:4f::
|
||||
allocated address 2001:db8:0:50::
|
||||
allocated address 2001:db8:0:51::
|
||||
allocated address 2001:db8:0:52::
|
||||
allocated address 2001:db8:0:53::
|
||||
allocated address 2001:db8:0:54::
|
||||
allocated address 2001:db8:0:55::
|
||||
allocated address 2001:db8:0:56::
|
||||
allocated address 2001:db8:0:57::
|
||||
allocated address 2001:db8:0:58::
|
||||
allocated address 2001:db8:0:59::
|
||||
allocated address 2001:db8:0:5a::
|
||||
allocated address 2001:db8:0:5b::
|
||||
allocated address 2001:db8:0:5c::
|
||||
allocated address 2001:db8:0:5d::
|
||||
allocated address 2001:db8:0:5e::
|
||||
allocated address 2001:db8:0:5f::
|
||||
allocated address 2001:db8:0:60::
|
||||
allocated address 2001:db8:0:61::
|
||||
allocated address 2001:db8:0:62::
|
||||
allocated address 2001:db8:0:63::
|
||||
allocated address 2001:db8:0:64::
|
||||
allocated address 2001:db8:0:65::
|
||||
allocated address 2001:db8:0:66::
|
||||
allocated address 2001:db8:0:67::
|
||||
allocated address 2001:db8:0:68::
|
||||
allocated address 2001:db8:0:69::
|
||||
allocated address 2001:db8:0:6a::
|
||||
allocated address 2001:db8:0:6b::
|
||||
allocated address 2001:db8:0:6c::
|
||||
allocated address 2001:db8:0:6d::
|
||||
allocated address 2001:db8:0:6e::
|
||||
allocated address 2001:db8:0:6f::
|
||||
allocated address 2001:db8:0:70::
|
||||
allocated address 2001:db8:0:71::
|
||||
allocated address 2001:db8:0:72::
|
||||
allocated address 2001:db8:0:73::
|
||||
allocated address 2001:db8:0:74::
|
||||
allocated address 2001:db8:0:75::
|
||||
allocated address 2001:db8:0:76::
|
||||
allocated address 2001:db8:0:77::
|
||||
allocated address 2001:db8:0:78::
|
||||
allocated address 2001:db8:0:79::
|
||||
allocated address 2001:db8:0:7a::
|
||||
allocated address 2001:db8:0:7b::
|
||||
allocated address 2001:db8:0:7c::
|
||||
allocated address 2001:db8:0:7d::
|
||||
allocated address 2001:db8:0:7e::
|
||||
allocated address 2001:db8:0:7f::
|
||||
allocated address 2001:db8:0:80::
|
||||
allocated address 2001:db8:0:81::
|
||||
allocated address 2001:db8:0:82::
|
||||
allocated address 2001:db8:0:83::
|
||||
allocated address 2001:db8:0:84::
|
||||
allocated address 2001:db8:0:85::
|
||||
allocated address 2001:db8:0:86::
|
||||
allocated address 2001:db8:0:87::
|
||||
allocated address 2001:db8:0:88::
|
||||
allocated address 2001:db8:0:89::
|
||||
allocated address 2001:db8:0:8a::
|
||||
allocated address 2001:db8:0:8b::
|
||||
allocated address 2001:db8:0:8c::
|
||||
allocated address 2001:db8:0:8d::
|
||||
allocated address 2001:db8:0:8e::
|
||||
allocated address 2001:db8:0:8f::
|
||||
allocated address 2001:db8:0:90::
|
||||
allocated address 2001:db8:0:91::
|
||||
allocated address 2001:db8:0:92::
|
||||
allocated address 2001:db8:0:93::
|
||||
allocated address 2001:db8:0:94::
|
||||
allocated address 2001:db8:0:95::
|
||||
allocated address 2001:db8:0:96::
|
||||
allocated address 2001:db8:0:97::
|
||||
allocated address 2001:db8:0:98::
|
||||
allocated address 2001:db8:0:99::
|
||||
allocated address 2001:db8:0:9a::
|
||||
allocated address 2001:db8:0:9b::
|
||||
allocated address 2001:db8:0:9c::
|
||||
allocated address 2001:db8:0:9d::
|
||||
allocated address 2001:db8:0:9e::
|
||||
allocated address 2001:db8:0:9f::
|
||||
allocated address 2001:db8:0:a0::
|
||||
allocated address 2001:db8:0:a1::
|
||||
allocated address 2001:db8:0:a2::
|
||||
allocated address 2001:db8:0:a3::
|
||||
allocated address 2001:db8:0:a4::
|
||||
allocated address 2001:db8:0:a5::
|
||||
allocated address 2001:db8:0:a6::
|
||||
allocated address 2001:db8:0:a7::
|
||||
allocated address 2001:db8:0:a8::
|
||||
allocated address 2001:db8:0:a9::
|
||||
allocated address 2001:db8:0:aa::
|
||||
allocated address 2001:db8:0:ab::
|
||||
allocated address 2001:db8:0:ac::
|
||||
allocated address 2001:db8:0:ad::
|
||||
allocated address 2001:db8:0:ae::
|
||||
allocated address 2001:db8:0:af::
|
||||
allocated address 2001:db8:0:b0::
|
||||
allocated address 2001:db8:0:b1::
|
||||
allocated address 2001:db8:0:b2::
|
||||
allocated address 2001:db8:0:b3::
|
||||
allocated address 2001:db8:0:b4::
|
||||
allocated address 2001:db8:0:b5::
|
||||
allocated address 2001:db8:0:b6::
|
||||
allocated address 2001:db8:0:b7::
|
||||
allocated address 2001:db8:0:b8::
|
||||
allocated address 2001:db8:0:b9::
|
||||
allocated address 2001:db8:0:ba::
|
||||
allocated address 2001:db8:0:bb::
|
||||
allocated address 2001:db8:0:bc::
|
||||
allocated address 2001:db8:0:bd::
|
||||
allocated address 2001:db8:0:be::
|
||||
allocated address 2001:db8:0:bf::
|
||||
allocated address 2001:db8:0:c0::
|
||||
allocated address 2001:db8:0:c1::
|
||||
allocated address 2001:db8:0:c2::
|
||||
allocated address 2001:db8:0:c3::
|
||||
allocated address 2001:db8:0:c4::
|
||||
allocated address 2001:db8:0:c5::
|
||||
allocated address 2001:db8:0:c6::
|
||||
allocated address 2001:db8:0:c7::
|
||||
allocated address 2001:db8:0:c8::
|
||||
allocated address 2001:db8:0:c9::
|
||||
allocated address 2001:db8:0:ca::
|
||||
allocated address 2001:db8:0:cb::
|
||||
allocated address 2001:db8:0:cc::
|
||||
allocated address 2001:db8:0:cd::
|
||||
allocated address 2001:db8:0:ce::
|
||||
allocated address 2001:db8:0:cf::
|
||||
allocated address 2001:db8:0:d0::
|
||||
allocated address 2001:db8:0:d1::
|
||||
allocated address 2001:db8:0:d2::
|
||||
allocated address 2001:db8:0:d3::
|
||||
allocated address 2001:db8:0:d4::
|
||||
allocated address 2001:db8:0:d5::
|
||||
allocated address 2001:db8:0:d6::
|
||||
allocated address 2001:db8:0:d7::
|
||||
allocated address 2001:db8:0:d8::
|
||||
allocated address 2001:db8:0:d9::
|
||||
allocated address 2001:db8:0:da::
|
||||
allocated address 2001:db8:0:db::
|
||||
allocated address 2001:db8:0:dc::
|
||||
allocated address 2001:db8:0:dd::
|
||||
allocated address 2001:db8:0:de::
|
||||
allocated address 2001:db8:0:df::
|
||||
allocated address 2001:db8:0:e0::
|
||||
allocated address 2001:db8:0:e1::
|
||||
allocated address 2001:db8:0:e2::
|
||||
allocated address 2001:db8:0:e3::
|
||||
allocated address 2001:db8:0:e4::
|
||||
allocated address 2001:db8:0:e5::
|
||||
allocated address 2001:db8:0:e6::
|
||||
allocated address 2001:db8:0:e7::
|
||||
allocated address 2001:db8:0:e8::
|
||||
allocated address 2001:db8:0:e9::
|
||||
allocated address 2001:db8:0:ea::
|
||||
allocated address 2001:db8:0:eb::
|
||||
allocated address 2001:db8:0:ec::
|
||||
allocated address 2001:db8:0:ed::
|
||||
allocated address 2001:db8:0:ee::
|
||||
allocated address 2001:db8:0:ef::
|
||||
allocated address 2001:db8:0:f0::
|
||||
allocated address 2001:db8:0:f1::
|
||||
allocated address 2001:db8:0:f2::
|
||||
allocated address 2001:db8:0:f3::
|
||||
allocated address 2001:db8:0:f4::
|
||||
allocated address 2001:db8:0:f5::
|
||||
allocated address 2001:db8:0:f6::
|
||||
allocated address 2001:db8:0:f7::
|
||||
allocated address 2001:db8:0:f8::
|
||||
allocated address 2001:db8:0:f9::
|
||||
allocated address 2001:db8:0:fa::
|
||||
allocated address 2001:db8:0:fb::
|
||||
allocated address 2001:db8:0:fc::
|
||||
allocated address 2001:db8:0:fd::
|
||||
allocated address 2001:db8:0:fe::
|
||||
allocated address 2001:db8:0:ff::
|
||||
testing pool for prefix 192.168.23.0/24, flags=0x3, blacklist_size=3, expected_size=253
|
||||
allocated address 192.168.23.2
|
||||
allocated address 192.168.23.3
|
||||
|
||||
2
tests/lib/ippool_v6_test.err
Normal file
2
tests/lib/ippool_v6_test.err
Normal file
@@ -0,0 +1,2 @@
|
||||
No more IP addresses available
|
||||
No more IP addresses available
|
||||
257
tests/lib/ippool_v6_test.ok
Normal file
257
tests/lib/ippool_v6_test.ok
Normal file
@@ -0,0 +1,257 @@
|
||||
testing pool for prefix 2001:DB8::/56, flags=0x0, blacklist_size=0, expected_size=256
|
||||
allocated address 2001:db8::
|
||||
allocated address 2001:db8:0:1::
|
||||
allocated address 2001:db8:0:2::
|
||||
allocated address 2001:db8:0:3::
|
||||
allocated address 2001:db8:0:4::
|
||||
allocated address 2001:db8:0:5::
|
||||
allocated address 2001:db8:0:6::
|
||||
allocated address 2001:db8:0:7::
|
||||
allocated address 2001:db8:0:8::
|
||||
allocated address 2001:db8:0:9::
|
||||
allocated address 2001:db8:0:a::
|
||||
allocated address 2001:db8:0:b::
|
||||
allocated address 2001:db8:0:c::
|
||||
allocated address 2001:db8:0:d::
|
||||
allocated address 2001:db8:0:e::
|
||||
allocated address 2001:db8:0:f::
|
||||
allocated address 2001:db8:0:10::
|
||||
allocated address 2001:db8:0:11::
|
||||
allocated address 2001:db8:0:12::
|
||||
allocated address 2001:db8:0:13::
|
||||
allocated address 2001:db8:0:14::
|
||||
allocated address 2001:db8:0:15::
|
||||
allocated address 2001:db8:0:16::
|
||||
allocated address 2001:db8:0:17::
|
||||
allocated address 2001:db8:0:18::
|
||||
allocated address 2001:db8:0:19::
|
||||
allocated address 2001:db8:0:1a::
|
||||
allocated address 2001:db8:0:1b::
|
||||
allocated address 2001:db8:0:1c::
|
||||
allocated address 2001:db8:0:1d::
|
||||
allocated address 2001:db8:0:1e::
|
||||
allocated address 2001:db8:0:1f::
|
||||
allocated address 2001:db8:0:20::
|
||||
allocated address 2001:db8:0:21::
|
||||
allocated address 2001:db8:0:22::
|
||||
allocated address 2001:db8:0:23::
|
||||
allocated address 2001:db8:0:24::
|
||||
allocated address 2001:db8:0:25::
|
||||
allocated address 2001:db8:0:26::
|
||||
allocated address 2001:db8:0:27::
|
||||
allocated address 2001:db8:0:28::
|
||||
allocated address 2001:db8:0:29::
|
||||
allocated address 2001:db8:0:2a::
|
||||
allocated address 2001:db8:0:2b::
|
||||
allocated address 2001:db8:0:2c::
|
||||
allocated address 2001:db8:0:2d::
|
||||
allocated address 2001:db8:0:2e::
|
||||
allocated address 2001:db8:0:2f::
|
||||
allocated address 2001:db8:0:30::
|
||||
allocated address 2001:db8:0:31::
|
||||
allocated address 2001:db8:0:32::
|
||||
allocated address 2001:db8:0:33::
|
||||
allocated address 2001:db8:0:34::
|
||||
allocated address 2001:db8:0:35::
|
||||
allocated address 2001:db8:0:36::
|
||||
allocated address 2001:db8:0:37::
|
||||
allocated address 2001:db8:0:38::
|
||||
allocated address 2001:db8:0:39::
|
||||
allocated address 2001:db8:0:3a::
|
||||
allocated address 2001:db8:0:3b::
|
||||
allocated address 2001:db8:0:3c::
|
||||
allocated address 2001:db8:0:3d::
|
||||
allocated address 2001:db8:0:3e::
|
||||
allocated address 2001:db8:0:3f::
|
||||
allocated address 2001:db8:0:40::
|
||||
allocated address 2001:db8:0:41::
|
||||
allocated address 2001:db8:0:42::
|
||||
allocated address 2001:db8:0:43::
|
||||
allocated address 2001:db8:0:44::
|
||||
allocated address 2001:db8:0:45::
|
||||
allocated address 2001:db8:0:46::
|
||||
allocated address 2001:db8:0:47::
|
||||
allocated address 2001:db8:0:48::
|
||||
allocated address 2001:db8:0:49::
|
||||
allocated address 2001:db8:0:4a::
|
||||
allocated address 2001:db8:0:4b::
|
||||
allocated address 2001:db8:0:4c::
|
||||
allocated address 2001:db8:0:4d::
|
||||
allocated address 2001:db8:0:4e::
|
||||
allocated address 2001:db8:0:4f::
|
||||
allocated address 2001:db8:0:50::
|
||||
allocated address 2001:db8:0:51::
|
||||
allocated address 2001:db8:0:52::
|
||||
allocated address 2001:db8:0:53::
|
||||
allocated address 2001:db8:0:54::
|
||||
allocated address 2001:db8:0:55::
|
||||
allocated address 2001:db8:0:56::
|
||||
allocated address 2001:db8:0:57::
|
||||
allocated address 2001:db8:0:58::
|
||||
allocated address 2001:db8:0:59::
|
||||
allocated address 2001:db8:0:5a::
|
||||
allocated address 2001:db8:0:5b::
|
||||
allocated address 2001:db8:0:5c::
|
||||
allocated address 2001:db8:0:5d::
|
||||
allocated address 2001:db8:0:5e::
|
||||
allocated address 2001:db8:0:5f::
|
||||
allocated address 2001:db8:0:60::
|
||||
allocated address 2001:db8:0:61::
|
||||
allocated address 2001:db8:0:62::
|
||||
allocated address 2001:db8:0:63::
|
||||
allocated address 2001:db8:0:64::
|
||||
allocated address 2001:db8:0:65::
|
||||
allocated address 2001:db8:0:66::
|
||||
allocated address 2001:db8:0:67::
|
||||
allocated address 2001:db8:0:68::
|
||||
allocated address 2001:db8:0:69::
|
||||
allocated address 2001:db8:0:6a::
|
||||
allocated address 2001:db8:0:6b::
|
||||
allocated address 2001:db8:0:6c::
|
||||
allocated address 2001:db8:0:6d::
|
||||
allocated address 2001:db8:0:6e::
|
||||
allocated address 2001:db8:0:6f::
|
||||
allocated address 2001:db8:0:70::
|
||||
allocated address 2001:db8:0:71::
|
||||
allocated address 2001:db8:0:72::
|
||||
allocated address 2001:db8:0:73::
|
||||
allocated address 2001:db8:0:74::
|
||||
allocated address 2001:db8:0:75::
|
||||
allocated address 2001:db8:0:76::
|
||||
allocated address 2001:db8:0:77::
|
||||
allocated address 2001:db8:0:78::
|
||||
allocated address 2001:db8:0:79::
|
||||
allocated address 2001:db8:0:7a::
|
||||
allocated address 2001:db8:0:7b::
|
||||
allocated address 2001:db8:0:7c::
|
||||
allocated address 2001:db8:0:7d::
|
||||
allocated address 2001:db8:0:7e::
|
||||
allocated address 2001:db8:0:7f::
|
||||
allocated address 2001:db8:0:80::
|
||||
allocated address 2001:db8:0:81::
|
||||
allocated address 2001:db8:0:82::
|
||||
allocated address 2001:db8:0:83::
|
||||
allocated address 2001:db8:0:84::
|
||||
allocated address 2001:db8:0:85::
|
||||
allocated address 2001:db8:0:86::
|
||||
allocated address 2001:db8:0:87::
|
||||
allocated address 2001:db8:0:88::
|
||||
allocated address 2001:db8:0:89::
|
||||
allocated address 2001:db8:0:8a::
|
||||
allocated address 2001:db8:0:8b::
|
||||
allocated address 2001:db8:0:8c::
|
||||
allocated address 2001:db8:0:8d::
|
||||
allocated address 2001:db8:0:8e::
|
||||
allocated address 2001:db8:0:8f::
|
||||
allocated address 2001:db8:0:90::
|
||||
allocated address 2001:db8:0:91::
|
||||
allocated address 2001:db8:0:92::
|
||||
allocated address 2001:db8:0:93::
|
||||
allocated address 2001:db8:0:94::
|
||||
allocated address 2001:db8:0:95::
|
||||
allocated address 2001:db8:0:96::
|
||||
allocated address 2001:db8:0:97::
|
||||
allocated address 2001:db8:0:98::
|
||||
allocated address 2001:db8:0:99::
|
||||
allocated address 2001:db8:0:9a::
|
||||
allocated address 2001:db8:0:9b::
|
||||
allocated address 2001:db8:0:9c::
|
||||
allocated address 2001:db8:0:9d::
|
||||
allocated address 2001:db8:0:9e::
|
||||
allocated address 2001:db8:0:9f::
|
||||
allocated address 2001:db8:0:a0::
|
||||
allocated address 2001:db8:0:a1::
|
||||
allocated address 2001:db8:0:a2::
|
||||
allocated address 2001:db8:0:a3::
|
||||
allocated address 2001:db8:0:a4::
|
||||
allocated address 2001:db8:0:a5::
|
||||
allocated address 2001:db8:0:a6::
|
||||
allocated address 2001:db8:0:a7::
|
||||
allocated address 2001:db8:0:a8::
|
||||
allocated address 2001:db8:0:a9::
|
||||
allocated address 2001:db8:0:aa::
|
||||
allocated address 2001:db8:0:ab::
|
||||
allocated address 2001:db8:0:ac::
|
||||
allocated address 2001:db8:0:ad::
|
||||
allocated address 2001:db8:0:ae::
|
||||
allocated address 2001:db8:0:af::
|
||||
allocated address 2001:db8:0:b0::
|
||||
allocated address 2001:db8:0:b1::
|
||||
allocated address 2001:db8:0:b2::
|
||||
allocated address 2001:db8:0:b3::
|
||||
allocated address 2001:db8:0:b4::
|
||||
allocated address 2001:db8:0:b5::
|
||||
allocated address 2001:db8:0:b6::
|
||||
allocated address 2001:db8:0:b7::
|
||||
allocated address 2001:db8:0:b8::
|
||||
allocated address 2001:db8:0:b9::
|
||||
allocated address 2001:db8:0:ba::
|
||||
allocated address 2001:db8:0:bb::
|
||||
allocated address 2001:db8:0:bc::
|
||||
allocated address 2001:db8:0:bd::
|
||||
allocated address 2001:db8:0:be::
|
||||
allocated address 2001:db8:0:bf::
|
||||
allocated address 2001:db8:0:c0::
|
||||
allocated address 2001:db8:0:c1::
|
||||
allocated address 2001:db8:0:c2::
|
||||
allocated address 2001:db8:0:c3::
|
||||
allocated address 2001:db8:0:c4::
|
||||
allocated address 2001:db8:0:c5::
|
||||
allocated address 2001:db8:0:c6::
|
||||
allocated address 2001:db8:0:c7::
|
||||
allocated address 2001:db8:0:c8::
|
||||
allocated address 2001:db8:0:c9::
|
||||
allocated address 2001:db8:0:ca::
|
||||
allocated address 2001:db8:0:cb::
|
||||
allocated address 2001:db8:0:cc::
|
||||
allocated address 2001:db8:0:cd::
|
||||
allocated address 2001:db8:0:ce::
|
||||
allocated address 2001:db8:0:cf::
|
||||
allocated address 2001:db8:0:d0::
|
||||
allocated address 2001:db8:0:d1::
|
||||
allocated address 2001:db8:0:d2::
|
||||
allocated address 2001:db8:0:d3::
|
||||
allocated address 2001:db8:0:d4::
|
||||
allocated address 2001:db8:0:d5::
|
||||
allocated address 2001:db8:0:d6::
|
||||
allocated address 2001:db8:0:d7::
|
||||
allocated address 2001:db8:0:d8::
|
||||
allocated address 2001:db8:0:d9::
|
||||
allocated address 2001:db8:0:da::
|
||||
allocated address 2001:db8:0:db::
|
||||
allocated address 2001:db8:0:dc::
|
||||
allocated address 2001:db8:0:dd::
|
||||
allocated address 2001:db8:0:de::
|
||||
allocated address 2001:db8:0:df::
|
||||
allocated address 2001:db8:0:e0::
|
||||
allocated address 2001:db8:0:e1::
|
||||
allocated address 2001:db8:0:e2::
|
||||
allocated address 2001:db8:0:e3::
|
||||
allocated address 2001:db8:0:e4::
|
||||
allocated address 2001:db8:0:e5::
|
||||
allocated address 2001:db8:0:e6::
|
||||
allocated address 2001:db8:0:e7::
|
||||
allocated address 2001:db8:0:e8::
|
||||
allocated address 2001:db8:0:e9::
|
||||
allocated address 2001:db8:0:ea::
|
||||
allocated address 2001:db8:0:eb::
|
||||
allocated address 2001:db8:0:ec::
|
||||
allocated address 2001:db8:0:ed::
|
||||
allocated address 2001:db8:0:ee::
|
||||
allocated address 2001:db8:0:ef::
|
||||
allocated address 2001:db8:0:f0::
|
||||
allocated address 2001:db8:0:f1::
|
||||
allocated address 2001:db8:0:f2::
|
||||
allocated address 2001:db8:0:f3::
|
||||
allocated address 2001:db8:0:f4::
|
||||
allocated address 2001:db8:0:f5::
|
||||
allocated address 2001:db8:0:f6::
|
||||
allocated address 2001:db8:0:f7::
|
||||
allocated address 2001:db8:0:f8::
|
||||
allocated address 2001:db8:0:f9::
|
||||
allocated address 2001:db8:0:fa::
|
||||
allocated address 2001:db8:0:fb::
|
||||
allocated address 2001:db8:0:fc::
|
||||
allocated address 2001:db8:0:fd::
|
||||
allocated address 2001:db8:0:fe::
|
||||
allocated address 2001:db8:0:ff::
|
||||
@@ -8,12 +8,25 @@ cat $abs_srcdir/lib/ippool_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/lib/ippool_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([ippool_v6])
|
||||
AT_KEYWORDS([ippool_v6])
|
||||
cat $abs_srcdir/lib/ippool_v6_test.ok > expout
|
||||
cat $abs_srcdir/lib/ippool_v6_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/lib/ippool_test -v6], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([in46a])
|
||||
AT_KEYWORDS([in46a])
|
||||
cat $abs_srcdir/lib/in46a_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/lib/in46a_test], [], [expout], [])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([in46a_v6])
|
||||
AT_KEYWORDS([in46a_v6])
|
||||
cat $abs_srcdir/lib/in46a_v6_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/lib/in46a_test -v6], [], [expout], [])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([gtpie])
|
||||
AT_KEYWORDS([gtpie])
|
||||
cat $abs_srcdir/gtp/gtpie_test.ok > expout
|
||||
|
||||
Reference in New Issue
Block a user