mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn.git
synced 2025-11-06 07:03:45 +00:00
Compare commits
179 Commits
1.0.0
...
daniel/onw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24d216c683 | ||
|
|
8e97fccc34 | ||
|
|
ee44b82b96 | ||
|
|
b5f93346df | ||
|
|
8e8c7ef3c7 | ||
|
|
57238889eb | ||
|
|
d70ab97fa4 | ||
|
|
d1bd6fce9c | ||
|
|
a32e4c4fb8 | ||
|
|
3b84e92ab3 | ||
|
|
835496d7dd | ||
|
|
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 | ||
|
|
b9ace14717 | ||
|
|
d9fff0c543 | ||
|
|
1d85bea152 | ||
|
|
f1e44c5493 | ||
|
|
bebd75c2d1 | ||
|
|
878ece768b | ||
|
|
a00e79242b | ||
|
|
840ce8a0a8 | ||
|
|
afd76a731f | ||
|
|
fd30bd1032 | ||
|
|
227034c88e | ||
|
|
3dad951171 | ||
|
|
318795635e | ||
|
|
22e1573831 | ||
|
|
c85e89961a | ||
|
|
2fc2bc6bc4 | ||
|
|
0d0e242685 | ||
|
|
698a2339eb | ||
|
|
490782d18e | ||
|
|
e3c5918aee | ||
|
|
e2a1de5ca5 | ||
|
|
4c7d29107f | ||
|
|
f55a039048 | ||
|
|
fc6676c4a0 | ||
|
|
1af543f44c | ||
|
|
bd228244da | ||
|
|
a06120de77 | ||
|
|
b589e78f13 | ||
|
|
b11ed0f132 | ||
|
|
9c332104eb | ||
|
|
51127ea962 | ||
|
|
dabb8b4860 | ||
|
|
ff069172ce | ||
|
|
7bee06e1cc | ||
|
|
6c10aa0e6f | ||
|
|
a4e24f5546 | ||
|
|
29caaab817 | ||
|
|
859f9b0752 | ||
|
|
a037e5908a | ||
|
|
2e7b9ff891 | ||
|
|
361cb9e910 | ||
|
|
5bacb59a6c | ||
|
|
5b0096a236 | ||
|
|
df6a105024 | ||
|
|
226e95af1f | ||
|
|
e37f48eaf9 | ||
|
|
8a55263a1b | ||
|
|
fed3389112 | ||
|
|
081f30cba4 | ||
|
|
ea0c26a436 | ||
|
|
9d9d91b8e8 | ||
|
|
73abc38dc5 | ||
|
|
be4baa6d97 | ||
|
|
d369013250 | ||
|
|
8afec5f86d | ||
|
|
5943cbb73f | ||
|
|
f6c5f9524f | ||
|
|
bc41c8d581 | ||
|
|
89e1abcb18 | ||
|
|
8376972050 | ||
|
|
a964027344 | ||
|
|
c5150cecc5 | ||
|
|
02af9b3ca2 | ||
|
|
db924d3908 | ||
|
|
34a7416ec0 | ||
|
|
45ce2725ac | ||
|
|
a2eb5eb760 | ||
|
|
fdd732b130 | ||
|
|
1d8ffc6b23 | ||
|
|
58c0da7833 | ||
|
|
958256f5cf | ||
|
|
6748dc90b8 | ||
|
|
7bd7b6815a | ||
|
|
b16c46b4c3 | ||
|
|
840a8e9713 | ||
|
|
cee7546f15 | ||
|
|
ed1ba2c902 | ||
|
|
ed08eb1c5a | ||
|
|
2e84d2c29a | ||
|
|
dd266066c7 | ||
|
|
b5624c3d48 | ||
|
|
6a21527a2d | ||
|
|
1a8bc9839a | ||
|
|
79aa4bd837 | ||
|
|
fbb9c7f59a | ||
|
|
3c1cce245e | ||
|
|
93fed3bc51 | ||
|
|
00d346092b | ||
|
|
3ca419a2ef | ||
|
|
ff438174aa | ||
|
|
f85fe9720b | ||
|
|
fed598f41d | ||
|
|
471e349ecc | ||
|
|
7e1175f6d8 | ||
|
|
f621498129 | ||
|
|
7c20148e39 | ||
|
|
b6fc227763 | ||
|
|
1d94585f96 | ||
|
|
05ac095006 | ||
|
|
73d28c9dda | ||
|
|
ea70f3619a | ||
|
|
98146776dd |
48
.gitignore
vendored
48
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
# autotools
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
@@ -17,31 +18,54 @@ ltmain.sh
|
|||||||
missing
|
missing
|
||||||
osmo-ggsn.spec
|
osmo-ggsn.spec
|
||||||
stamp-h1
|
stamp-h1
|
||||||
doc/Makefile.in
|
INSTALL
|
||||||
ggsn/Makefile.in
|
m4/
|
||||||
gtp/Makefile.in
|
Makefile
|
||||||
sgsnemu/Makefile.in
|
osmo-ggsn-*.tar*
|
||||||
|
.version
|
||||||
|
.tarball-version
|
||||||
|
|
||||||
|
# debian
|
||||||
debian/osmo-ggsn/
|
debian/osmo-ggsn/
|
||||||
debian/*.debhelper
|
debian/*.debhelper
|
||||||
debian/libgtp/
|
debian/libgtp1
|
||||||
|
debian/osmo-ggsn-dbg
|
||||||
debian/*.log
|
debian/*.log
|
||||||
INSTALL
|
|
||||||
debian/autoreconf.*
|
debian/autoreconf.*
|
||||||
debian/*.substvars
|
debian/*.substvars
|
||||||
debian/tmp/
|
debian/tmp/
|
||||||
sgsnemu/sgsnemu
|
|
||||||
debian/files
|
debian/files
|
||||||
debian/libgtp-dev/
|
debian/libgtp-dev/
|
||||||
|
|
||||||
|
# programs / libraries
|
||||||
|
sgsnemu/sgsnemu
|
||||||
libgtp.pc
|
libgtp.pc
|
||||||
ggsn/osmo-ggsn
|
ggsn/osmo-ggsn
|
||||||
m4/
|
|
||||||
*.swp
|
# compiler results
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.la
|
*.la
|
||||||
*.lo
|
*.lo
|
||||||
|
*.pyc
|
||||||
|
.deps
|
||||||
|
.libs
|
||||||
|
|
||||||
|
# misc
|
||||||
|
*.swp
|
||||||
|
.dirstamp
|
||||||
|
.deps
|
||||||
*.orig
|
*.orig
|
||||||
|
*.new
|
||||||
*.rej
|
*.rej
|
||||||
*/.deps
|
*~
|
||||||
*/.libs
|
osmo-ggsn.cfg
|
||||||
*/Makefile
|
sgsnemu.pid
|
||||||
|
gsn_restart
|
||||||
|
|
||||||
|
# testsuite
|
||||||
|
tests/atconfig
|
||||||
|
tests/*/*_test
|
||||||
|
tests/testsuite
|
||||||
|
tests/testsuite.log
|
||||||
|
tests/package.m4
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[gerrit]
|
[gerrit]
|
||||||
host=gerrit.osmocom.org
|
host=gerrit.osmocom.org
|
||||||
project=openggsn
|
project=osmo-ggsn
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
## Process this file with automake to produce Makefile.in
|
## Process this file with automake to produce Makefile.in
|
||||||
SUBDIRS = lib gtp ggsn sgsnemu doc
|
SUBDIRS = lib gtp ggsn sgsnemu doc tests
|
||||||
|
|
||||||
pkgconfigdir = $(libdir)/pkgconfig
|
pkgconfigdir = $(libdir)/pkgconfig
|
||||||
pkgconfig_DATA = libgtp.pc
|
pkgconfig_DATA = libgtp.pc
|
||||||
|
|||||||
@@ -263,8 +263,7 @@ following:
|
|||||||
|
|
||||||
1. Install sgsnemu on a Linux Box. See under installation above.
|
1. Install sgsnemu on a Linux Box. See under installation above.
|
||||||
2. Connect your Linux box with sgsnemu installed to the GPRS core
|
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
|
network. You also need a free IP address that can be used by sgsnemu.
|
||||||
to. You also need a free IP address that can be used by sgsnemu.
|
|
||||||
3. You need to configure networking in terms of interface address,
|
3. You need to configure networking in terms of interface address,
|
||||||
subnet mask and default route. See the Linux Networking HOWTO for
|
subnet mask and default route. See the Linux Networking HOWTO for
|
||||||
details.
|
details.
|
||||||
|
|||||||
45
configure.ac
45
configure.ac
@@ -7,6 +7,7 @@ AM_CONFIG_HEADER([config.h])
|
|||||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||||
AC_CONFIG_AUX_DIR([.])
|
AC_CONFIG_AUX_DIR([.])
|
||||||
|
|
||||||
|
AC_CONFIG_TESTDIR(tests)
|
||||||
AC_CANONICAL_SYSTEM
|
AC_CANONICAL_SYSTEM
|
||||||
|
|
||||||
dnl kernel style compile messages
|
dnl kernel style compile messages
|
||||||
@@ -17,7 +18,6 @@ AC_PROG_CC
|
|||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
AC_PROG_AWK
|
AC_PROG_AWK
|
||||||
AC_PROG_CPP
|
AC_PROG_CPP
|
||||||
AC_PROG_CXX
|
|
||||||
LT_INIT
|
LT_INIT
|
||||||
|
|
||||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||||
@@ -65,7 +65,7 @@ AC_ARG_ENABLE([gtp-linux],
|
|||||||
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
|
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
|
||||||
|
|
||||||
AS_IF([test "x$enable_gtp_linux" = "xyes"], [
|
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"])
|
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
|
||||||
@@ -135,9 +135,42 @@ adl_FUNC_GETOPT_LONG
|
|||||||
|
|
||||||
AM_INIT_AUTOMAKE([foreign])
|
AM_INIT_AUTOMAKE([foreign])
|
||||||
|
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
|
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(sanitize,
|
||||||
|
[AS_HELP_STRING(
|
||||||
|
[--enable-sanitize],
|
||||||
|
[Compile with address sanitizer enabled],
|
||||||
|
)],
|
||||||
|
[sanitize=$enableval], [sanitize="no"])
|
||||||
|
if test x"$sanitize" = x"yes"
|
||||||
|
then
|
||||||
|
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||||
|
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
|
||||||
|
|
||||||
|
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||||
|
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
@@ -149,6 +182,8 @@ AC_CONFIG_FILES([Makefile
|
|||||||
po/Makefile
|
po/Makefile
|
||||||
sgsnemu/Makefile
|
sgsnemu/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
|
tests/lib/Makefile
|
||||||
|
tests/gtp/Makefile
|
||||||
libgtp.pc
|
libgtp.pc
|
||||||
osmo-ggsn.spec])
|
osmo-ggsn.spec])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|||||||
@@ -14,9 +14,13 @@ deps="$base/deps"
|
|||||||
inst="$deps/install"
|
inst="$deps/install"
|
||||||
export deps inst
|
export deps inst
|
||||||
|
|
||||||
mkdir "$deps" || true
|
osmo-clean-workspace.sh
|
||||||
rm -rf "$inst"
|
|
||||||
|
|
||||||
|
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
|
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||||
|
|
||||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||||
@@ -28,12 +32,14 @@ set +x
|
|||||||
echo
|
echo
|
||||||
echo
|
echo
|
||||||
echo
|
echo
|
||||||
echo " =============================== openggsn ==============================="
|
echo " =============================== OsmoGGSN ==============================="
|
||||||
echo
|
echo
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
cd "$base"
|
cd "$base"
|
||||||
autoreconf --install --force
|
autoreconf --install --force
|
||||||
./configure
|
./configure --enable-sanitize --enable-werror $GTP
|
||||||
$MAKE $PARALLEL_MAKE
|
$MAKE $PARALLEL_MAKE
|
||||||
$MAKE distcheck
|
$MAKE distcheck
|
||||||
|
|
||||||
|
osmo-clean-workspace.sh
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=OpenGGSN
|
Description=OsmoGGSN
|
||||||
After=networking.service
|
After=networking.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/bin/ggsn -c /etc/ggsn.conf -f
|
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
|
||||||
RestartSec=2
|
RestartSec=2
|
||||||
RestartPreventExitStatus=1
|
RestartPreventExitStatus=1
|
||||||
|
|
||||||
145
debian/changelog
vendored
145
debian/changelog
vendored
@@ -1,3 +1,148 @@
|
|||||||
|
osmo-ggsn (1.2.2+ow2) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Release version for On-Waves
|
||||||
|
* From commit ee44b82b967929eaf8867d967a22428972b58d0a
|
||||||
|
|
||||||
|
-- Daniel Willmann <dwillmann@sysmocom.de> Fri, 31 Aug 2018 18:02:47 +0200
|
||||||
|
|
||||||
|
osmo-ggsn (1.2.2+ow1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Release new version for On-Waves
|
||||||
|
* commit b673d1c438488fb74abda344e563d733e5ce451a
|
||||||
|
|
||||||
|
-- Daniel Willmann <dwillmann@sysmocom.de> Fri, 31 Aug 2018 18:02:02 +0200
|
||||||
|
|
||||||
|
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
|
||||||
|
* libgtp: pdp.h: add LOGPDPX() helper to public API
|
||||||
|
|
||||||
|
-- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 19:00:23 +0200
|
||||||
|
|
||||||
osmo-ggsn (1.0.0) unstable; urgency=medium
|
osmo-ggsn (1.0.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Transition to OsmoGGSN
|
* Transition to OsmoGGSN
|
||||||
|
|||||||
23
debian/control
vendored
23
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
|
operators as the interface between the Internet and the rest of the
|
||||||
mobile network infrastructure.
|
mobile network infrastructure.
|
||||||
|
|
||||||
Package: libgtp1
|
Package: libgtp3
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Section: libs
|
Section: libs
|
||||||
@@ -41,7 +41,7 @@ Architecture: any
|
|||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Section: libdevel
|
Section: libdevel
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
libgtp1 (= ${binary:Version})
|
libgtp3 (= ${binary:Version})
|
||||||
Description: Development files for libgtp
|
Description: Development files for libgtp
|
||||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
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
|
operators as the interface between the Internet and the rest of the
|
||||||
@@ -54,6 +54,23 @@ Package: osmo-ggsn-dbg
|
|||||||
Section: debug
|
Section: debug
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Priority: extra
|
Priority: extra
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp1 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp3 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Description: Debug symbols for OsmoGGSN
|
Description: Debug symbols for OsmoGGSN
|
||||||
|
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||||
|
operators as the interface between the Internet and the rest of the
|
||||||
|
mobile network infrastructure.
|
||||||
|
|
||||||
|
Package: libgtp-dbg
|
||||||
|
Section: debug
|
||||||
|
Architecture: any
|
||||||
|
Priority: extra
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp3 (= ${binary:Version})
|
||||||
|
Multi-Arch: same
|
||||||
|
Description: Debug symbols for OsmoGGSN
|
||||||
|
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||||
|
operators as the interface between the Internet and the rest of the
|
||||||
|
mobile network infrastructure.
|
||||||
|
.
|
||||||
|
The library libgtp implements the GTP protocol between SGSN and GGSN
|
||||||
|
and this package contains the development files for this library.
|
||||||
|
|||||||
1
debian/osmo-ggsn.service
vendored
Symbolic link
1
debian/osmo-ggsn.service
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../contrib/osmo-ggsn.service
|
||||||
7
debian/rules
vendored
7
debian/rules
vendored
@@ -15,8 +15,5 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
|||||||
dh $@ --with autoreconf
|
dh $@ --with autoreconf
|
||||||
|
|
||||||
override_dh_strip:
|
override_dh_strip:
|
||||||
dh_strip --dbg-package=osmo-ggsn-dbg
|
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
|
||||||
|
dh_strip -plibgtp3 --dbg-package=libgtp-dbg
|
||||||
override_dh_autoreconf:
|
|
||||||
echo $(VERSION) > .tarball-version
|
|
||||||
dh_autoreconf
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ ggsn ggsn0
|
|||||||
type-support v6
|
type-support v6
|
||||||
ipv6 prefix dynamic 2001:780:44:2000:0:0:0:0/56
|
ipv6 prefix dynamic 2001:780:44:2000:0:0:0:0/56
|
||||||
ipv6 dns 0 2001:4860:4860::8888
|
ipv6 dns 0 2001:4860:4860::8888
|
||||||
|
ipv6 dns 1 2001:4860:4860::8844
|
||||||
ipv6 ifconfig 2001:780:44:2000:0:0:0:0/56
|
ipv6 ifconfig 2001:780:44:2000:0:0:0:0/56
|
||||||
no shutdown
|
no shutdown
|
||||||
apn inet46
|
apn inet46
|
||||||
@@ -65,6 +66,7 @@ ggsn ggsn0
|
|||||||
ip ifconfig 176.16.46.0/24
|
ip ifconfig 176.16.46.0/24
|
||||||
ipv6 prefix dynamic 2001:780:44:2100:0:0:0:0/56
|
ipv6 prefix dynamic 2001:780:44:2100:0:0:0:0/56
|
||||||
ipv6 dns 0 2001:4860:4860::8888
|
ipv6 dns 0 2001:4860:4860::8888
|
||||||
|
ipv6 dns 1 2001:4860:4860::8844
|
||||||
ipv6 ifconfig 2001:780:44:2100:0:0:0:0/56
|
ipv6 ifconfig 2001:780:44:2100:0:0:0:0/56
|
||||||
no shutdown
|
no shutdown
|
||||||
default-apn internet
|
default-apn internet
|
||||||
|
|||||||
@@ -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)
|
osmo_ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||||
|
|
||||||
if ENABLE_GTP_KERNEL
|
if ENABLE_GTP_KERNEL
|
||||||
AM_CFLAGS += -DGTP_KERNEL
|
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||||
osmo_ggsn_LDADD += -lgtpnl
|
osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
|
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
|
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h
|
||||||
|
|
||||||
if ENABLE_GTP_KERNEL
|
|
||||||
osmo_ggsn_SOURCES += gtp-kernel.c
|
|
||||||
endif
|
|
||||||
|
|||||||
490
ggsn/ggsn.c
490
ggsn/ggsn.c
@@ -56,15 +56,16 @@
|
|||||||
#include <osmocom/vty/stats.h>
|
#include <osmocom/vty/stats.h>
|
||||||
#include <osmocom/vty/ports.h>
|
#include <osmocom/vty/ports.h>
|
||||||
#include <osmocom/vty/command.h>
|
#include <osmocom/vty/command.h>
|
||||||
|
#include <osmocom/vty/misc.h>
|
||||||
#include <osmocom/gsm/apn.h>
|
#include <osmocom/gsm/apn.h>
|
||||||
|
|
||||||
#include "../lib/tun.h"
|
#include "../lib/tun.h"
|
||||||
#include "../lib/ippool.h"
|
#include "../lib/ippool.h"
|
||||||
#include "../lib/syserr.h"
|
#include "../lib/syserr.h"
|
||||||
#include "../lib/in46_addr.h"
|
#include "../lib/in46_addr.h"
|
||||||
|
#include "../lib/gtp-kernel.h"
|
||||||
#include "../gtp/pdp.h"
|
#include "../gtp/pdp.h"
|
||||||
#include "../gtp/gtp.h"
|
#include "../gtp/gtp.h"
|
||||||
#include "gtp-kernel.h"
|
|
||||||
#include "icmpv6.h"
|
#include "icmpv6.h"
|
||||||
#include "ggsn.h"
|
#include "ggsn.h"
|
||||||
|
|
||||||
@@ -83,8 +84,7 @@ struct ul255_t apn;
|
|||||||
#define LOGPGGSN(level, ggsn, fmt, args...) \
|
#define LOGPGGSN(level, ggsn, fmt, args...) \
|
||||||
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
|
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
|
||||||
|
|
||||||
#define LOGPPDP(level, pdp, fmt, args...) \
|
#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
|
||||||
LOGP(DGGSN, level, "PDP(%s:%u): " fmt, imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi, ## args)
|
|
||||||
|
|
||||||
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
|
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
|
||||||
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
|
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
|
||||||
@@ -113,9 +113,6 @@ static void pool_close_all_pdp(struct ippool_t *pool)
|
|||||||
|
|
||||||
int apn_stop(struct apn_ctx *apn, bool force)
|
int apn_stop(struct apn_ctx *apn, bool force)
|
||||||
{
|
{
|
||||||
if (!apn->started)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
LOGPAPN(LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
|
LOGPAPN(LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
|
||||||
/* check if pools have any active PDP contexts and bail out */
|
/* check if pools have any active PDP contexts and bail out */
|
||||||
pool_close_all_pdp(apn->v4.pool);
|
pool_close_all_pdp(apn->v4.pool);
|
||||||
@@ -128,9 +125,11 @@ int apn_stop(struct apn_ctx *apn, bool force)
|
|||||||
LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
|
LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
|
||||||
tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
|
tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
|
||||||
}
|
}
|
||||||
|
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
|
||||||
/* release tun device */
|
/* release tun device */
|
||||||
LOGPAPN(LOGL_INFO, apn, "Closing TUN device\n");
|
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
|
||||||
osmo_fd_unregister(&apn->tun.fd);
|
osmo_fd_unregister(&apn->tun.fd);
|
||||||
|
}
|
||||||
tun_free(apn->tun.tun);
|
tun_free(apn->tun.tun);
|
||||||
apn->tun.tun = NULL;
|
apn->tun.tun = NULL;
|
||||||
}
|
}
|
||||||
@@ -150,9 +149,56 @@ int apn_stop(struct apn_ctx *apn, bool force)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int alloc_ippool_blacklist(struct apn_ctx *apn, struct in46_prefix **blacklist, bool ipv6)
|
||||||
|
{
|
||||||
|
|
||||||
|
int flags, len, len2, i;
|
||||||
|
|
||||||
|
*blacklist = NULL;
|
||||||
|
|
||||||
|
if (ipv6)
|
||||||
|
flags = IP_TYPE_IPv6_NONLINK;
|
||||||
|
else
|
||||||
|
flags = IP_TYPE_IPv4;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
len = netdev_ip_local_get(apn->tun.cfg.dev_name, NULL, 0, flags);
|
||||||
|
if (len < 1)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
*blacklist = talloc_zero_size(apn, len * sizeof(struct in46_prefix));
|
||||||
|
len2 = netdev_ip_local_get(apn->tun.cfg.dev_name, *blacklist, len, flags);
|
||||||
|
if (len2 < 1) {
|
||||||
|
talloc_free(*blacklist);
|
||||||
|
*blacklist = NULL;
|
||||||
|
return len2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len2 > len) { /* iface was added between 2 calls, repeat operation */
|
||||||
|
talloc_free(*blacklist);
|
||||||
|
*blacklist = NULL;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len2; i++)
|
||||||
|
LOGPAPN(LOGL_INFO, apn, "Blacklist tun IP %s\n",
|
||||||
|
in46p_ntoa(&(*blacklist)[i]));
|
||||||
|
|
||||||
|
return len2;
|
||||||
|
}
|
||||||
|
|
||||||
/* actually start the APN with its current config */
|
/* actually start the APN with its current config */
|
||||||
int apn_start(struct apn_ctx *apn)
|
int apn_start(struct apn_ctx *apn)
|
||||||
{
|
{
|
||||||
|
int ippool_flags = IPPOOL_NONETWORK | IPPOOL_NOBROADCAST;
|
||||||
|
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)
|
if (apn->started)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -160,7 +206,7 @@ int apn_start(struct apn_ctx *apn)
|
|||||||
switch (apn->cfg.gtpu_mode) {
|
switch (apn->cfg.gtpu_mode) {
|
||||||
case APN_GTPU_MODE_TUN:
|
case APN_GTPU_MODE_TUN:
|
||||||
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
|
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");
|
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -172,11 +218,42 @@ int apn_start(struct apn_ctx *apn)
|
|||||||
|
|
||||||
/* Set TUN library callback */
|
/* Set TUN library callback */
|
||||||
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
|
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
|
||||||
|
break;
|
||||||
|
case APN_GTPU_MODE_KERNEL_GTP:
|
||||||
|
LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
|
||||||
|
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
|
||||||
|
LOGPAPN(LOGL_ERROR, apn, "Kernel GTP currently supports only IPv4\n");
|
||||||
|
apn_stop(apn, false);
|
||||||
|
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 (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;
|
||||||
|
default:
|
||||||
|
LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
|
||||||
|
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) {
|
if (apn->v4.cfg.ifconfig_prefix.addr.len) {
|
||||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
|
LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
|
||||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
|
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
|
||||||
if (tun_setaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
|
if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
|
||||||
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
|
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
|
||||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
|
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
|
||||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
|
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
|
||||||
@@ -188,58 +265,80 @@ int apn_start(struct apn_ctx *apn)
|
|||||||
if (apn->v6.cfg.ifconfig_prefix.addr.len) {
|
if (apn->v6.cfg.ifconfig_prefix.addr.len) {
|
||||||
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
|
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
|
||||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
|
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
|
||||||
if (tun_setaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
|
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
|
||||||
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
|
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
|
||||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s\n",
|
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));
|
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
|
||||||
apn_stop(apn, false);
|
apn_stop(apn, false);
|
||||||
return -1;
|
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) {
|
if (apn->tun.cfg.ipup_script) {
|
||||||
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
|
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
|
||||||
apn->tun.cfg.ipup_script);
|
apn->tun.cfg.ipup_script);
|
||||||
tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
|
tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
|
||||||
}
|
}
|
||||||
/* set back-pointer from TUN device to APN */
|
|
||||||
apn->tun.tun->priv = apn;
|
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
|
||||||
break;
|
apn->v6.cfg.ll_prefix.addr.len == 0) {
|
||||||
case APN_GTPU_MODE_KERNEL_GTP:
|
rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
|
||||||
LOGPAPN(LOGL_ERROR, apn, "FIXME: Kernel GTP\n");
|
if (rc < 1) {
|
||||||
#if 0
|
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
|
||||||
/* use GTP kernel module for data packet encapsulation */
|
rc ? strerror(errno) : "tun interface has no link-local IP assigned");
|
||||||
if (gtp_kernel_init(gsn, &net.v4, prefixlen, net_arg) < 0)
|
apn_stop(apn, false);
|
||||||
goto err;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create IPv4 pool */
|
/* Create IPv4 pool */
|
||||||
if (apn->v4.cfg.dynamic_prefix.addr.len) {
|
if (apn->v4.cfg.dynamic_prefix.addr.len) {
|
||||||
LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
|
LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
|
||||||
in46p_ntoa(&apn->v4.cfg.dynamic_prefix));
|
in46p_ntoa(&apn->v4.cfg.dynamic_prefix));
|
||||||
|
if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, false)) < 0)
|
||||||
|
LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv4 tun IPs\n");
|
||||||
if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix,
|
if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix,
|
||||||
&apn->v4.cfg.static_prefix, 0)) {
|
&apn->v4.cfg.static_prefix, ippool_flags,
|
||||||
|
blacklist, blacklist_size)) {
|
||||||
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
|
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
|
||||||
|
talloc_free(blacklist);
|
||||||
apn_stop(apn, false);
|
apn_stop(apn, false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
talloc_free(blacklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create IPv6 pool */
|
/* Create IPv6 pool */
|
||||||
if (apn->v6.cfg.dynamic_prefix.addr.len) {
|
if (apn->v6.cfg.dynamic_prefix.addr.len) {
|
||||||
LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n",
|
LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n",
|
||||||
in46p_ntoa(&apn->v6.cfg.dynamic_prefix));
|
in46p_ntoa(&apn->v6.cfg.dynamic_prefix));
|
||||||
|
if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, true)) < 0)
|
||||||
|
LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv6 tun IPs\n");
|
||||||
if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix,
|
if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix,
|
||||||
&apn->v6.cfg.static_prefix, 0)) {
|
&apn->v6.cfg.static_prefix, ippool_flags,
|
||||||
|
blacklist, blacklist_size)) {
|
||||||
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
|
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
|
||||||
|
talloc_free(blacklist);
|
||||||
apn_stop(apn, false);
|
apn_stop(apn, false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
talloc_free(blacklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
|
LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
|
||||||
@@ -266,27 +365,71 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
|
|||||||
static int delete_context(struct pdp_t *pdp)
|
static int delete_context(struct pdp_t *pdp)
|
||||||
{
|
{
|
||||||
struct gsn_t *gsn = pdp->gsn;
|
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");
|
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
|
||||||
struct ippoolm_t *member = pdp->peer;
|
|
||||||
|
|
||||||
if (pdp->peer) {
|
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 */
|
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
|
||||||
ippool_freeip(ipp->pool, ipp);
|
ippool_freeip(member->pool, member);
|
||||||
} else
|
} else if(i == 0)
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (gtp_kernel_tunnel_del(pdp)) {
|
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",
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <osmocom/gsm/tlv.h>
|
#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 */
|
/* 3GPP TS 24.008 10.6.5.3 */
|
||||||
enum pco_protocols {
|
enum pco_protocols {
|
||||||
PCO_P_LCP = 0xC021,
|
PCO_P_LCP = 0xC021,
|
||||||
@@ -320,59 +463,88 @@ enum pco_protocols {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* determine if PCO contains given protocol */
|
/* 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 */
|
/* iterate over PCO and check if protocol contained */
|
||||||
while (cur + 3 <= pco->v + pco->l) {
|
while (cur + 3 <= pco->v + pco->l) {
|
||||||
uint16_t cur_prot = osmo_load16be(cur);
|
uint16_t cur_prot = osmo_load16be(cur);
|
||||||
uint8_t cur_len = cur[2];
|
uint8_t cur_len = cur[2];
|
||||||
if (cur_prot == prot)
|
if (cur_prot == prot && cur_len >= prot_minlen)
|
||||||
return true;
|
return cur;
|
||||||
if (cur_len == 0)
|
|
||||||
break;
|
|
||||||
cur += cur_len + 3;
|
cur += cur_len + 3;
|
||||||
}
|
}
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* determine if PDP context has IPv6 support */
|
/*! Get the peer of pdp based on IP version used.
|
||||||
static bool pdp_has_v4(struct pdp_t *pdp)
|
* \param[in] pdp PDP context to select the peer from.
|
||||||
{
|
* \param[in] v4v6 IP version to select. Valid values are 4 and 6.
|
||||||
if (pdp->eua.l == 4+2)
|
* \returns The selected peer matching the given IP version. NULL if not present.
|
||||||
return true;
|
*/
|
||||||
else
|
static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
|
||||||
return false;
|
uint8_t len1, len2, i;
|
||||||
|
|
||||||
|
if (is_ipv6) {
|
||||||
|
len1 = 8;
|
||||||
|
len2 = 16;
|
||||||
|
} else {
|
||||||
|
len1 = sizeof(struct in_addr);
|
||||||
|
len2 = len1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* construct an IPCP PCO from up to two given DNS addreses */
|
for (i = 0; i < 2; i++) {
|
||||||
static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *dns1,
|
struct ippoolm_t * ippool = pdp->peer[i];
|
||||||
const struct in46_addr *dns2)
|
if (ippool && (ippool->addr.len == len1 || ippool->addr.len == len2))
|
||||||
|
return ippool;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* construct an IPCP PCO response from request*/
|
||||||
|
static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg)
|
||||||
{
|
{
|
||||||
uint8_t *len1, *len2;
|
const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
|
||||||
uint8_t *start = msg->tail;
|
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;
|
unsigned int len_appended;
|
||||||
|
ptrdiff_t consumed;
|
||||||
|
size_t remain, offset = 0;
|
||||||
|
|
||||||
|
/* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */
|
||||||
|
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||||
|
while (pco_ipcp) {
|
||||||
|
uint8_t *start = msg->tail;
|
||||||
|
|
||||||
|
ipcp = (pco_ipcp + 3); /* 2=type + 1=len */
|
||||||
|
consumed = (ipcp - &pdp->pco_req.v[0]);
|
||||||
|
remain = sizeof(pdp->pco_req.v) - consumed;
|
||||||
|
ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */
|
||||||
|
if (remain < 0 || remain < ipcp_len)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Three byte T16L header */
|
/* Three byte T16L header */
|
||||||
msgb_put_u16(msg, 0x8021); /* IPCP */
|
msgb_put_u16(msg, 0x8021); /* IPCP */
|
||||||
len1 = msgb_put(msg, 1); /* Length of contents: delay */
|
len1 = msgb_put(msg, 1); /* Length of contents: delay */
|
||||||
|
|
||||||
msgb_put_u8(msg, 0x02); /* ACK */
|
msgb_put_u8(msg, 0x02); /* ACK */
|
||||||
msgb_put_u8(msg, id); /* ID: Needs to match request */
|
msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */
|
||||||
msgb_put_u8(msg, 0x00); /* Length MSB */
|
msgb_put_u8(msg, 0x00); /* Length MSB */
|
||||||
len2 = msgb_put(msg, 1); /* Length LSB: delay */
|
len2 = msgb_put(msg, 1); /* Length LSB: delay */
|
||||||
|
|
||||||
if (dns1 && dns1->len == 4) {
|
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, 0x81); /* DNS1 Tag */
|
||||||
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
|
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
|
||||||
msgb_put_u32(msg, dns1->v4.s_addr);
|
msgb_put_u32(msg, ntohl(dns1->v4.s_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dns2 && dns2->len == 4) {
|
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, 0x83); /* DNS2 Tag */
|
||||||
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
|
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
|
||||||
msgb_put_u32(msg, dns2->v4.s_addr);
|
msgb_put_u32(msg, ntohl(dns2->v4.s_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* patch in length values */
|
/* patch in length values */
|
||||||
@@ -380,25 +552,26 @@ static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *
|
|||||||
*len1 = len_appended - 3;
|
*len1 = len_appended - 3;
|
||||||
*len2 = len_appended - 3;
|
*len2 = len_appended - 3;
|
||||||
|
|
||||||
return 0;
|
offset += 3 + ipcp_len;
|
||||||
|
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* process one PCO request from a MS/UE, putting together the proper responses */
|
/* 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)
|
static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc(256, "PCO");
|
struct msgb *msg = msgb_alloc(256, "PCO");
|
||||||
|
struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
OSMO_ASSERT(msg);
|
OSMO_ASSERT(msg);
|
||||||
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
|
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
|
||||||
|
|
||||||
/* FIXME: also check if primary / secondary DNS was requested */
|
if (peer_v4)
|
||||||
if (pdp_has_v4(pdp) && pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)) {
|
build_ipcp_pco(apn, pdp, msg);
|
||||||
/* FIXME: properly implement this for IPCP */
|
|
||||||
build_ipcp_pco(msg, 0, &apn->v4.cfg.dns[0], &apn->v4.cfg.dns[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
||||||
struct in46_addr *i46a = &apn->v6.cfg.dns[i];
|
struct in46_addr *i46a = &apn->v6.cfg.dns[i];
|
||||||
if (i46a->len != 16)
|
if (i46a->len != 16)
|
||||||
@@ -407,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++) {
|
for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
|
||||||
struct in46_addr *i46a = &apn->v4.cfg.dns[i];
|
struct in46_addr *i46a = &apn->v4.cfg.dns[i];
|
||||||
if (i46a->len != 4)
|
if (i46a->len != 4)
|
||||||
@@ -425,15 +598,30 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
|
|||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool apn_supports_ipv4(const struct apn_ctx *apn)
|
||||||
|
{
|
||||||
|
if (apn->v4.cfg.static_prefix.addr.len || apn->v4.cfg.dynamic_prefix.addr.len)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool apn_supports_ipv6(const struct apn_ctx *apn)
|
||||||
|
{
|
||||||
|
if (apn->v6.cfg.static_prefix.addr.len || apn->v6.cfg.dynamic_prefix.addr.len)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int create_context_ind(struct pdp_t *pdp)
|
int create_context_ind(struct pdp_t *pdp)
|
||||||
{
|
{
|
||||||
static char name_buf[256];
|
static char name_buf[256];
|
||||||
struct gsn_t *gsn = pdp->gsn;
|
struct gsn_t *gsn = pdp->gsn;
|
||||||
struct ggsn_ctx *ggsn = gsn->priv;
|
struct ggsn_ctx *ggsn = gsn->priv;
|
||||||
struct in46_addr addr;
|
struct in46_addr addr[2];
|
||||||
struct ippoolm_t *member;
|
struct ippoolm_t *member = NULL, *addrv4 = NULL, *addrv6 = NULL;
|
||||||
|
char straddrv4[INET_ADDRSTRLEN], straddrv6[INET6_ADDRSTRLEN];
|
||||||
struct apn_ctx *apn;
|
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);
|
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||||
|
|
||||||
@@ -441,9 +629,17 @@ int create_context_ind(struct pdp_t *pdp)
|
|||||||
|
|
||||||
/* First find an exact APN name match */
|
/* First find an exact APN name match */
|
||||||
apn = ggsn_find_apn(ggsn, name_buf);
|
apn = ggsn_find_apn(ggsn, name_buf);
|
||||||
|
/* ignore if the APN has not been started */
|
||||||
|
if (apn && !apn->started)
|
||||||
|
apn = NULL;
|
||||||
|
|
||||||
/* then try default (if any) */
|
/* then try default (if any) */
|
||||||
if (!apn)
|
if (!apn)
|
||||||
apn = ggsn->cfg.default_apn;
|
apn = ggsn->cfg.default_apn;
|
||||||
|
/* ignore if the APN has not been started */
|
||||||
|
if (apn && !apn->started)
|
||||||
|
apn = NULL;
|
||||||
|
|
||||||
if (!apn) {
|
if (!apn) {
|
||||||
/* no APN found for what user requested */
|
/* no APN found for what user requested */
|
||||||
LOGPPDP(LOGL_NOTICE, pdp, "Unknown APN '%s', rejecting\n", name_buf);
|
LOGPPDP(LOGL_NOTICE, pdp, "Unknown APN '%s', rejecting\n", name_buf);
|
||||||
@@ -460,46 +656,69 @@ int create_context_ind(struct pdp_t *pdp)
|
|||||||
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
|
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
|
||||||
pdp->qos_neg.l = pdp->qos_req.l;
|
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",
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
|
||||||
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr.len == sizeof(struct in_addr)) {
|
/* Allocate dynamic addresses from the pool */
|
||||||
rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
|
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[i], 0);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto err_pool_full;
|
goto err_pool_full;
|
||||||
in46a_to_eua(&member->addr, &pdp->eua);
|
/* copy back */
|
||||||
|
memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
|
||||||
|
|
||||||
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
|
addrv4 = member;
|
||||||
if (gtp_kernel_tunnel_add(pdp) < 0) {
|
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
|
} else if (addr[i].len == sizeof(struct in6_addr)) {
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
|
|
||||||
return 0;
|
/* does this APN actually have an IPv6 pool? */
|
||||||
}
|
if (!apn_supports_ipv6(apn))
|
||||||
} else if (addr.len == sizeof(struct in6_addr)) {
|
goto err_wrong_af;
|
||||||
struct in46_addr tmp;
|
|
||||||
rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
|
rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto err_pool_full;
|
goto err_pool_full;
|
||||||
|
|
||||||
/* IPv6 doesn't really send the real/allocated address at this point, but just
|
/* 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 */
|
* 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 */
|
/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
|
||||||
memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
|
memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
|
||||||
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
|
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
|
||||||
memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
|
memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
|
||||||
in46a_to_eua(&tmp, &pdp->eua);
|
|
||||||
|
addrv6 = member;
|
||||||
} else
|
} else
|
||||||
OSMO_ASSERT(0);
|
OSMO_ASSERT(0);
|
||||||
|
|
||||||
pdp->peer = member;
|
pdp->peer[i] = member;
|
||||||
pdp->ipif = apn->tun.tun; /* TODO */
|
|
||||||
member->peer = pdp;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdp->ipif = apn->tun.tun; /* TODO */
|
||||||
|
pdp->priv = apn;
|
||||||
|
|
||||||
|
/* TODO: change trap to send 2 IPs */
|
||||||
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -507,8 +726,13 @@ int create_context_ind(struct pdp_t *pdp)
|
|||||||
|
|
||||||
process_pco(apn, pdp);
|
process_pco(apn, pdp);
|
||||||
|
|
||||||
LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IP=%s\n",
|
/* Transmit G-PDU sequence numbers (only) if configured in APN */
|
||||||
name_buf, apn->cfg.name, pdp->teic_own, in46a_ntoa(&member->addr));
|
pdp->tx_gpdu_seq = apn->cfg.tx_gpdu_seq;
|
||||||
|
|
||||||
|
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);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
|
|
||||||
@@ -516,6 +740,11 @@ err_pool_full:
|
|||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot allocate IP address from pool (full!)\n");
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot allocate IP address from pool (full!)\n");
|
||||||
gtp_create_context_resp(gsn, pdp, -rc);
|
gtp_create_context_resp(gsn, pdp, -rc);
|
||||||
return 0; /* Already in use, or no more available */
|
return 0; /* Already in use, or no more available */
|
||||||
|
|
||||||
|
err_wrong_af:
|
||||||
|
LOGPPDP(LOGL_ERROR, pdp, "APN doesn't support requested EUA / AF type\n");
|
||||||
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Internet-originated IP packet, needs to be sent via GTP towards MS */
|
/* Internet-originated IP packet, needs to be sent via GTP towards MS */
|
||||||
@@ -527,23 +756,31 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
|||||||
struct iphdr *iph = (struct iphdr *)pack;
|
struct iphdr *iph = (struct iphdr *)pack;
|
||||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||||
struct ippool_t *pool;
|
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)
|
if (len < sizeof(*iph) || len < 4*iph->ihl)
|
||||||
return -1;
|
return -1;
|
||||||
dst.len = 4;
|
dst.len = 4;
|
||||||
dst.v4.s_addr = iph->daddr;
|
dst.v4.s_addr = iph->daddr;
|
||||||
pool = apn->v4.pool;
|
pool = apn->v4.pool;
|
||||||
} else if (iph->version == 6) {
|
break;
|
||||||
|
case 6:
|
||||||
/* Due to the fact that 3GPP requires an allocation of a
|
/* Due to the fact that 3GPP requires an allocation of a
|
||||||
* /64 prefix to each MS, we must instruct
|
* /64 prefix to each MS, we must instruct
|
||||||
* ippool_getip() below to match only the leading /64
|
* 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.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;
|
pool = apn->v6.pool;
|
||||||
} else {
|
break;
|
||||||
LOGP(DTUN, LOGL_NOTICE, "non-IPv packet received from tun\n");
|
default:
|
||||||
|
LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,12 +788,15 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
|||||||
if (!pool)
|
if (!pool)
|
||||||
return 0;
|
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)) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
DEBUGPC(DTUN, "\n");
|
||||||
|
|
||||||
if (ipm->peer) /* Check if a peer protocol is defined */
|
if (ipm->peer) /* Check if a peer protocol is defined */
|
||||||
gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
|
gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
|
||||||
@@ -573,16 +813,55 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
|||||||
{
|
{
|
||||||
struct iphdr *iph = (struct iphdr *)pack;
|
struct iphdr *iph = (struct iphdr *)pack;
|
||||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
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;
|
||||||
|
|
||||||
LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n");
|
OSMO_ASSERT(tun);
|
||||||
|
OSMO_ASSERT(apn);
|
||||||
|
|
||||||
|
LOGPPDP(LOGL_DEBUG, pdp, "Packet received on APN(%s): forwarding to tun %s\n", apn->cfg.name, tun->devname);
|
||||||
|
|
||||||
switch (iph->version) {
|
switch (iph->version) {
|
||||||
case 6:
|
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 */
|
/* daddr: all-routers multicast addr */
|
||||||
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
|
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
|
||||||
return handle_router_mcast(pdp->gsn, pdp, pack, len);
|
return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
|
||||||
|
&apn->v6_lladdr, pack, len);
|
||||||
break;
|
break;
|
||||||
case 4:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
|
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
|
||||||
@@ -693,6 +972,14 @@ int ggsn_start(struct ggsn_ctx *ggsn)
|
|||||||
}
|
}
|
||||||
ggsn->gsn->priv = ggsn;
|
ggsn->gsn->priv = ggsn;
|
||||||
|
|
||||||
|
/* patch in different addresses to use (in case we're behind NAT, the listen
|
||||||
|
* address is different from what we advertise externally) */
|
||||||
|
if (ggsn->cfg.gtpc_addr.v4.s_addr)
|
||||||
|
ggsn->gsn->gsnc = ggsn->cfg.gtpc_addr.v4;
|
||||||
|
|
||||||
|
if (ggsn->cfg.gtpu_addr.v4.s_addr)
|
||||||
|
ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4;
|
||||||
|
|
||||||
/* Register File Descriptors */
|
/* Register File Descriptors */
|
||||||
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
||||||
rc = osmo_fd_register(&ggsn->gtp_fd0);
|
rc = osmo_fd_register(&ggsn->gtp_fd0);
|
||||||
@@ -806,6 +1093,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
|
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
|
||||||
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
|
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
|
||||||
|
g_vty_info.tall_ctx = tall_ggsn_ctx;
|
||||||
|
|
||||||
/* Handle keyboard interrupt SIGINT */
|
/* Handle keyboard interrupt SIGINT */
|
||||||
signal(SIGINT, &signal_handler);
|
signal(SIGINT, &signal_handler);
|
||||||
@@ -815,11 +1103,12 @@ int main(int argc, char **argv)
|
|||||||
signal(SIGUSR2, &signal_handler);
|
signal(SIGUSR2, &signal_handler);
|
||||||
|
|
||||||
osmo_init_ignore_signals();
|
osmo_init_ignore_signals();
|
||||||
osmo_init_logging(&log_info);
|
osmo_init_logging2(tall_ggsn_ctx, &log_info);
|
||||||
osmo_stats_init(tall_ggsn_ctx);
|
osmo_stats_init(tall_ggsn_ctx);
|
||||||
|
|
||||||
vty_init(&g_vty_info);
|
vty_init(&g_vty_info);
|
||||||
logging_vty_add_cmds(NULL);
|
logging_vty_add_cmds(NULL);
|
||||||
|
osmo_talloc_vty_add_cmds();
|
||||||
osmo_stats_vty_add_cmds(&log_info);
|
osmo_stats_vty_add_cmds(&log_info);
|
||||||
ggsn_vty_init();
|
ggsn_vty_init();
|
||||||
ctrl_vty_init(tall_ggsn_ctx);
|
ctrl_vty_init(tall_ggsn_ctx);
|
||||||
@@ -838,7 +1127,8 @@ int main(int argc, char **argv)
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
exit(1);
|
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) {
|
if (!g_ctrlh) {
|
||||||
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
|
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
10
ggsn/ggsn.h
10
ggsn/ggsn.h
@@ -22,6 +22,7 @@ struct ggsn_ctx;
|
|||||||
struct apn_ctx_ip {
|
struct apn_ctx_ip {
|
||||||
struct {
|
struct {
|
||||||
struct in46_prefix ifconfig_prefix;
|
struct in46_prefix ifconfig_prefix;
|
||||||
|
struct in46_prefix ll_prefix;
|
||||||
struct in46_prefix static_prefix;
|
struct in46_prefix static_prefix;
|
||||||
struct in46_prefix dynamic_prefix;
|
struct in46_prefix dynamic_prefix;
|
||||||
/* v4 DNS server names */
|
/* v4 DNS server names */
|
||||||
@@ -63,6 +64,8 @@ struct apn_ctx {
|
|||||||
enum apn_gtpu_mode gtpu_mode;
|
enum apn_gtpu_mode gtpu_mode;
|
||||||
/* administratively shut-down (true) or not (false) */
|
/* administratively shut-down (true) or not (false) */
|
||||||
bool shutdown;
|
bool shutdown;
|
||||||
|
/* transmit G-PDU sequeence numbers (true) or not (false) */
|
||||||
|
bool tx_gpdu_seq;
|
||||||
} cfg;
|
} cfg;
|
||||||
|
|
||||||
/* corresponding tun device */
|
/* corresponding tun device */
|
||||||
@@ -78,6 +81,9 @@ struct apn_ctx {
|
|||||||
struct osmo_fd fd;
|
struct osmo_fd fd;
|
||||||
} tun;
|
} tun;
|
||||||
|
|
||||||
|
/* ipv6 link-local address */
|
||||||
|
struct in6_addr v6_lladdr;
|
||||||
|
|
||||||
struct apn_ctx_ip v4;
|
struct apn_ctx_ip v4;
|
||||||
struct apn_ctx_ip v6;
|
struct apn_ctx_ip v6;
|
||||||
};
|
};
|
||||||
@@ -99,6 +105,10 @@ struct ggsn_ctx {
|
|||||||
struct apn_ctx *default_apn;
|
struct apn_ctx *default_apn;
|
||||||
/* ADdress to which we listen for GTP */
|
/* ADdress to which we listen for GTP */
|
||||||
struct in46_addr listen_addr;
|
struct in46_addr listen_addr;
|
||||||
|
/* Local GTP-C address advertised in GTP */
|
||||||
|
struct in46_addr gtpc_addr;
|
||||||
|
/* Local GTP-U address advertised in GTP */
|
||||||
|
struct in46_addr gtpu_addr;
|
||||||
/* directory for state file */
|
/* directory for state file */
|
||||||
char *state_dir;
|
char *state_dir;
|
||||||
/* administratively shut-down (true) or not (false) */
|
/* administratively shut-down (true) or not (false) */
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
|
|||||||
apn->ggsn = ggsn;
|
apn->ggsn = ggsn;
|
||||||
apn->cfg.name = talloc_strdup(apn, name);
|
apn->cfg.name = talloc_strdup(apn, name);
|
||||||
apn->cfg.shutdown = true;
|
apn->cfg.shutdown = true;
|
||||||
|
apn->cfg.tx_gpdu_seq = true;
|
||||||
INIT_LLIST_HEAD(&apn->cfg.name_list);
|
INIT_LLIST_HEAD(&apn->cfg.name_list);
|
||||||
|
|
||||||
llist_add_tail(&apn->list, &ggsn->apn_list);
|
llist_add_tail(&apn->list, &ggsn->apn_list);
|
||||||
@@ -165,8 +166,8 @@ DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
|
DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
|
||||||
"gtp local-ip A.B.C.D",
|
"gtp bind-ip A.B.C.D",
|
||||||
"GTP Parameters\n"
|
"GTP Parameters\n"
|
||||||
"Set the IP address for the local GTP bind\n"
|
"Set the IP address for the local GTP bind\n"
|
||||||
"IPv4 Address\n")
|
"IPv4 Address\n")
|
||||||
@@ -179,6 +180,34 @@ DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
|
||||||
|
"gtp control-ip A.B.C.D",
|
||||||
|
"GTP Parameters\n"
|
||||||
|
"Set the IP address states as local IP in GTP-C messages\n"
|
||||||
|
"IPv4 Address\n")
|
||||||
|
{
|
||||||
|
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||||
|
size_t t;
|
||||||
|
|
||||||
|
ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
|
||||||
|
"gtp user-ip A.B.C.D",
|
||||||
|
"GTP Parameters\n"
|
||||||
|
"Set the IP address states as local IP in GTP-U messages\n"
|
||||||
|
"IPv4 Address\n")
|
||||||
|
{
|
||||||
|
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||||
|
size_t t;
|
||||||
|
|
||||||
|
ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
|
DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
|
||||||
"gtp state-dir PATH",
|
"gtp state-dir PATH",
|
||||||
"GTP Parameters\n"
|
"GTP Parameters\n"
|
||||||
@@ -484,6 +513,24 @@ DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
|
|||||||
return CMD_SUCCESS;
|
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"
|
#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,
|
DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
|
||||||
@@ -530,6 +577,24 @@ DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
|
||||||
|
"g-pdu tx-sequence-numbers",
|
||||||
|
"G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
|
||||||
|
{
|
||||||
|
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||||
|
apn->cfg.tx_gpdu_seq = true;
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
|
||||||
|
"no g-pdu tx-sequence-numbers",
|
||||||
|
NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
|
||||||
|
{
|
||||||
|
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||||
|
apn->cfg.tx_gpdu_seq = false;
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
||||||
"shutdown",
|
"shutdown",
|
||||||
"Put the APN in administrative shut-down\n")
|
"Put the APN in administrative shut-down\n")
|
||||||
@@ -593,6 +658,9 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
|
|||||||
VTY_NEWLINE);
|
VTY_NEWLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!apn->cfg.tx_gpdu_seq)
|
||||||
|
vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
|
||||||
|
|
||||||
/* IPv4 prefixes + DNS */
|
/* IPv4 prefixes + DNS */
|
||||||
if (apn->v4.cfg.static_prefix.addr.len)
|
if (apn->v4.cfg.static_prefix.addr.len)
|
||||||
vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
|
vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
|
||||||
@@ -614,10 +682,12 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
|
|||||||
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
||||||
if (!apn->v6.cfg.dns[i].len)
|
if (!apn->v6.cfg.dns[i].len)
|
||||||
continue;
|
continue;
|
||||||
vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
|
vty_out(vty, " ipv6 dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
|
||||||
}
|
}
|
||||||
if (apn->v6.cfg.ifconfig_prefix.addr.len)
|
if (apn->v6.cfg.ifconfig_prefix.addr.len)
|
||||||
vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
|
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 */
|
/* must be last */
|
||||||
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
|
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
|
||||||
@@ -633,7 +703,11 @@ static int config_write_ggsn(struct vty *vty)
|
|||||||
if (ggsn->cfg.description)
|
if (ggsn->cfg.description)
|
||||||
vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
|
vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
|
||||||
vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
|
vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
|
||||||
vty_out(vty, " gtp local-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
|
vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
|
||||||
|
if (ggsn->cfg.gtpc_addr.v4.s_addr)
|
||||||
|
vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
|
||||||
|
if (ggsn->cfg.gtpu_addr.v4.s_addr)
|
||||||
|
vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
|
||||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||||
config_write_apn(vty, apn);
|
config_write_apn(vty, apn);
|
||||||
if (ggsn->cfg.default_apn)
|
if (ggsn->cfg.default_apn)
|
||||||
@@ -671,6 +745,8 @@ static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
|
|||||||
|
|
||||||
in46a_from_eua(&pdp->eua, &eua46);
|
in46a_from_eua(&pdp->eua, &eua46);
|
||||||
vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
|
vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
|
||||||
|
pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
|
DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
|
||||||
@@ -800,13 +876,15 @@ int ggsn_vty_init(void)
|
|||||||
|
|
||||||
install_element(CONFIG_NODE, &cfg_ggsn_cmd);
|
install_element(CONFIG_NODE, &cfg_ggsn_cmd);
|
||||||
install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
|
install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
|
||||||
|
|
||||||
install_node(&ggsn_node, config_write_ggsn);
|
install_node(&ggsn_node, config_write_ggsn);
|
||||||
vty_install_default(GGSN_NODE);
|
|
||||||
install_element(GGSN_NODE, &cfg_description_cmd);
|
install_element(GGSN_NODE, &cfg_description_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_no_description_cmd);
|
install_element(GGSN_NODE, &cfg_no_description_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_local_ip_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
|
||||||
|
install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
|
||||||
|
install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
|
||||||
install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
|
||||||
@@ -814,7 +892,6 @@ int ggsn_vty_init(void)
|
|||||||
install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
|
install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
|
||||||
|
|
||||||
install_node(&apn_node, NULL);
|
install_node(&apn_node, NULL);
|
||||||
vty_install_default(APN_NODE);
|
|
||||||
install_element(APN_NODE, &cfg_description_cmd);
|
install_element(APN_NODE, &cfg_description_cmd);
|
||||||
install_element(APN_NODE, &cfg_no_description_cmd);
|
install_element(APN_NODE, &cfg_no_description_cmd);
|
||||||
install_element(APN_NODE, &cfg_apn_shutdown_cmd);
|
install_element(APN_NODE, &cfg_apn_shutdown_cmd);
|
||||||
@@ -836,6 +913,10 @@ int ggsn_vty_init(void)
|
|||||||
install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
|
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_ipv6_ifconfig_cmd);
|
||||||
install_element(APN_NODE, &cfg_apn_no_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);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -867,6 +948,10 @@ static int ggsn_vty_go_parent(struct vty *vty)
|
|||||||
vty->index_sub = &apn->ggsn->cfg.description;
|
vty->index_sub = &apn->ggsn->cfg.description;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
vty->node = CONFIG_NODE;
|
||||||
|
vty->index = NULL;
|
||||||
|
vty->index_sub = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vty->node;
|
return vty->node;
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
#ifdef __linux__
|
|
||||||
#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../config.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_STDINT_H
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
|
|
||||||
#include <libgtpnl/gtp.h>
|
|
||||||
#include <libgtpnl/gtpnl.h>
|
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "../lib/tun.h"
|
|
||||||
#include "../lib/syserr.h"
|
|
||||||
#include "../gtp/pdp.h"
|
|
||||||
#include "../gtp/gtp.h"
|
|
||||||
|
|
||||||
#include <libgtpnl/gtp.h>
|
|
||||||
#include <libgtpnl/gtpnl.h>
|
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "gtp-kernel.h"
|
|
||||||
|
|
||||||
static void pdp_debug(struct pdp_t *pdp)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint64_t teid;
|
|
||||||
|
|
||||||
if (!debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
printf("version %u\n", pdp->version);
|
|
||||||
if (pdp->version == 0) {
|
|
||||||
teid = pdp_gettid(pdp->imsi, pdp->nsapi);
|
|
||||||
printf("flowid %u\n", pdp->flru);
|
|
||||||
} else {
|
|
||||||
teid = pdp->teid_gn; /* GTPIE_TEI_DI */
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("teid %llx\n", teid);
|
|
||||||
printf("address (%u)\n", pdp->eua.l);
|
|
||||||
|
|
||||||
/* Byte 0: 0xf1 == IETF */
|
|
||||||
/* Byte 1: 0x21 == IPv4 */
|
|
||||||
/* Byte 2-6: IPv4 address */
|
|
||||||
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
int genl_id;
|
|
||||||
struct mnl_socket *nl;
|
|
||||||
bool enabled;
|
|
||||||
} gtp_nl;
|
|
||||||
|
|
||||||
/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
|
|
||||||
#define GTP_DEVNAME "gtp0"
|
|
||||||
|
|
||||||
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
|
|
||||||
size_t prefixlen, const char *net_arg)
|
|
||||||
{
|
|
||||||
if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 0) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0,
|
|
||||||
"cannot create GTP tunnel device: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gtp_nl.enabled = true;
|
|
||||||
|
|
||||||
gtp_nl.nl = genl_socket_open();
|
|
||||||
if (gtp_nl.nl == NULL) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0,
|
|
||||||
"cannot create genetlink socket\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
|
|
||||||
if (gtp_nl.genl_id < 0) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0,
|
|
||||||
"cannot lookup GTP genetlink ID\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (debug) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_NOTICE, 0,
|
|
||||||
"Using the GTP kernel mode (genl ID is %d)\n",
|
|
||||||
gtp_nl.genl_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
|
|
||||||
net_arg, GTP_DEVNAME);
|
|
||||||
|
|
||||||
if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0,
|
|
||||||
"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, GTP_DEVNAME, net_arg);
|
|
||||||
cmd[sizeof(cmd)-1] = '\0';
|
|
||||||
|
|
||||||
err = system(cmd);
|
|
||||||
if (err < 0) {
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0,
|
|
||||||
"Failed to launch script `%s'", ipup);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gtp_kernel_stop(void)
|
|
||||||
{
|
|
||||||
if (!gtp_nl.enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gtp_dev_destroy(GTP_DEVNAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
int gtp_kernel_tunnel_add(struct pdp_t *pdp)
|
|
||||||
{
|
|
||||||
struct in_addr ms, sgsn;
|
|
||||||
struct gtp_tunnel *t;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!gtp_nl.enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pdp_debug(pdp);
|
|
||||||
|
|
||||||
t = gtp_tunnel_alloc();
|
|
||||||
if (t == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
|
|
||||||
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
|
|
||||||
|
|
||||||
gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
|
|
||||||
gtp_tunnel_set_version(t, pdp->version);
|
|
||||||
gtp_tunnel_set_ms_ip4(t, &ms);
|
|
||||||
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
|
|
||||||
if (pdp->version == 0) {
|
|
||||||
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
|
||||||
gtp_tunnel_set_flowid(t, pdp->flru);
|
|
||||||
} else {
|
|
||||||
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
|
||||||
/* use the TEI advertised by SGSN when sending packets
|
|
||||||
* towards the SGSN */
|
|
||||||
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
|
||||||
gtp_tunnel_free(t);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gtp_kernel_tunnel_del(struct pdp_t *pdp)
|
|
||||||
{
|
|
||||||
struct gtp_tunnel *t;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!gtp_nl.enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pdp_debug(pdp);
|
|
||||||
|
|
||||||
t = gtp_tunnel_alloc();
|
|
||||||
if (t == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
|
|
||||||
gtp_tunnel_set_version(t, pdp->version);
|
|
||||||
if (pdp->version == 0) {
|
|
||||||
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
|
||||||
gtp_tunnel_set_flowid(t, pdp->flru);
|
|
||||||
} else {
|
|
||||||
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
|
||||||
gtp_tunnel_free(t);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gtp_kernel_enabled(void)
|
|
||||||
{
|
|
||||||
return gtp_nl.enabled;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#ifndef _GTP_KERNEL_H_
|
|
||||||
#define _GTP_KERNEL_H_
|
|
||||||
|
|
||||||
struct gengetopt_args_info;
|
|
||||||
|
|
||||||
extern int debug;
|
|
||||||
extern char *ipup;
|
|
||||||
|
|
||||||
#ifdef GTP_KERNEL
|
|
||||||
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
|
|
||||||
size_t prefixlen, const char *net_arg);
|
|
||||||
void gtp_kernel_stop(void);
|
|
||||||
|
|
||||||
int gtp_kernel_tunnel_add(struct pdp_t *pdp);
|
|
||||||
int gtp_kernel_tunnel_del(struct pdp_t *pdp);
|
|
||||||
|
|
||||||
int gtp_kernel_enabled(void);
|
|
||||||
|
|
||||||
#else
|
|
||||||
static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
|
|
||||||
size_t prefixlen, const char *net_arg)
|
|
||||||
{
|
|
||||||
SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void gtp_kernel_stop(void) {}
|
|
||||||
|
|
||||||
static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int gtp_kernel_enabled(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif /* _GTP_KERNEL_H_ */
|
|
||||||
@@ -179,22 +179,16 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC3307 link-local scope multicast address */
|
|
||||||
static const struct in6_addr my_local_addr = {
|
|
||||||
.s6_addr = { 0x01,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0xff }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* handle incoming packets to the all-routers multicast address */
|
/* handle incoming packets to the all-routers multicast address */
|
||||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len)
|
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 ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||||
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
|
|
||||||
OSMO_ASSERT(pdp);
|
|
||||||
OSMO_ASSERT(member);
|
|
||||||
|
|
||||||
if (len < sizeof(*ip6h)) {
|
if (len < sizeof(*ip6h)) {
|
||||||
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
|
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -222,10 +216,10 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pac
|
|||||||
osmo_hexdump(pack, len));
|
osmo_hexdump(pack, len));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* FIXME: Send router advertisement from GGSN link-local
|
/* Send router advertisement from GGSN link-local
|
||||||
* address to MS link-local address, including prefix
|
* address to MS link-local address, including prefix
|
||||||
* allocated to this PDP context */
|
* allocated to this PDP context */
|
||||||
msg = icmpv6_construct_ra(&my_local_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 */
|
/* Send the constructed RA to the MS */
|
||||||
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
|
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
|
|||||||
@@ -3,4 +3,7 @@
|
|||||||
#include "../gtp/gtp.h"
|
#include "../gtp/gtp.h"
|
||||||
#include "../gtp/pdp.h"
|
#include "../gtp/pdp.h"
|
||||||
|
|
||||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len);
|
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.
|
# This is _NOT_ the library release version, it's an API version.
|
||||||
# Please read chapter "Library interface versions" of the libtool documentation
|
# 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
|
# 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=3:0:0
|
||||||
|
|
||||||
lib_LTLIBRARIES = libgtp.la
|
lib_LTLIBRARIES = libgtp.la
|
||||||
|
|
||||||
include_HEADERS = gtp.h pdp.h gtpie.h
|
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_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_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
|
||||||
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
|
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
447
gtp/gtp.c
447
gtp/gtp.c
@@ -86,6 +86,51 @@ const char *gtp_version()
|
|||||||
return 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_new */
|
||||||
/* gtp_free */
|
/* gtp_free */
|
||||||
|
|
||||||
@@ -145,6 +190,15 @@ int gtp_set_cb_conf(struct gsn_t *gsn,
|
|||||||
return 0;
|
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 gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||||
int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
|
int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
|
||||||
{
|
{
|
||||||
@@ -152,7 +206,21 @@ int gtp_set_cb_recovery(struct gsn_t *gsn,
|
|||||||
return 0;
|
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,
|
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||||
void *pack, unsigned len))
|
void *pack, unsigned len))
|
||||||
{
|
{
|
||||||
@@ -168,7 +236,7 @@ extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
|||||||
* to hold the packet header.
|
* to hold the packet header.
|
||||||
* returns the length of the header. 0 on error.
|
* 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 gtp0_header *gtp0_default = (struct gtp0_header *)packet;
|
||||||
struct gtp1_header_long *gtp1_default =
|
struct gtp1_header_long *gtp1_default =
|
||||||
@@ -192,7 +260,8 @@ static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
|
|||||||
/* set to 1 */
|
/* set to 1 */
|
||||||
/* Currently extension headers are not supported */
|
/* Currently extension headers are not supported */
|
||||||
memset(gtp1_default, 0, sizeof(struct gtp1_header_long));
|
memset(gtp1_default, 0, sizeof(struct gtp1_header_long));
|
||||||
gtp1_default->flags = 0x32; /* No extension, enable sequence, no N-PDU */
|
/* No extension, enable sequence, no N-PDU */
|
||||||
|
gtp1_default->flags = GTPHDR_F_VER(1) | GTP1HDR_F_GTP1 | GTP1HDR_F_SEQ;
|
||||||
gtp1_default->type = hton8(type);
|
gtp1_default->type = hton8(type);
|
||||||
return GTP1_HEADER_SIZE_LONG;
|
return GTP1_HEADER_SIZE_LONG;
|
||||||
default:
|
default:
|
||||||
@@ -210,10 +279,11 @@ static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
|
|||||||
static uint16_t get_seq(void *pack)
|
static uint16_t get_seq(void *pack)
|
||||||
{
|
{
|
||||||
union gtp_packet *packet = (union gtp_packet *)pack;
|
union gtp_packet *packet = (union gtp_packet *)pack;
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) {
|
||||||
return ntoh16(packet->gtp0.h.seq);
|
return ntoh16(packet->gtp0.h.seq);
|
||||||
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
|
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
|
||||||
return ntoh16(packet->gtp1l.h.seq);
|
return ntoh16(packet->gtp1l.h.seq);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -229,7 +299,7 @@ static uint64_t get_tid(void *pack)
|
|||||||
{
|
{
|
||||||
union gtp_packet *packet = (union gtp_packet *)pack;
|
union gtp_packet *packet = (union gtp_packet *)pack;
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (GTPHDR_F_GET_VER(packet->flags) == 0) { /* Version 0 */
|
||||||
return be64toh(packet->gtp0.h.tid);
|
return be64toh(packet->gtp0.h.tid);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -243,13 +313,14 @@ static uint64_t get_tid(void *pack)
|
|||||||
static uint16_t get_hlen(void *pack)
|
static uint16_t get_hlen(void *pack)
|
||||||
{
|
{
|
||||||
union gtp_packet *packet = (union gtp_packet *)pack;
|
union gtp_packet *packet = (union gtp_packet *)pack;
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) { /* Version 0 */
|
||||||
return GTP0_HEADER_SIZE;
|
return GTP0_HEADER_SIZE;
|
||||||
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
|
} else if (ver == 1 && (packet->flags & 0x07) == 0) { /* Short version 1 */
|
||||||
return GTP1_HEADER_SIZE_LONG;
|
|
||||||
} else if ((packet->flags & 0xe7) == 0x20) { /* Short version 1 */
|
|
||||||
return GTP1_HEADER_SIZE_SHORT;
|
return GTP1_HEADER_SIZE_SHORT;
|
||||||
|
} else if (ver == 1) { /* Version 1 with seq/n-pdu/ext */
|
||||||
|
return GTP1_HEADER_SIZE_LONG;
|
||||||
} else {
|
} else {
|
||||||
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
|
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -264,10 +335,11 @@ static uint16_t get_hlen(void *pack)
|
|||||||
static uint32_t get_tei(void *pack)
|
static uint32_t get_tei(void *pack)
|
||||||
{
|
{
|
||||||
union gtp_packet *packet = (union gtp_packet *)pack;
|
union gtp_packet *packet = (union gtp_packet *)pack;
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) { /* Version 0 */
|
||||||
return ntoh16(packet->gtp0.h.flow);
|
return ntoh16(packet->gtp0.h.flow);
|
||||||
} else if ((packet->flags & 0xe0) == 0x20) { /* Version 1 */
|
} else if (ver == 1) { /* Version 1 */
|
||||||
return ntoh32(packet->gtp1l.h.tei);
|
return ntoh32(packet->gtp1l.h.tei);
|
||||||
} else {
|
} else {
|
||||||
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
|
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
|
||||||
@@ -353,10 +425,11 @@ static uint32_t get_tei(void *pack)
|
|||||||
* a predefined timeout.
|
* 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,
|
union gtp_packet *packet, int len,
|
||||||
struct in_addr *inetaddr, void *cbp)
|
struct in_addr *inetaddr, void *cbp)
|
||||||
{
|
{
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct qmsg_t *qmsg;
|
struct qmsg_t *qmsg;
|
||||||
int fd;
|
int fd;
|
||||||
@@ -368,7 +441,7 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
|||||||
addr.sin_len = sizeof(addr);
|
addr.sin_len = sizeof(addr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) { /* Version 0 */
|
||||||
addr.sin_port = htons(GTP0_PORT);
|
addr.sin_port = htons(GTP0_PORT);
|
||||||
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
||||||
packet->gtp0.h.seq = hton16(gsn->seq_next);
|
packet->gtp0.h.seq = hton16(gsn->seq_next);
|
||||||
@@ -382,7 +455,7 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
|||||||
else if (pdp)
|
else if (pdp)
|
||||||
packet->gtp0.h.flow = hton16(pdp->flrc);
|
packet->gtp0.h.flow = hton16(pdp->flrc);
|
||||||
fd = gsn->fd0;
|
fd = gsn->fd0;
|
||||||
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
|
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
|
||||||
addr.sin_port = htons(GTP1C_PORT);
|
addr.sin_port = htons(GTP1C_PORT);
|
||||||
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
||||||
packet->gtp1l.h.seq = hton16(gsn->seq_next);
|
packet->gtp1l.h.seq = hton16(gsn->seq_next);
|
||||||
@@ -427,15 +500,15 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
|
|||||||
* Remove signalling packet from retransmission queue.
|
* Remove signalling packet from retransmission queue.
|
||||||
* return 0 on success, EOF if packet was not found */
|
* 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)
|
union gtp_packet *packet, int len, uint8_t * type, void **cbp)
|
||||||
{
|
{
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
uint16_t seq;
|
uint16_t seq;
|
||||||
|
|
||||||
if ((packet->gtp0.h.flags & 0xe0) == 0x00)
|
if (ver == 0)
|
||||||
seq = ntoh16(packet->gtp0.h.seq);
|
seq = ntoh16(packet->gtp0.h.seq);
|
||||||
else if ((packet->gtp1l.h.flags & 0xe2) == 0x22)
|
else if (ver == 1 && (packet->gtp1l.h.flags & GTP1HDR_F_SEQ))
|
||||||
seq = ntoh16(packet->gtp1l.h.seq);
|
seq = ntoh16(packet->gtp1l.h.seq);
|
||||||
else {
|
else {
|
||||||
GTP_LOGPKG(LOGL_ERROR, peer, packet, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, packet, len,
|
||||||
@@ -518,13 +591,14 @@ int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
|||||||
return 0;
|
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,
|
union gtp_packet *packet, int len,
|
||||||
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
|
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
|
||||||
{
|
{
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
struct qmsg_t *qmsg;
|
struct qmsg_t *qmsg;
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) { /* Version 0 */
|
||||||
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
||||||
packet->gtp0.h.seq = hton16(seq);
|
packet->gtp0.h.seq = hton16(seq);
|
||||||
packet->gtp0.h.tid = htobe64(tid);
|
packet->gtp0.h.tid = htobe64(tid);
|
||||||
@@ -533,7 +607,7 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
|
|||||||
packet->gtp0.h.flow = hton16(pdp->flru);
|
packet->gtp0.h.flow = hton16(pdp->flru);
|
||||||
else if (pdp)
|
else if (pdp)
|
||||||
packet->gtp0.h.flow = hton16(pdp->flrc);
|
packet->gtp0.h.flow = hton16(pdp->flrc);
|
||||||
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
|
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
|
||||||
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
||||||
packet->gtp1l.h.seq = hton16(seq);
|
packet->gtp1l.h.seq = hton16(seq);
|
||||||
if (pdp && (fd == gsn->fd1u))
|
if (pdp && (fd == gsn->fd1u))
|
||||||
@@ -575,11 +649,12 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
|
|||||||
return 0;
|
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,
|
union gtp_packet *packet, int len,
|
||||||
struct sockaddr_in *peer, int fd, uint16_t seq)
|
struct sockaddr_in *peer, int fd, uint16_t seq)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
memcpy(&addr, peer, sizeof(addr));
|
memcpy(&addr, peer, sizeof(addr));
|
||||||
@@ -592,10 +667,10 @@ int gtp_notification(struct gsn_t *gsn, int version,
|
|||||||
else if (fd == gsn->fd1u)
|
else if (fd == gsn->fd1u)
|
||||||
addr.sin_port = htons(GTP1C_PORT);
|
addr.sin_port = htons(GTP1C_PORT);
|
||||||
|
|
||||||
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
|
if (ver == 0) { /* Version 0 */
|
||||||
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
|
||||||
packet->gtp0.h.seq = hton16(seq);
|
packet->gtp0.h.seq = hton16(seq);
|
||||||
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
|
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
|
||||||
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
|
||||||
packet->gtp1l.h.seq = hton16(seq);
|
packet->gtp1l.h.seq = hton16(seq);
|
||||||
} else {
|
} else {
|
||||||
@@ -619,7 +694,7 @@ int gtp_notification(struct gsn_t *gsn, int version,
|
|||||||
return 0;
|
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 sockaddr_in *peer, uint16_t seq)
|
||||||
{
|
{
|
||||||
struct qmsg_t *qmsg;
|
struct qmsg_t *qmsg;
|
||||||
@@ -925,8 +1000,7 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
|||||||
if (gsn->cb_conf)
|
if (gsn->cb_conf)
|
||||||
gsn->cb_conf(type, recovery, NULL, cbp);
|
gsn->cb_conf(type, recovery, NULL, cbp);
|
||||||
|
|
||||||
if (gsn->cb_recovery)
|
emit_cb_recovery(gsn, peer, NULL, recovery);
|
||||||
gsn->cb_recovery(peer, recovery);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -964,7 +1038,7 @@ int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Send off an Supported Extension Headers Notification */
|
/* 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)
|
int fd, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
union gtp_packet packet;
|
union gtp_packet packet;
|
||||||
@@ -985,7 +1059,7 @@ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a Supported Extension Headers Notification */
|
/* 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)
|
void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -1008,7 +1082,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
|||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
||||||
/* API: Send Create PDP Context Request (7.3.1) */
|
/* 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)
|
void *cbp)
|
||||||
{
|
{
|
||||||
union gtp_packet packet;
|
union gtp_packet packet;
|
||||||
@@ -1258,6 +1332,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
struct pdp_t pdp_buf;
|
struct pdp_t pdp_buf;
|
||||||
union gtpie_member *ie[GTPIE_SIZE];
|
union gtpie_member *ie[GTPIE_SIZE];
|
||||||
uint8_t recovery;
|
uint8_t recovery;
|
||||||
|
bool recovery_recvd = false;
|
||||||
|
int rc;
|
||||||
|
|
||||||
uint16_t seq = get_seq(pack);
|
uint16_t seq = get_seq(pack);
|
||||||
int hlen = get_hlen(pack);
|
int hlen = get_hlen(pack);
|
||||||
@@ -1358,8 +1434,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
|
|
||||||
/* Recovery (optional) */
|
/* Recovery (optional) */
|
||||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||||
if (gsn->cb_recovery)
|
/* we use recovery futher down after announcing new pdp ctx to user */
|
||||||
gsn->cb_recovery(peer, recovery);
|
recovery_recvd = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Selection mode (conditional) */
|
/* Selection mode (conditional) */
|
||||||
@@ -1560,6 +1636,9 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
/* Switch to using the old pdp context */
|
/* Switch to using the old pdp context */
|
||||||
pdp = pdp_old;
|
pdp = pdp_old;
|
||||||
|
|
||||||
|
if (recovery_recvd)
|
||||||
|
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||||
|
|
||||||
/* Confirm to peer that things were "successful" */
|
/* Confirm to peer that things were "successful" */
|
||||||
return gtp_create_pdp_resp(gsn, version, pdp,
|
return gtp_create_pdp_resp(gsn, version, pdp,
|
||||||
GTPCAUSE_ACC_REQ);
|
GTPCAUSE_ACC_REQ);
|
||||||
@@ -1581,13 +1660,16 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
|
|
||||||
/* Callback function to validata login */
|
/* Callback function to validata login */
|
||||||
if (gsn->cb_create_context_ind != 0)
|
if (gsn->cb_create_context_ind != 0)
|
||||||
return gsn->cb_create_context_ind(pdp);
|
rc = gsn->cb_create_context_ind(pdp);
|
||||||
else {
|
else {
|
||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"No create_context_ind callback defined\n");
|
"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);
|
GTPCAUSE_NOT_SUPPORTED);
|
||||||
}
|
}
|
||||||
|
if (recovery_recvd)
|
||||||
|
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Create PDP Context Response */
|
/* Handle Create PDP Context Response */
|
||||||
@@ -1644,8 +1726,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
|
|||||||
|
|
||||||
/* Extract recovery (optional) */
|
/* Extract recovery (optional) */
|
||||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||||
if (gsn->cb_recovery)
|
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||||
gsn->cb_recovery(peer, recovery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract protocol configuration options (optional) */
|
/* Extract protocol configuration options (optional) */
|
||||||
@@ -1884,7 +1965,7 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Send Update PDP Context Response */
|
/* 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,
|
struct sockaddr_in *peer, int fd,
|
||||||
void *pack, unsigned len,
|
void *pack, unsigned len,
|
||||||
struct pdp_t *pdp, uint8_t cause)
|
struct pdp_t *pdp, uint8_t cause)
|
||||||
@@ -1946,7 +2027,7 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Update PDP Context Request */
|
/* 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,
|
struct sockaddr_in *peer, int fd,
|
||||||
void *pack, unsigned len)
|
void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
@@ -2054,8 +2135,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
|
|
||||||
/* Recovery (optional) */
|
/* Recovery (optional) */
|
||||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||||
if (gsn->cb_recovery)
|
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||||
gsn->cb_recovery(peer, recovery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == 0) {
|
if (version == 0) {
|
||||||
@@ -2174,7 +2254,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Update PDP Context Response */
|
/* 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 sockaddr_in *peer, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
struct pdp_t *pdp;
|
struct pdp_t *pdp;
|
||||||
@@ -2215,8 +2295,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
|
|||||||
|
|
||||||
/* Extract recovery (optional) */
|
/* Extract recovery (optional) */
|
||||||
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
|
||||||
if (gsn->cb_recovery)
|
emit_cb_recovery(gsn, peer, pdp, recovery);
|
||||||
gsn->cb_recovery(peer, recovery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check all conditional information elements */
|
/* Check all conditional information elements */
|
||||||
@@ -2285,16 +2364,66 @@ err_out:
|
|||||||
return EOF;
|
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 gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
|
||||||
int teardown)
|
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;
|
union gtp_packet packet;
|
||||||
unsigned int length =
|
unsigned int length =
|
||||||
get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
|
get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
|
||||||
struct in_addr addr;
|
struct in_addr addr;
|
||||||
struct pdp_t *linked_pdp;
|
struct pdp_t *linked_pdp;
|
||||||
struct pdp_t *secondary_pdp;
|
|
||||||
int n;
|
int n;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
@@ -2331,37 +2460,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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2501,6 +2599,9 @@ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
|
|||||||
if (linked_pdp->secondary_tei[n])
|
if (linked_pdp->secondary_tei[n])
|
||||||
count++;
|
count++;
|
||||||
if (count <= 1) {
|
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 */
|
return 0; /* 29.060 7.3.5 Ignore message */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2518,19 +2619,32 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
|||||||
uint8_t cause;
|
uint8_t cause;
|
||||||
void *cbp = NULL;
|
void *cbp = NULL;
|
||||||
uint8_t type = 0;
|
uint8_t type = 0;
|
||||||
|
struct pdp_t *pdp = NULL;
|
||||||
int hlen = get_hlen(pack);
|
int hlen = get_hlen(pack);
|
||||||
|
|
||||||
/* Remove packet from queue */
|
/* Remove packet from queue */
|
||||||
if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp))
|
if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp))
|
||||||
return EOF;
|
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 */
|
/* Decode information elements */
|
||||||
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
|
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
|
||||||
gsn->invalid++;
|
gsn->invalid++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"Invalid message format\n");
|
"Invalid message format\n");
|
||||||
if (gsn->cb_conf)
|
if (gsn->cb_conf)
|
||||||
gsn->cb_conf(type, EOF, NULL, cbp);
|
gsn->cb_conf(type, EOF, pdp, cbp);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2540,7 +2654,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
|||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"Missing mandatory information field\n");
|
"Missing mandatory information field\n");
|
||||||
if (gsn->cb_conf)
|
if (gsn->cb_conf)
|
||||||
gsn->cb_conf(type, EOF, NULL, cbp);
|
gsn->cb_conf(type, EOF, pdp, cbp);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2550,19 +2664,19 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
|||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"Unexpected cause value received: %d\n", cause);
|
"Unexpected cause value received: %d\n", cause);
|
||||||
if (gsn->cb_conf)
|
if (gsn->cb_conf)
|
||||||
gsn->cb_conf(type, cause, NULL, cbp);
|
gsn->cb_conf(type, cause, pdp, cbp);
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback function to notify application */
|
/* Callback function to notify application */
|
||||||
if (gsn->cb_conf)
|
if (gsn->cb_conf)
|
||||||
gsn->cb_conf(type, cause, NULL, cbp);
|
gsn->cb_conf(type, cause, pdp, cbp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send Error Indication (response to a GPDU message) - 3GPP TS 29.060 7.3.7 */
|
/* 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,
|
struct sockaddr_in *peer, int fd,
|
||||||
void *pack, unsigned len)
|
void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
@@ -2584,7 +2698,7 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Error Indication */
|
/* 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)
|
struct sockaddr_in *peer, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
union gtpie_member *ie[GTPIE_SIZE];
|
union gtpie_member *ie[GTPIE_SIZE];
|
||||||
@@ -2628,22 +2742,28 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
|
|||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"Received Error Indication\n");
|
"Received Error Indication\n");
|
||||||
|
|
||||||
|
/* This is obvious from above code, given the semantics of the
|
||||||
|
* functions above, but Coverity doesn't figure this out, so
|
||||||
|
* let's make it clear. It's good style anyway in case above
|
||||||
|
* code should ever change. */
|
||||||
|
OSMO_ASSERT(pdp);
|
||||||
|
|
||||||
if (gsn->cb_delete_context)
|
if (gsn->cb_delete_context)
|
||||||
gsn->cb_delete_context(pdp);
|
gsn->cb_delete_context(pdp);
|
||||||
pdp_freepdp(pdp);
|
pdp_freepdp(pdp);
|
||||||
return 0;
|
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)
|
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;
|
struct pdp_t *pdp;
|
||||||
|
|
||||||
if (version == 0) {
|
switch (version) {
|
||||||
|
case 0:
|
||||||
if (pdp_getgtp0
|
if (pdp_getgtp0
|
||||||
(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) {
|
(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) {
|
||||||
gsn->err_unknownpdp++;
|
gsn->err_unknownpdp++;
|
||||||
@@ -2653,7 +2773,8 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
|||||||
len);
|
len);
|
||||||
}
|
}
|
||||||
hlen = GTP0_HEADER_SIZE;
|
hlen = GTP0_HEADER_SIZE;
|
||||||
} else if (version == 1) {
|
break;
|
||||||
|
case 1:
|
||||||
if (pdp_getgtp1
|
if (pdp_getgtp1
|
||||||
(&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) {
|
(&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) {
|
||||||
gsn->err_unknownpdp++;
|
gsn->err_unknownpdp++;
|
||||||
@@ -2668,9 +2789,11 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
|
|||||||
hlen = GTP1_HEADER_SIZE_LONG;
|
hlen = GTP1_HEADER_SIZE_LONG;
|
||||||
else
|
else
|
||||||
hlen = GTP1_HEADER_SIZE_SHORT;
|
hlen = GTP1_HEADER_SIZE_SHORT;
|
||||||
} else {
|
break;
|
||||||
|
default:
|
||||||
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
|
||||||
"Unknown version: %d\n", version);
|
"Unknown version: %d\n", version);
|
||||||
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the GPDU was not from the peer GSN tell him to delete context */
|
/* If the GPDU was not from the peer GSN tell him to delete context */
|
||||||
@@ -2699,7 +2822,7 @@ int gtp_decaps0(struct gsn_t *gsn)
|
|||||||
socklen_t peerlen;
|
socklen_t peerlen;
|
||||||
int status;
|
int status;
|
||||||
struct gtp0_header *pheader;
|
struct gtp0_header *pheader;
|
||||||
int version = 0; /* GTP version should be determined from header! */
|
uint8_t version;
|
||||||
int fd = gsn->fd0;
|
int fd = gsn->fd0;
|
||||||
|
|
||||||
/* TODO: Need strategy of userspace buffering and blocking */
|
/* TODO: Need strategy of userspace buffering and blocking */
|
||||||
@@ -2735,15 +2858,17 @@ int gtp_decaps0(struct gsn_t *gsn)
|
|||||||
|
|
||||||
pheader = (struct gtp0_header *)(buffer);
|
pheader = (struct gtp0_header *)(buffer);
|
||||||
|
|
||||||
|
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||||
|
|
||||||
/* Version should be gtp0 (or earlier) */
|
/* Version should be gtp0 (or earlier) */
|
||||||
/* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */
|
/* 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 */
|
/* GTP 0 messages. If other version message is received we reply that we */
|
||||||
/* only support version 0, implying that this is the only version */
|
/* only support version 0, implying that this is the only version */
|
||||||
/* supported on this port */
|
/* supported on this port */
|
||||||
if (((pheader->flags & 0xe0) > 0x00)) {
|
if (version > 0) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||||
status, "Unsupported GTP version\n");
|
"Unsupported GTP version %"PRIu8"\n", version);
|
||||||
gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
|
gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -2767,23 +2892,23 @@ int gtp_decaps0(struct gsn_t *gsn)
|
|||||||
|
|
||||||
if ((gsn->mode == GTP_MODE_GGSN) &&
|
if ((gsn->mode == GTP_MODE_GGSN) &&
|
||||||
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
||||||
(pheader->type == GTP_UPDATE_PDP_RSP) ||
|
(pheader->type == GTP_UPDATE_PDP_RSP))) {
|
||||||
(pheader->type == GTP_DELETE_PDP_RSP))) {
|
|
||||||
gsn->unexpect++;
|
gsn->unexpect++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status,
|
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 */
|
continue; /* Silently discard 29.60: 11.1.4 */
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gsn->mode == GTP_MODE_SGSN) &&
|
if ((gsn->mode == GTP_MODE_SGSN) &&
|
||||||
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
||||||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
|
(pheader->type == GTP_UPDATE_PDP_REQ))) {
|
||||||
(pheader->type == GTP_DELETE_PDP_REQ))) {
|
|
||||||
gsn->unexpect++;
|
gsn->unexpect++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status,
|
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 */
|
continue; /* Silently discard 29.60: 11.1.4 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2844,7 +2969,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
|||||||
socklen_t peerlen;
|
socklen_t peerlen;
|
||||||
int status;
|
int status;
|
||||||
struct gtp1_header_short *pheader;
|
struct gtp1_header_short *pheader;
|
||||||
int version = 1; /* TODO GTP version should be determined from header! */
|
uint8_t version;
|
||||||
int fd = gsn->fd1c;
|
int fd = gsn->fd1c;
|
||||||
|
|
||||||
/* TODO: Need strategy of userspace buffering and blocking */
|
/* TODO: Need strategy of userspace buffering and blocking */
|
||||||
@@ -2880,11 +3005,13 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
|||||||
|
|
||||||
pheader = (struct gtp1_header_short *)(buffer);
|
pheader = (struct gtp1_header_short *)(buffer);
|
||||||
|
|
||||||
|
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||||
|
|
||||||
/* Version must be no larger than GTP 1 */
|
/* Version must be no larger than GTP 1 */
|
||||||
if (((pheader->flags & 0xe0) > 0x20)) {
|
if (version > 1) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||||
status, "Unsupported GTP version\n");
|
"Unsupported GTP version %"PRIu8"\n", version);
|
||||||
gtp_unsup_req(gsn, version, &peer, fd, buffer, status);
|
gtp_unsup_req(gsn, version, &peer, fd, buffer, status);
|
||||||
/*29.60: 11.1.1 */
|
/*29.60: 11.1.1 */
|
||||||
continue;
|
continue;
|
||||||
@@ -2894,10 +3021,10 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
|||||||
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
|
/* 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 */
|
/* GTP 1 messages. If GTP 0 message is received we silently discard */
|
||||||
/* the message */
|
/* the message */
|
||||||
if (((pheader->flags & 0xe0) < 0x20)) {
|
if (version < 1) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||||
status, "Unsupported GTP version\n");
|
"Unsupported GTP version %"PRIu8"\n", version);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2930,7 +3057,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
|||||||
/* Check for extension headers */
|
/* Check for extension headers */
|
||||||
/* TODO: We really should cycle through the headers and determine */
|
/* TODO: We really should cycle through the headers and determine */
|
||||||
/* if any have the comprehension required flag set */
|
/* if any have the comprehension required flag set */
|
||||||
if (((pheader->flags & 0x04) != 0x00)) {
|
if (((pheader->flags & GTP1HDR_F_EXT) != 0x00)) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status, "Unsupported extension header\n");
|
status, "Unsupported extension header\n");
|
||||||
@@ -2942,23 +3069,23 @@ int gtp_decaps1c(struct gsn_t *gsn)
|
|||||||
|
|
||||||
if ((gsn->mode == GTP_MODE_GGSN) &&
|
if ((gsn->mode == GTP_MODE_GGSN) &&
|
||||||
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
((pheader->type == GTP_CREATE_PDP_RSP) ||
|
||||||
(pheader->type == GTP_UPDATE_PDP_RSP) ||
|
(pheader->type == GTP_UPDATE_PDP_RSP))) {
|
||||||
(pheader->type == GTP_DELETE_PDP_RSP))) {
|
|
||||||
gsn->unexpect++;
|
gsn->unexpect++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status,
|
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 */
|
continue; /* Silently discard 29.60: 11.1.4 */
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gsn->mode == GTP_MODE_SGSN) &&
|
if ((gsn->mode == GTP_MODE_SGSN) &&
|
||||||
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
((pheader->type == GTP_CREATE_PDP_REQ) ||
|
||||||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
|
(pheader->type == GTP_UPDATE_PDP_REQ))) {
|
||||||
(pheader->type == GTP_DELETE_PDP_REQ))) {
|
|
||||||
gsn->unexpect++;
|
gsn->unexpect++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status,
|
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 */
|
continue; /* Silently discard 29.60: 11.1.4 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3019,7 +3146,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
|||||||
socklen_t peerlen;
|
socklen_t peerlen;
|
||||||
int status;
|
int status;
|
||||||
struct gtp1_header_short *pheader;
|
struct gtp1_header_short *pheader;
|
||||||
int version = 1; /* GTP version should be determined from header! */
|
uint8_t version;
|
||||||
int fd = gsn->fd1u;
|
int fd = gsn->fd1u;
|
||||||
|
|
||||||
/* TODO: Need strategy of userspace buffering and blocking */
|
/* TODO: Need strategy of userspace buffering and blocking */
|
||||||
@@ -3056,11 +3183,13 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
|||||||
|
|
||||||
pheader = (struct gtp1_header_short *)(buffer);
|
pheader = (struct gtp1_header_short *)(buffer);
|
||||||
|
|
||||||
|
version = GTPHDR_F_GET_VER(pheader->flags);
|
||||||
|
|
||||||
/* Version must be no larger than GTP 1 */
|
/* Version must be no larger than GTP 1 */
|
||||||
if (((pheader->flags & 0xe0) > 0x20)) {
|
if (version > 1) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||||
status, "Unsupported GTP version\n");
|
"Unsupported GTP version %"PRIu8"\n", version);
|
||||||
gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */
|
gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -3069,10 +3198,10 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
|||||||
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
|
/* 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 */
|
/* GTP 1 messages. If GTP 0 message is received we silently discard */
|
||||||
/* the message */
|
/* the message */
|
||||||
if (((pheader->flags & 0xe0) < 0x20)) {
|
if (version < 1) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
|
||||||
status, "Unsupported GTP version\n");
|
"Unsupported GTP version %"PRIu8"\n", version);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3105,7 +3234,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
|
|||||||
/* Check for extension headers */
|
/* Check for extension headers */
|
||||||
/* TODO: We really should cycle through the headers and determine */
|
/* TODO: We really should cycle through the headers and determine */
|
||||||
/* if any have the comprehension required flag set */
|
/* if any have the comprehension required flag set */
|
||||||
if (((pheader->flags & 0x04) != 0x00)) {
|
if (((pheader->flags & GTP1HDR_F_EXT) != 0x00)) {
|
||||||
gsn->unsup++;
|
gsn->unsup++;
|
||||||
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
|
||||||
status, "Unsupported extension header\n");
|
status, "Unsupported extension header\n");
|
||||||
@@ -3146,61 +3275,63 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
|
|||||||
{
|
{
|
||||||
union gtp_packet packet;
|
union gtp_packet packet;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
struct msghdr msgh;
|
||||||
|
struct iovec iov[2];
|
||||||
int fd;
|
int fd;
|
||||||
int length;
|
|
||||||
|
|
||||||
|
/* prepare destination address */
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||||
addr.sin_len = sizeof(addr);
|
addr.sin_len = sizeof(addr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(&addr.sin_addr, pdp->gsnru.v, pdp->gsnru.l); /* TODO range check */
|
memcpy(&addr.sin_addr, pdp->gsnru.v, pdp->gsnru.l); /* TODO range check */
|
||||||
|
|
||||||
|
/* prepare msghdr */
|
||||||
|
memset(&msgh, 0, sizeof(msgh));
|
||||||
|
msgh.msg_name = &addr;
|
||||||
|
msgh.msg_namelen = sizeof(addr);
|
||||||
|
msgh.msg_iov = iov;
|
||||||
|
msgh.msg_iovlen = ARRAY_SIZE(iov);
|
||||||
|
|
||||||
|
/* prepare iovectors */
|
||||||
|
iov[0].iov_base = &packet;
|
||||||
|
/* iov[0].iov_len is not known here yet */
|
||||||
|
iov[1].iov_base = pack;
|
||||||
|
iov[1].iov_len = len;
|
||||||
|
|
||||||
if (pdp->version == 0) {
|
if (pdp->version == 0) {
|
||||||
|
|
||||||
length = GTP0_HEADER_SIZE + len;
|
iov[0].iov_len = GTP0_HEADER_SIZE;
|
||||||
addr.sin_port = htons(GTP0_PORT);
|
addr.sin_port = htons(GTP0_PORT);
|
||||||
fd = gsn->fd0;
|
fd = gsn->fd0;
|
||||||
|
|
||||||
get_default_gtp(0, GTP_GPDU, &packet);
|
get_default_gtp(0, GTP_GPDU, &packet);
|
||||||
packet.gtp0.h.length = hton16(len);
|
packet.gtp0.h.length = hton16(len);
|
||||||
|
if (pdp->tx_gpdu_seq)
|
||||||
packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
|
packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
|
||||||
|
else
|
||||||
|
packet.gtp0.h.seq = 0;
|
||||||
packet.gtp0.h.flow = hton16(pdp->flru);
|
packet.gtp0.h.flow = hton16(pdp->flru);
|
||||||
packet.gtp0.h.tid = htobe64(pdp_gettid(pdp->imsi, pdp->nsapi));
|
packet.gtp0.h.tid = htobe64(pdp_gettid(pdp->imsi, pdp->nsapi));
|
||||||
|
|
||||||
if (len > sizeof(union gtp_packet) - sizeof(struct gtp0_header)) {
|
|
||||||
gsn->err_memcpy++;
|
|
||||||
LOGP(DLGTP, LOGL_ERROR,
|
|
||||||
"Memcpy failed: %u > %zu\n", len,
|
|
||||||
sizeof(union gtp_packet) -
|
|
||||||
sizeof(struct gtp0_header));
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
|
|
||||||
} else if (pdp->version == 1) {
|
} else if (pdp->version == 1) {
|
||||||
|
|
||||||
length = GTP1_HEADER_SIZE_LONG + len;
|
|
||||||
addr.sin_port = htons(GTP1U_PORT);
|
addr.sin_port = htons(GTP1U_PORT);
|
||||||
fd = gsn->fd1u;
|
fd = gsn->fd1u;
|
||||||
|
|
||||||
get_default_gtp(1, GTP_GPDU, &packet);
|
get_default_gtp(1, GTP_GPDU, &packet);
|
||||||
|
if (pdp->tx_gpdu_seq) {
|
||||||
|
packet.gtp1l.h.seq = hton16(pdp->gtpsntx++);
|
||||||
packet.gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT +
|
packet.gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT +
|
||||||
GTP1_HEADER_SIZE_LONG);
|
GTP1_HEADER_SIZE_LONG);
|
||||||
packet.gtp1l.h.seq = hton16(pdp->gtpsntx++);
|
|
||||||
packet.gtp1l.h.tei = hton32(pdp->teid_gn);
|
packet.gtp1l.h.tei = hton32(pdp->teid_gn);
|
||||||
|
iov[0].iov_len = GTP1_HEADER_SIZE_LONG;
|
||||||
if (len >
|
} else {
|
||||||
sizeof(union gtp_packet) -
|
packet.gtp1s.h.flags &= ~GTP1HDR_F_SEQ;
|
||||||
sizeof(struct gtp1_header_long)) {
|
packet.gtp1s.h.length = hton16(len);
|
||||||
gsn->err_memcpy++;
|
packet.gtp1s.h.tei = hton32(pdp->teid_gn);
|
||||||
LOGP(DLGTP, LOGL_ERROR,
|
iov[0].iov_len = GTP1_HEADER_SIZE_SHORT;
|
||||||
"Memcpy failed: %u > %zu\n", len,
|
|
||||||
sizeof(union gtp_packet) -
|
|
||||||
sizeof(struct gtp0_header));
|
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */
|
|
||||||
} else {
|
} else {
|
||||||
LOGP(DLGTP, LOGL_ERROR, "Unknown version: %d\n", pdp->version);
|
LOGP(DLGTP, LOGL_ERROR, "Unknown version: %d\n", pdp->version);
|
||||||
return EOF;
|
return EOF;
|
||||||
@@ -3211,11 +3342,10 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendto(fd, &packet, length, 0,
|
if (sendmsg(fd, &msgh, 0) < 0) {
|
||||||
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
||||||
gsn->err_sendto++;
|
gsn->err_sendto++;
|
||||||
LOGP(DLGTP, LOGL_ERROR,
|
LOGP(DLGTP, LOGL_ERROR,
|
||||||
"Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
|
"sendmsg(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
|
||||||
(unsigned long)&packet, GTP0_HEADER_SIZE + len,
|
(unsigned long)&packet, GTP0_HEADER_SIZE + len,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return EOF;
|
return EOF;
|
||||||
@@ -3227,15 +3357,6 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
|
|||||||
* Conversion functions
|
* 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
|
* IP address conversion functions
|
||||||
* There exist several types of address representations:
|
* There exist several types of address representations:
|
||||||
@@ -3251,8 +3372,8 @@ int char2ul_t(char *src, struct ul_t dst)
|
|||||||
|
|
||||||
int ipv42eua(struct ul66_t *eua, struct in_addr *src)
|
int ipv42eua(struct ul66_t *eua, struct in_addr *src)
|
||||||
{
|
{
|
||||||
eua->v[0] = 0xf1; /* IETF */
|
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||||
eua->v[1] = 0x21; /* IPv4 */
|
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||||
if (src) {
|
if (src) {
|
||||||
eua->l = 6;
|
eua->l = 6;
|
||||||
memcpy(&eua->v[2], src, 4);
|
memcpy(&eua->v[2], src, 4);
|
||||||
@@ -3264,7 +3385,7 @@ int ipv42eua(struct ul66_t *eua, struct in_addr *src)
|
|||||||
|
|
||||||
int eua2ipv4(struct in_addr *dst, struct ul66_t *eua)
|
int eua2ipv4(struct in_addr *dst, struct ul66_t *eua)
|
||||||
{
|
{
|
||||||
if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] = 0x21))
|
if ((eua->l != 6) || (eua->v[0] != PDP_EUA_ORG_IETF) || (eua->v[1] != PDP_EUA_TYPE_v4))
|
||||||
return -1; /* Not IPv4 address */
|
return -1; /* Not IPv4 address */
|
||||||
memcpy(dst, &eua->v[2], 4);
|
memcpy(dst, &eua->v[2], 4);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -3293,7 +3414,7 @@ int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src)
|
|||||||
* _network byte order_ to contain BCD digits ?!? */
|
* _network byte order_ to contain BCD digits ?!? */
|
||||||
const char *imsi_gtp2str(const uint64_t *imsi)
|
const char *imsi_gtp2str(const uint64_t *imsi)
|
||||||
{
|
{
|
||||||
static char buf[sizeof(*imsi)+1];
|
static char buf[sizeof(*imsi)*2+1];
|
||||||
const uint8_t *imsi8 = (const uint8_t *) imsi;
|
const uint8_t *imsi8 = (const uint8_t *) imsi;
|
||||||
unsigned int i, j = 0;
|
unsigned int i, j = 0;
|
||||||
|
|
||||||
|
|||||||
23
gtp/gtp.h
23
gtp/gtp.h
@@ -12,6 +12,9 @@
|
|||||||
#ifndef _GTP_H
|
#ifndef _GTP_H
|
||||||
#define _GTP_H
|
#define _GTP_H
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/defs.h>
|
||||||
|
|
||||||
#define GTP_MODE_GGSN 1
|
#define GTP_MODE_GGSN 1
|
||||||
#define GTP_MODE_SGSN 2
|
#define GTP_MODE_SGSN 2
|
||||||
|
|
||||||
@@ -85,6 +88,10 @@
|
|||||||
/* 242-254 For future use. */
|
/* 242-254 For future use. */
|
||||||
#define GTP_GPDU 255 /* G-PDU */
|
#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 */
|
/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
|
||||||
/* */
|
/* */
|
||||||
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
|
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
|
||||||
@@ -162,6 +169,13 @@ struct gtp0_header { /* Descriptions from 3GPP 09.60 */
|
|||||||
uint64_t tid; /* 13 Tunnel ID */
|
uint64_t tid; /* 13 Tunnel ID */
|
||||||
} __attribute__((packed)); /* 20 */
|
} __attribute__((packed)); /* 20 */
|
||||||
|
|
||||||
|
#define GTP1HDR_F_NPDU 0x01
|
||||||
|
#define GTP1HDR_F_SEQ 0x02
|
||||||
|
#define GTP1HDR_F_EXT 0x04
|
||||||
|
#define GTP1HDR_F_GTP1 0x10
|
||||||
|
#define GTPHDR_F_VER(n) ((n) << 5)
|
||||||
|
#define GTPHDR_F_GET_VER(flags) ((flags)>>5)
|
||||||
|
|
||||||
struct gtp1_header_short { /* Descriptions from 3GPP 29060 */
|
struct gtp1_header_short { /* Descriptions from 3GPP 29060 */
|
||||||
uint8_t flags; /* 01 bitfield, with typical values */
|
uint8_t flags; /* 01 bitfield, with typical values */
|
||||||
/* 001..... Version: 1 */
|
/* 001..... Version: 1 */
|
||||||
@@ -257,6 +271,7 @@ struct gsn_t {
|
|||||||
int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
|
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_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
|
||||||
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
|
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 */
|
/* Counters */
|
||||||
|
|
||||||
@@ -310,6 +325,9 @@ extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp,
|
|||||||
void *cbp, struct in_addr *inetaddr);
|
void *cbp, struct in_addr *inetaddr);
|
||||||
|
|
||||||
extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||||
|
void *cbp, int teardown)
|
||||||
|
OSMO_DEPRECATED("Use gtp_delete_context_req2() instead, to avoid freeing pdp ctx before reply");
|
||||||
|
extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||||
void *cbp, int teardown);
|
void *cbp, int teardown);
|
||||||
|
|
||||||
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||||
@@ -344,6 +362,11 @@ extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
|||||||
|
|
||||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||||
int (*cb) (struct sockaddr_in * peer,
|
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));
|
uint8_t recovery));
|
||||||
|
|
||||||
/* Internal functions (not part of the API */
|
/* Internal functions (not part of the API */
|
||||||
|
|||||||
337
gtp/gtpie.c
337
gtp/gtpie.c
@@ -39,8 +39,16 @@
|
|||||||
|
|
||||||
#include "gtpie.h"
|
#include "gtpie.h"
|
||||||
|
|
||||||
|
/*! Encode a TLV type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] l Length of value \a v in bytes
|
||||||
|
* \param[in] v Pointer to input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
int l, void *v)
|
int l, const void *v)
|
||||||
{
|
{
|
||||||
if ((*length + 3 + l) >= size)
|
if ((*length + 3 + l) >= size)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -51,8 +59,16 @@ int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode a TV0 (Tag + value) type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] l Length of value \a v in bytes
|
||||||
|
* \param[in] v Pointer to input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
int l, uint8_t * v)
|
int l, const uint8_t * v)
|
||||||
{
|
{
|
||||||
if ((*length + 1 + l) >= size)
|
if ((*length + 1 + l) >= size)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -62,6 +78,13 @@ int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode a TV1 (Tag + 8bit value) type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] v Input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
uint8_t v)
|
uint8_t v)
|
||||||
{
|
{
|
||||||
@@ -73,6 +96,13 @@ int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode a TV2 (Tag + 16bit value) type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] v Input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
uint16_t v)
|
uint16_t v)
|
||||||
{
|
{
|
||||||
@@ -84,6 +114,13 @@ int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode a TV4 (Tag + 32bit value) type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] v Input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
uint32_t v)
|
uint32_t v)
|
||||||
{
|
{
|
||||||
@@ -95,6 +132,13 @@ int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode a TV8 (Tag + 64bit value) type Information Element.
|
||||||
|
* \param[inout] p Pointer to output packet to which IE is appended
|
||||||
|
* \param[inout] length Up to which byte length is \a p used/filled
|
||||||
|
* \param[in] size Total size of \a p in bytes
|
||||||
|
* \param[in] t Tag / Information Element Identifier
|
||||||
|
* \param[in] v Input value
|
||||||
|
* \returns 0 on success, 1 on error */
|
||||||
int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||||
uint64_t v)
|
uint64_t v)
|
||||||
{
|
{
|
||||||
@@ -106,6 +150,11 @@ int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain a GTP IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \returns index into \a ie on success; -1 if not found */
|
||||||
int gtpie_getie(union gtpie_member *ie[], int type, int instance)
|
int gtpie_getie(union gtpie_member *ie[], int type, int instance)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
@@ -118,6 +167,11 @@ int gtpie_getie(union gtpie_member *ie[], int type, int instance)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Determine if IE for a given tag/IEI exists in a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \returns 1 if IEI instance present in \a ie; 0 if not */
|
||||||
int gtpie_exist(union gtpie_member *ie[], int type, int instance)
|
int gtpie_exist(union gtpie_member *ie[], int type, int instance)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
@@ -130,6 +184,14 @@ int gtpie_exist(union gtpie_member *ie[], int type, int instance)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TLV-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[out] length Length of IE
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \param[in] size Size of \a dst in bytes
|
||||||
|
* \returns 0 on sucess; EOF in case value is larger than \a size */
|
||||||
int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
||||||
unsigned int *length, void *dst, unsigned int size)
|
unsigned int *length, void *dst, unsigned int size)
|
||||||
{
|
{
|
||||||
@@ -145,6 +207,13 @@ int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TV0-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \param[in] size Size of value in bytes
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
||||||
void *dst, unsigned int size)
|
void *dst, unsigned int size)
|
||||||
{
|
{
|
||||||
@@ -157,6 +226,12 @@ int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TV1-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
||||||
uint8_t * dst)
|
uint8_t * dst)
|
||||||
{
|
{
|
||||||
@@ -169,6 +244,12 @@ int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TV2-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
||||||
uint16_t * dst)
|
uint16_t * dst)
|
||||||
{
|
{
|
||||||
@@ -181,6 +262,12 @@ int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TV4-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
||||||
uint32_t * dst)
|
uint32_t * dst)
|
||||||
{
|
{
|
||||||
@@ -193,6 +280,12 @@ int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Obtain Value of TV8-type IE for a given tag/IEI from a list/array.
|
||||||
|
* \param[in] ie Array of GTPIE
|
||||||
|
* \param[in] type Tag/IEI for which we're looking
|
||||||
|
* \param[in] instance Instance (number of occurence) of this IEI
|
||||||
|
* \param[inout] dst Caller-allocated buffer where to store value
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
||||||
uint64_t * dst)
|
uint64_t * dst)
|
||||||
{
|
{
|
||||||
@@ -205,13 +298,19 @@ int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
|
/*! Parse an incoming GTP packet into its Information Elements.
|
||||||
|
* \param[out] ie Caller-allocated Array of GTPIE
|
||||||
|
* \param[in] version GTP protocol version
|
||||||
|
* \param[in] pack Pointer to raw GTP packet (payload part)
|
||||||
|
* \param[in] len Length of \a pack in bytes
|
||||||
|
* \returns 0 on sucess; EOF in case IE not found */
|
||||||
|
int gtpie_decaps(union gtpie_member *ie[], int version, const void *pack,
|
||||||
unsigned len)
|
unsigned len)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
unsigned char *p;
|
const unsigned char *p;
|
||||||
unsigned char *end;
|
const unsigned char *end;
|
||||||
|
|
||||||
end = (unsigned char *)pack + len;
|
end = (unsigned char *)pack + len;
|
||||||
p = pack;
|
p = pack;
|
||||||
@@ -391,11 +490,76 @@ int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
|
|||||||
case GTPIE_RAB_SETUP:
|
case GTPIE_RAB_SETUP:
|
||||||
case GTPIE_TRIGGER_ID:
|
case GTPIE_TRIGGER_ID:
|
||||||
case GTPIE_OMC_ID:
|
case GTPIE_OMC_ID:
|
||||||
case GTPIE_CHARGING_ADDR:
|
case GTPIE_RAN_T_CONTAIN:
|
||||||
|
case GTPIE_PDP_CTX_PRIO:
|
||||||
|
case GTPIE_ADDL_RAB_S_I:
|
||||||
|
case GTPIE_SGSN_NUMBER:
|
||||||
|
case GTPIE_COMMON_FLAGS:
|
||||||
|
case GTPIE_APN_RESTR:
|
||||||
|
case GTPIE_R_PRIO_LCS:
|
||||||
case GTPIE_RAT_TYPE:
|
case GTPIE_RAT_TYPE:
|
||||||
case GTPIE_USER_LOC:
|
case GTPIE_USER_LOC:
|
||||||
case GTPIE_MS_TZ:
|
case GTPIE_MS_TZ:
|
||||||
case GTPIE_IMEI_SV:
|
case GTPIE_IMEI_SV:
|
||||||
|
case GTPIE_CML_CHG_I_CT:
|
||||||
|
case GTPIE_MBMS_UE_CTX:
|
||||||
|
case GTPIE_TMGI:
|
||||||
|
case GTPIE_RIM_ROUT_ADDR:
|
||||||
|
case GTPIE_MBMS_PCO:
|
||||||
|
case GTPIE_MBMS_SA:
|
||||||
|
case GTPIE_SRNC_PDCP_CTX:
|
||||||
|
case GTPIE_ADDL_TRACE:
|
||||||
|
case GTPIE_HOP_CTR:
|
||||||
|
case GTPIE_SEL_PLMN_ID:
|
||||||
|
case GTPIE_MBMS_SESS_ID:
|
||||||
|
case GTPIE_MBMS_2_3G_IND:
|
||||||
|
case GTPIE_ENH_NSAPI:
|
||||||
|
case GTPIE_MBMS_SESS_DUR:
|
||||||
|
case GTPIE_A_MBMS_TRAC_I:
|
||||||
|
case GTPIE_MBMS_S_REP_N:
|
||||||
|
case GTPIE_MBMS_TTDT:
|
||||||
|
case GTPIE_PS_HO_REQ_CTX:
|
||||||
|
case GTPIE_BSS_CONTAINER:
|
||||||
|
case GTPIE_CELL_ID:
|
||||||
|
case GTPIE_PDU_NUMBERS:
|
||||||
|
case GTPIE_BSSGP_CAUSE:
|
||||||
|
case GTPIE_RQD_MBMS_BCAP:
|
||||||
|
case GTPIE_RIM_RA_DISCR:
|
||||||
|
case GTPIE_L_SETUP_PFCS:
|
||||||
|
case GTPIE_PS_HO_XID_PAR:
|
||||||
|
case GTPIE_MS_CHG_REP_A:
|
||||||
|
case GTPIE_DIR_TUN_FLAGS:
|
||||||
|
case GTPIE_CORREL_ID:
|
||||||
|
case GTPIE_MBMS_FLOWI:
|
||||||
|
case GTPIE_MBMS_MC_DIST:
|
||||||
|
case GTPIE_MBMS_DIST_ACK:
|
||||||
|
case GTPIE_R_IRAT_HO_INF:
|
||||||
|
case GTPIE_RFSP_IDX:
|
||||||
|
case GTPIE_FQDN:
|
||||||
|
case GTPIE_E_ALL_PRIO_1:
|
||||||
|
case GTPIE_E_ALL_PRIO_2:
|
||||||
|
case GTPIE_E_CMN_FLAGS:
|
||||||
|
case GTPIE_U_CSG_INFO:
|
||||||
|
case GTPIE_CSG_I_REP_ACT:
|
||||||
|
case GTPIE_CSG_ID:
|
||||||
|
case GTPIE_CSG_MEMB_IND:
|
||||||
|
case GTPIE_AMBR:
|
||||||
|
case GTPIE_UE_NET_CAPA:
|
||||||
|
case GTPIE_UE_AMBR:
|
||||||
|
case GTPIE_APN_AMBR_NS:
|
||||||
|
case GTPIE_GGSN_BACKOFF:
|
||||||
|
case GTPIE_S_PRIO_IND:
|
||||||
|
case GTPIE_S_PRIO_IND_NS:
|
||||||
|
case GTPIE_H_BR_16MBPS_F:
|
||||||
|
case GTPIE_A_MMCTX_SRVCC:
|
||||||
|
case GTPIE_A_FLAGS_SRVCC:
|
||||||
|
case GTPIE_STN_SR:
|
||||||
|
case GTPIE_C_MSISDN:
|
||||||
|
case GTPIE_E_RANAP_CAUSE:
|
||||||
|
case GTPIE_ENODEB_ID:
|
||||||
|
case GTPIE_SEL_MODE_NS:
|
||||||
|
case GTPIE_ULI_TIMESTAMP:
|
||||||
|
case GTPIE_CHARGING_ADDR:
|
||||||
case GTPIE_PRIVATE:
|
case GTPIE_PRIVATE:
|
||||||
if (j < GTPIE_SIZE) {
|
if (j < GTPIE_SIZE) {
|
||||||
ie[j] = (union gtpie_member *)p;
|
ie[j] = (union gtpie_member *)p;
|
||||||
@@ -430,6 +594,11 @@ int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode GTP packet payload from Array of Information Elements.
|
||||||
|
* \param[out] ie Input Array of GTPIE
|
||||||
|
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
|
||||||
|
* \param[out] len Encoded length of \a pack in bytes
|
||||||
|
* \returns 0 on sucess; 2 for out-of-space */
|
||||||
int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -483,11 +652,13 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
|||||||
case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */
|
case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */
|
||||||
iesize = 6;
|
iesize = 6;
|
||||||
break;
|
break;
|
||||||
|
case GTPIE_RAI: /* TV GTPIE types with value length 6 */
|
||||||
|
iesize = 7;
|
||||||
|
break;
|
||||||
case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */
|
case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */
|
||||||
iesize = 8;
|
iesize = 8;
|
||||||
break;
|
break;
|
||||||
case GTPIE_IMSI: /* TV GTPIE types with value length 8 */
|
case GTPIE_IMSI: /* TV GTPIE types with value length 8 */
|
||||||
case GTPIE_RAI:
|
|
||||||
iesize = 9;
|
iesize = 9;
|
||||||
break;
|
break;
|
||||||
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
|
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
|
||||||
@@ -511,6 +682,75 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
|||||||
case GTPIE_RAB_SETUP:
|
case GTPIE_RAB_SETUP:
|
||||||
case GTPIE_TRIGGER_ID:
|
case GTPIE_TRIGGER_ID:
|
||||||
case GTPIE_OMC_ID:
|
case GTPIE_OMC_ID:
|
||||||
|
case GTPIE_RAN_T_CONTAIN:
|
||||||
|
case GTPIE_PDP_CTX_PRIO:
|
||||||
|
case GTPIE_ADDL_RAB_S_I:
|
||||||
|
case GTPIE_SGSN_NUMBER:
|
||||||
|
case GTPIE_COMMON_FLAGS:
|
||||||
|
case GTPIE_APN_RESTR:
|
||||||
|
case GTPIE_R_PRIO_LCS:
|
||||||
|
case GTPIE_RAT_TYPE:
|
||||||
|
case GTPIE_USER_LOC:
|
||||||
|
case GTPIE_MS_TZ:
|
||||||
|
case GTPIE_IMEI_SV:
|
||||||
|
case GTPIE_CML_CHG_I_CT:
|
||||||
|
case GTPIE_MBMS_UE_CTX:
|
||||||
|
case GTPIE_TMGI:
|
||||||
|
case GTPIE_RIM_ROUT_ADDR:
|
||||||
|
case GTPIE_MBMS_PCO:
|
||||||
|
case GTPIE_MBMS_SA:
|
||||||
|
case GTPIE_SRNC_PDCP_CTX:
|
||||||
|
case GTPIE_ADDL_TRACE:
|
||||||
|
case GTPIE_HOP_CTR:
|
||||||
|
case GTPIE_SEL_PLMN_ID:
|
||||||
|
case GTPIE_MBMS_SESS_ID:
|
||||||
|
case GTPIE_MBMS_2_3G_IND:
|
||||||
|
case GTPIE_ENH_NSAPI:
|
||||||
|
case GTPIE_MBMS_SESS_DUR:
|
||||||
|
case GTPIE_A_MBMS_TRAC_I:
|
||||||
|
case GTPIE_MBMS_S_REP_N:
|
||||||
|
case GTPIE_MBMS_TTDT:
|
||||||
|
case GTPIE_PS_HO_REQ_CTX:
|
||||||
|
case GTPIE_BSS_CONTAINER:
|
||||||
|
case GTPIE_CELL_ID:
|
||||||
|
case GTPIE_PDU_NUMBERS:
|
||||||
|
case GTPIE_BSSGP_CAUSE:
|
||||||
|
case GTPIE_RQD_MBMS_BCAP:
|
||||||
|
case GTPIE_RIM_RA_DISCR:
|
||||||
|
case GTPIE_L_SETUP_PFCS:
|
||||||
|
case GTPIE_PS_HO_XID_PAR:
|
||||||
|
case GTPIE_MS_CHG_REP_A:
|
||||||
|
case GTPIE_DIR_TUN_FLAGS:
|
||||||
|
case GTPIE_CORREL_ID:
|
||||||
|
case GTPIE_MBMS_FLOWI:
|
||||||
|
case GTPIE_MBMS_MC_DIST:
|
||||||
|
case GTPIE_MBMS_DIST_ACK:
|
||||||
|
case GTPIE_R_IRAT_HO_INF:
|
||||||
|
case GTPIE_RFSP_IDX:
|
||||||
|
case GTPIE_FQDN:
|
||||||
|
case GTPIE_E_ALL_PRIO_1:
|
||||||
|
case GTPIE_E_ALL_PRIO_2:
|
||||||
|
case GTPIE_E_CMN_FLAGS:
|
||||||
|
case GTPIE_U_CSG_INFO:
|
||||||
|
case GTPIE_CSG_I_REP_ACT:
|
||||||
|
case GTPIE_CSG_ID:
|
||||||
|
case GTPIE_CSG_MEMB_IND:
|
||||||
|
case GTPIE_AMBR:
|
||||||
|
case GTPIE_UE_NET_CAPA:
|
||||||
|
case GTPIE_UE_AMBR:
|
||||||
|
case GTPIE_APN_AMBR_NS:
|
||||||
|
case GTPIE_GGSN_BACKOFF:
|
||||||
|
case GTPIE_S_PRIO_IND:
|
||||||
|
case GTPIE_S_PRIO_IND_NS:
|
||||||
|
case GTPIE_H_BR_16MBPS_F:
|
||||||
|
case GTPIE_A_MMCTX_SRVCC:
|
||||||
|
case GTPIE_A_FLAGS_SRVCC:
|
||||||
|
case GTPIE_STN_SR:
|
||||||
|
case GTPIE_C_MSISDN:
|
||||||
|
case GTPIE_E_RANAP_CAUSE:
|
||||||
|
case GTPIE_ENODEB_ID:
|
||||||
|
case GTPIE_SEL_MODE_NS:
|
||||||
|
case GTPIE_ULI_TIMESTAMP:
|
||||||
case GTPIE_CHARGING_ADDR:
|
case GTPIE_CHARGING_ADDR:
|
||||||
case GTPIE_PRIVATE:
|
case GTPIE_PRIVATE:
|
||||||
iesize = 3 + hton16(ie[i]->tlv.l);
|
iesize = 3 + hton16(ie[i]->tlv.l);
|
||||||
@@ -528,6 +768,12 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode GTP packet payload from Array of Information Elements.
|
||||||
|
* \param[out] ie Input Array of GTPIE
|
||||||
|
* \param[in] size Size of ?
|
||||||
|
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
|
||||||
|
* \param[out] len Encoded length of \a pack in bytes
|
||||||
|
* \returns 0 on sucess; 2 for out-of-space */
|
||||||
int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
||||||
void *pack, unsigned *len)
|
void *pack, unsigned *len)
|
||||||
{
|
{
|
||||||
@@ -577,16 +823,19 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
|||||||
case GTPIE_P_TMSI:
|
case GTPIE_P_TMSI:
|
||||||
case GTPIE_TEI_DI:
|
case GTPIE_TEI_DI:
|
||||||
case GTPIE_TEI_C:
|
case GTPIE_TEI_C:
|
||||||
|
case GTPIE_CHARGING_ID:
|
||||||
iesize = 5;
|
iesize = 5;
|
||||||
break;
|
break;
|
||||||
case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */
|
case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */
|
||||||
iesize = 6;
|
iesize = 6;
|
||||||
break;
|
break;
|
||||||
|
case GTPIE_RAI: /* TV GTPIE types with value length 6 */
|
||||||
|
iesize = 7;
|
||||||
|
break;
|
||||||
case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */
|
case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */
|
||||||
iesize = 8;
|
iesize = 8;
|
||||||
break;
|
break;
|
||||||
case GTPIE_IMSI: /* TV GTPIE types with value length 8 */
|
case GTPIE_IMSI: /* TV GTPIE types with value length 8 */
|
||||||
case GTPIE_RAI:
|
|
||||||
iesize = 9;
|
iesize = 9;
|
||||||
break;
|
break;
|
||||||
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
|
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
|
||||||
@@ -595,8 +844,7 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
|||||||
case GTPIE_EXT_HEADER_T: /* GTP extension header */
|
case GTPIE_EXT_HEADER_T: /* GTP extension header */
|
||||||
iesize = 2 + hton8(ie[i].ext.l);
|
iesize = 2 + hton8(ie[i].ext.l);
|
||||||
break;
|
break;
|
||||||
case GTPIE_CHARGING_ID: /* TLV GTPIE types with length length 2 */
|
case GTPIE_EUA: /* TLV GTPIE types with length length 2 */
|
||||||
case GTPIE_EUA:
|
|
||||||
case GTPIE_MM_CONTEXT:
|
case GTPIE_MM_CONTEXT:
|
||||||
case GTPIE_PDP_CONTEXT:
|
case GTPIE_PDP_CONTEXT:
|
||||||
case GTPIE_APN:
|
case GTPIE_APN:
|
||||||
@@ -611,6 +859,75 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
|||||||
case GTPIE_RAB_SETUP:
|
case GTPIE_RAB_SETUP:
|
||||||
case GTPIE_TRIGGER_ID:
|
case GTPIE_TRIGGER_ID:
|
||||||
case GTPIE_OMC_ID:
|
case GTPIE_OMC_ID:
|
||||||
|
case GTPIE_RAN_T_CONTAIN:
|
||||||
|
case GTPIE_PDP_CTX_PRIO:
|
||||||
|
case GTPIE_ADDL_RAB_S_I:
|
||||||
|
case GTPIE_SGSN_NUMBER:
|
||||||
|
case GTPIE_COMMON_FLAGS:
|
||||||
|
case GTPIE_APN_RESTR:
|
||||||
|
case GTPIE_R_PRIO_LCS:
|
||||||
|
case GTPIE_RAT_TYPE:
|
||||||
|
case GTPIE_USER_LOC:
|
||||||
|
case GTPIE_MS_TZ:
|
||||||
|
case GTPIE_IMEI_SV:
|
||||||
|
case GTPIE_CML_CHG_I_CT:
|
||||||
|
case GTPIE_MBMS_UE_CTX:
|
||||||
|
case GTPIE_TMGI:
|
||||||
|
case GTPIE_RIM_ROUT_ADDR:
|
||||||
|
case GTPIE_MBMS_PCO:
|
||||||
|
case GTPIE_MBMS_SA:
|
||||||
|
case GTPIE_SRNC_PDCP_CTX:
|
||||||
|
case GTPIE_ADDL_TRACE:
|
||||||
|
case GTPIE_HOP_CTR:
|
||||||
|
case GTPIE_SEL_PLMN_ID:
|
||||||
|
case GTPIE_MBMS_SESS_ID:
|
||||||
|
case GTPIE_MBMS_2_3G_IND:
|
||||||
|
case GTPIE_ENH_NSAPI:
|
||||||
|
case GTPIE_MBMS_SESS_DUR:
|
||||||
|
case GTPIE_A_MBMS_TRAC_I:
|
||||||
|
case GTPIE_MBMS_S_REP_N:
|
||||||
|
case GTPIE_MBMS_TTDT:
|
||||||
|
case GTPIE_PS_HO_REQ_CTX:
|
||||||
|
case GTPIE_BSS_CONTAINER:
|
||||||
|
case GTPIE_CELL_ID:
|
||||||
|
case GTPIE_PDU_NUMBERS:
|
||||||
|
case GTPIE_BSSGP_CAUSE:
|
||||||
|
case GTPIE_RQD_MBMS_BCAP:
|
||||||
|
case GTPIE_RIM_RA_DISCR:
|
||||||
|
case GTPIE_L_SETUP_PFCS:
|
||||||
|
case GTPIE_PS_HO_XID_PAR:
|
||||||
|
case GTPIE_MS_CHG_REP_A:
|
||||||
|
case GTPIE_DIR_TUN_FLAGS:
|
||||||
|
case GTPIE_CORREL_ID:
|
||||||
|
case GTPIE_MBMS_FLOWI:
|
||||||
|
case GTPIE_MBMS_MC_DIST:
|
||||||
|
case GTPIE_MBMS_DIST_ACK:
|
||||||
|
case GTPIE_R_IRAT_HO_INF:
|
||||||
|
case GTPIE_RFSP_IDX:
|
||||||
|
case GTPIE_FQDN:
|
||||||
|
case GTPIE_E_ALL_PRIO_1:
|
||||||
|
case GTPIE_E_ALL_PRIO_2:
|
||||||
|
case GTPIE_E_CMN_FLAGS:
|
||||||
|
case GTPIE_U_CSG_INFO:
|
||||||
|
case GTPIE_CSG_I_REP_ACT:
|
||||||
|
case GTPIE_CSG_ID:
|
||||||
|
case GTPIE_CSG_MEMB_IND:
|
||||||
|
case GTPIE_AMBR:
|
||||||
|
case GTPIE_UE_NET_CAPA:
|
||||||
|
case GTPIE_UE_AMBR:
|
||||||
|
case GTPIE_APN_AMBR_NS:
|
||||||
|
case GTPIE_GGSN_BACKOFF:
|
||||||
|
case GTPIE_S_PRIO_IND:
|
||||||
|
case GTPIE_S_PRIO_IND_NS:
|
||||||
|
case GTPIE_H_BR_16MBPS_F:
|
||||||
|
case GTPIE_A_MMCTX_SRVCC:
|
||||||
|
case GTPIE_A_FLAGS_SRVCC:
|
||||||
|
case GTPIE_STN_SR:
|
||||||
|
case GTPIE_C_MSISDN:
|
||||||
|
case GTPIE_E_RANAP_CAUSE:
|
||||||
|
case GTPIE_ENODEB_ID:
|
||||||
|
case GTPIE_SEL_MODE_NS:
|
||||||
|
case GTPIE_ULI_TIMESTAMP:
|
||||||
case GTPIE_CHARGING_ADDR:
|
case GTPIE_CHARGING_ADDR:
|
||||||
case GTPIE_PRIVATE:
|
case GTPIE_PRIVATE:
|
||||||
iesize = 3 + hton16(ie[i].tlv.l);
|
iesize = 3 + hton16(ie[i].tlv.l);
|
||||||
|
|||||||
83
gtp/gtpie.h
83
gtp/gtpie.h
@@ -12,6 +12,8 @@
|
|||||||
#ifndef _GTPIE_H
|
#ifndef _GTPIE_H
|
||||||
#define _GTPIE_H
|
#define _GTPIE_H
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
/* Macroes for conversion between host and network byte order */
|
/* Macroes for conversion between host and network byte order */
|
||||||
#define hton8(x) (x)
|
#define hton8(x) (x)
|
||||||
#define ntoh8(x) (x)
|
#define ntoh8(x) (x)
|
||||||
@@ -48,7 +50,7 @@ static __inline uint64_t hton64(uint64_t q)
|
|||||||
|
|
||||||
#define GTPIE_DEBUG 0 /* Print debug information */
|
#define GTPIE_DEBUG 0 /* Print debug information */
|
||||||
|
|
||||||
/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
|
/* GTP Information elements from 29.060 v11.8.0 7.7 Information Elements */
|
||||||
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
|
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
|
||||||
* by 135: QOS Profile in version 1 */
|
* by 135: QOS Profile in version 1 */
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ static __inline uint64_t hton64(uint64_t q)
|
|||||||
#define GTPIE_TRACE_TYPE 28 /* Trace Type 2 */
|
#define GTPIE_TRACE_TYPE 28 /* Trace Type 2 */
|
||||||
#define GTPIE_MS_NOT_REACH 29 /* MS Not Reachable Reason 1 */
|
#define GTPIE_MS_NOT_REACH 29 /* MS Not Reachable Reason 1 */
|
||||||
/* 30-116 UNUSED */
|
/* 30-116 UNUSED */
|
||||||
/* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
|
/* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
|
||||||
#define GTPIE_CHARGING_ID 127 /* Charging ID 4 */
|
#define GTPIE_CHARGING_ID 127 /* Charging ID 4 */
|
||||||
#define GTPIE_EUA 128 /* End User Address */
|
#define GTPIE_EUA 128 /* End User Address */
|
||||||
#define GTPIE_MM_CONTEXT 129 /* MM Context */
|
#define GTPIE_MM_CONTEXT 129 /* MM Context */
|
||||||
@@ -102,14 +104,81 @@ static __inline uint64_t hton64(uint64_t q)
|
|||||||
#define GTPIE_EXT_HEADER_T 141 /* Extension Header Type List */
|
#define GTPIE_EXT_HEADER_T 141 /* Extension Header Type List */
|
||||||
#define GTPIE_TRIGGER_ID 142 /* Trigger Id */
|
#define GTPIE_TRIGGER_ID 142 /* Trigger Id */
|
||||||
#define GTPIE_OMC_ID 143 /* OMC Identity */
|
#define GTPIE_OMC_ID 143 /* OMC Identity */
|
||||||
|
#define GTPIE_RAN_T_CONTAIN 144 /* RAN Transparent Container */
|
||||||
|
#define GTPIE_PDP_CTX_PRIO 145 /* PDP Context Prioritization */
|
||||||
|
#define GTPIE_ADDL_RAB_S_I 146 /* Additional RAB Setup Information */
|
||||||
|
#define GTPIE_SGSN_NUMBER 147 /* SGSN Number */
|
||||||
|
#define GTPIE_COMMON_FLAGS 148 /* Common Flags */
|
||||||
|
#define GTPIE_APN_RESTR 149 /* APN Restriction */
|
||||||
|
#define GTPIE_R_PRIO_LCS 150 /* Radio Priority LCS */
|
||||||
#define GTPIE_RAT_TYPE 151 /* Radio Access Technology Type */
|
#define GTPIE_RAT_TYPE 151 /* Radio Access Technology Type */
|
||||||
#define GTPIE_USER_LOC 152 /* User Location Information */
|
#define GTPIE_USER_LOC 152 /* User Location Information */
|
||||||
#define GTPIE_MS_TZ 153 /* MS Time Zone */
|
#define GTPIE_MS_TZ 153 /* MS Time Zone */
|
||||||
#define GTPIE_IMEI_SV 154 /* IMEI Software Version */
|
#define GTPIE_IMEI_SV 154 /* IMEI Software Version */
|
||||||
|
#define GTPIE_CML_CHG_I_CT 155 /* CAMEL Charging Information Container */
|
||||||
|
#define GTPIE_MBMS_UE_CTX 156 /* MSMS UE Context */
|
||||||
|
#define GTPIE_TMGI 157 /* Temporary Mobile Group Identity (TMGI) */
|
||||||
|
#define GTPIE_RIM_ROUT_ADDR 158 /* RIM Routing Address */
|
||||||
|
#define GTPIE_MBMS_PCO 159 /* MBMS Protocol Configuratin Options */
|
||||||
|
#define GTPIE_MBMS_SA 160 /* MBMS Service Area */
|
||||||
|
#define GTPIE_SRNC_PDCP_CTX 161 /* Source RNC PDCP Context Info */
|
||||||
|
#define GTPIE_ADDL_TRACE 162 /* Additional Trace Info */
|
||||||
|
#define GTPIE_HOP_CTR 163 /* Hop Counter */
|
||||||
|
#define GTPIE_SEL_PLMN_ID 164 /* Selected PLMN ID */
|
||||||
|
#define GTPIE_MBMS_SESS_ID 165 /* MBMS Session Identifier */
|
||||||
|
#define GTPIE_MBMS_2_3G_IND 166 /* MBMS 2G/3G Indicator */
|
||||||
|
#define GTPIE_ENH_NSAPI 167 /* Enhanced NSAPI */
|
||||||
|
#define GTPIE_MBMS_SESS_DUR 168 /* MBMS Session Duration */
|
||||||
|
#define GTPIE_A_MBMS_TRAC_I 169 /* Additional MBMS Trace Info */
|
||||||
|
#define GTPIE_MBMS_S_REP_N 170 /* MBMS Session Repetition Number */
|
||||||
|
#define GTPIE_MBMS_TTDT 171 /* MBMS Time To Data Transfer */
|
||||||
|
#define GTPIE_PS_HO_REQ_CTX 172 /* PS Handover Request Context */
|
||||||
|
#define GTPIE_BSS_CONTAINER 173 /* BSS Container */
|
||||||
|
#define GTPIE_CELL_ID 174 /* Cell Identification */
|
||||||
|
#define GTPIE_PDU_NUMBERS 175 /* PDU Numbers */
|
||||||
|
#define GTPIE_BSSGP_CAUSE 176 /* BSSGP Cause */
|
||||||
|
#define GTPIE_RQD_MBMS_BCAP 177 /* Required MBMS Bearer Capabilities */
|
||||||
|
#define GTPIE_RIM_RA_DISCR 178 /* RIM Routing Address Discriminator */
|
||||||
|
#define GTPIE_L_SETUP_PFCS 179 /* List of set-up PFCs */
|
||||||
|
#define GTPIE_PS_HO_XID_PAR 180 /* PS Handover XID Parameters */
|
||||||
|
#define GTPIE_MS_CHG_REP_A 181 /* MS Info Change Reporting Action */
|
||||||
|
#define GTPIE_DIR_TUN_FLAGS 182 /* Direct Tunnel Flags */
|
||||||
|
#define GTPIE_CORREL_ID 183 /* Correlation-ID */
|
||||||
#define GTPIE_BCM 184 /* Bearer control mode */
|
#define GTPIE_BCM 184 /* Bearer control mode */
|
||||||
/* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
|
#define GTPIE_MBMS_FLOWI 185 /* MBMS Flow Identifier */
|
||||||
|
#define GTPIE_MBMS_MC_DIST 186 /* MBMS IP Multicast Distribution */
|
||||||
|
#define GTPIE_MBMS_DIST_ACK 187 /* MBMS Distribution Acknowledgement */
|
||||||
|
#define GTPIE_R_IRAT_HO_INF 188 /* Reliable INTER RAT HANDOVER INFO */
|
||||||
|
#define GTPIE_RFSP_IDX 189 /* RFSP Index */
|
||||||
|
#define GTPIE_FQDN 190 /* FQDN */
|
||||||
|
#define GTPIE_E_ALL_PRIO_1 191 /* Evolvd Allocation/Retention Priority I */
|
||||||
|
#define GTPIE_E_ALL_PRIO_2 192 /* Evolvd Allocation/Retention Priority II */
|
||||||
|
#define GTPIE_E_CMN_FLAGS 193 /* Extended Common Flags */
|
||||||
|
#define GTPIE_U_CSG_INFO 194 /* User CSG Information (UCI) */
|
||||||
|
#define GTPIE_CSG_I_REP_ACT 195 /* CSG Information Reporting Action */
|
||||||
|
#define GTPIE_CSG_ID 196 /* CSG ID */
|
||||||
|
#define GTPIE_CSG_MEMB_IND 197 /* CSG Membership Indication (CMI) */
|
||||||
|
#define GTPIE_AMBR 198 /* Aggregate Maximum Bit Rate (AMBR) */
|
||||||
|
#define GTPIE_UE_NET_CAPA 199 /* UE Network Capability */
|
||||||
|
#define GTPIE_UE_AMBR 200 /* UE-AMBR */
|
||||||
|
#define GTPIE_APN_AMBR_NS 201 /* APN-AMBR with NSAPI */
|
||||||
|
#define GTPIE_GGSN_BACKOFF 202 /* GGSN Back-Off Time */
|
||||||
|
#define GTPIE_S_PRIO_IND 203 /* Signalling Priority Indication */
|
||||||
|
#define GTPIE_S_PRIO_IND_NS 204 /* Signalling Priority Indication with NSAPI */
|
||||||
|
#define GTPIE_H_BR_16MBPS_F 205 /* Higher Bitrates than 16 Mbps flag */
|
||||||
|
/* 206: Reserved */
|
||||||
|
#define GTPIE_A_MMCTX_SRVCC 207 /* Additional MM context for SRVCC */
|
||||||
|
#define GTPIE_A_FLAGS_SRVCC 208 /* Additional flags fro SRVC */
|
||||||
|
#define GTPIE_STN_SR 209 /* STN-SR */
|
||||||
|
#define GTPIE_C_MSISDN 210 /* C-MSISDN */
|
||||||
|
#define GTPIE_E_RANAP_CAUSE 211 /* Extended RANAP Cause */
|
||||||
|
#define GTPIE_ENODEB_ID 212 /* eNodeB ID */
|
||||||
|
#define GTPIE_SEL_MODE_NS 213 /* Selection Mode with NSAPI */
|
||||||
|
#define GTPIE_ULI_TIMESTAMP 214 /* ULI Timestamp */
|
||||||
|
/* 215-238 Spare. For future use */
|
||||||
|
/* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
|
||||||
#define GTPIE_CHARGING_ADDR 251 /* Charging Gateway Address */
|
#define GTPIE_CHARGING_ADDR 251 /* Charging Gateway Address */
|
||||||
/* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
|
/* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
|
||||||
#define GTPIE_PRIVATE 255 /* Private Extension */
|
#define GTPIE_PRIVATE 255 /* Private Extension */
|
||||||
|
|
||||||
/* GTP information element structs in network order */
|
/* GTP information element structs in network order */
|
||||||
@@ -221,9 +290,9 @@ struct tlv2 {
|
|||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
extern int gtpie_tlv(void *p, unsigned int *length, unsigned int size,
|
extern int gtpie_tlv(void *p, unsigned int *length, unsigned int size,
|
||||||
uint8_t t, int l, void *v);
|
uint8_t t, int l, const void *v);
|
||||||
extern int gtpie_tv0(void *p, unsigned int *length, unsigned int size,
|
extern int gtpie_tv0(void *p, unsigned int *length, unsigned int size,
|
||||||
uint8_t t, int l, uint8_t * v);
|
uint8_t t, int l, const uint8_t * v);
|
||||||
extern int gtpie_tv1(void *p, unsigned int *length, unsigned int size,
|
extern int gtpie_tv1(void *p, unsigned int *length, unsigned int size,
|
||||||
uint8_t t, uint8_t v);
|
uint8_t t, uint8_t v);
|
||||||
extern int gtpie_tv2(void *p, unsigned int *length, unsigned int size,
|
extern int gtpie_tv2(void *p, unsigned int *length, unsigned int size,
|
||||||
@@ -248,7 +317,7 @@ extern int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
|||||||
uint64_t * dst);
|
uint64_t * dst);
|
||||||
|
|
||||||
extern int gtpie_decaps(union gtpie_member *ie[], int version,
|
extern int gtpie_decaps(union gtpie_member *ie[], int version,
|
||||||
void *pack, unsigned len);
|
const void *pack, unsigned len);
|
||||||
extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len);
|
extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len);
|
||||||
extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
||||||
void *pack, unsigned *len);
|
void *pack, unsigned *len);
|
||||||
|
|||||||
20
gtp/pdp.c
20
gtp/pdp.c
@@ -149,6 +149,8 @@ int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
|||||||
1].secondary_tei[(*pdp)->nsapi & 0x0f] =
|
1].secondary_tei[(*pdp)->nsapi & 0x0f] =
|
||||||
(*pdp)->teid_own;
|
(*pdp)->teid_own;
|
||||||
}
|
}
|
||||||
|
/* Default: Generate G-PDU sequence numbers on Tx */
|
||||||
|
(*pdp)->tx_gpdu_seq = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -362,24 +364,6 @@ int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
|
|||||||
*/
|
*/
|
||||||
/* Various conversion functions */
|
/* Various conversion functions */
|
||||||
|
|
||||||
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
|
|
||||||
{
|
|
||||||
eua->l = 6;
|
|
||||||
eua->v[0] = 0xf1; /* IETF */
|
|
||||||
eua->v[1] = 0x21; /* IPv4 */
|
|
||||||
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] != 0xf1) || (eua->v[1] != 0x21)) {
|
|
||||||
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)
|
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
|
||||||
{
|
{
|
||||||
return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
|
return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
|
||||||
|
|||||||
16
gtp/pdp.h
16
gtp/pdp.h
@@ -13,11 +13,21 @@
|
|||||||
#ifndef _PDP_H
|
#ifndef _PDP_H
|
||||||
#define _PDP_H
|
#define _PDP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
struct gsn_t;
|
struct gsn_t;
|
||||||
|
|
||||||
|
#define LOGPDPX(ss, level, pdp, fmt, args...) \
|
||||||
|
LOGP(ss, level, "PDP(%s:%u): " fmt, imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi, ## args)
|
||||||
|
|
||||||
#define PDP_MAX 1024 /* Max number of PDP contexts */
|
#define PDP_MAX 1024 /* Max number of PDP contexts */
|
||||||
#define PDP_MAXNSAPI 16 /* Max number of NSAPI */
|
#define PDP_MAXNSAPI 16 /* Max number of NSAPI */
|
||||||
|
|
||||||
|
#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 */
|
/* 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 *
|
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
|
||||||
* by 135: QOS Profile in version 1 */
|
* by 135: QOS Profile in version 1 */
|
||||||
@@ -112,7 +122,7 @@ struct pdp_t {
|
|||||||
/* Parameters shared by all PDP context belonging to the same MS */
|
/* Parameters shared by all PDP context belonging to the same MS */
|
||||||
|
|
||||||
void *ipif; /* IP network interface */
|
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 */
|
void *asap; /* Application specific service access point */
|
||||||
|
|
||||||
uint64_t imsi; /* International Mobile Subscriber Identity. */
|
uint64_t imsi; /* International Mobile Subscriber Identity. */
|
||||||
@@ -226,6 +236,8 @@ struct pdp_t {
|
|||||||
void *priv;
|
void *priv;
|
||||||
|
|
||||||
struct gsn_t *gsn;
|
struct gsn_t *gsn;
|
||||||
|
|
||||||
|
bool tx_gpdu_seq; /* Transmit (true) or suppress G-PDU sequence numbers */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* functions related to pdp_t management */
|
/* functions related to pdp_t management */
|
||||||
@@ -255,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_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);
|
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
|
||||||
|
|
||||||
#endif /* !_PDP_H */
|
#endif /* !_PDP_H */
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
noinst_LIBRARIES = libmisc.a
|
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)
|
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
|
||||||
|
|||||||
160
lib/gtp-kernel.c
Normal file
160
lib/gtp-kernel.c
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#ifdef __linux__
|
||||||
|
#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include <libgtpnl/gtp.h>
|
||||||
|
#include <libgtpnl/gtpnl.h>
|
||||||
|
#include <libmnl/libmnl.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "../lib/tun.h"
|
||||||
|
#include "../lib/syserr.h"
|
||||||
|
#include "../gtp/pdp.h"
|
||||||
|
#include "../gtp/gtp.h"
|
||||||
|
|
||||||
|
#include <libgtpnl/gtp.h>
|
||||||
|
#include <libgtpnl/gtpnl.h>
|
||||||
|
#include <libmnl/libmnl.h>
|
||||||
|
|
||||||
|
#include "gtp-kernel.h"
|
||||||
|
|
||||||
|
static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp)
|
||||||
|
{
|
||||||
|
struct in46_addr ia46;
|
||||||
|
struct in_addr ia;
|
||||||
|
|
||||||
|
in46a_from_eua(&pdp->eua, &ia46);
|
||||||
|
gsna2in_addr(&ia, &pdp->gsnrc);
|
||||||
|
|
||||||
|
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix,
|
||||||
|
devname, pdp->version,
|
||||||
|
pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
|
||||||
|
in46a_ntoa(&ia46), inet_ntoa(ia));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int genl_id;
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
} gtp_nl;
|
||||||
|
|
||||||
|
static int gtp_kernel_init_once(void)
|
||||||
|
{
|
||||||
|
/* only initialize once */
|
||||||
|
if (gtp_nl.nl)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gtp_nl.nl = genl_socket_open();
|
||||||
|
if (gtp_nl.nl == NULL) {
|
||||||
|
LOGP(DGGSN, LOGL_ERROR, "cannot create genetlink socket\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
|
||||||
|
if (gtp_nl.genl_id < 0) {
|
||||||
|
LOGP(DGGSN, LOGL_ERROR, "cannot lookup GTP genetlink ID\n");
|
||||||
|
genl_socket_close(gtp_nl.nl);
|
||||||
|
gtp_nl.nl = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOGP(DGGSN, LOGL_NOTICE, "Initialized GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
|
||||||
|
{
|
||||||
|
if (gtp_kernel_init_once() < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gtp_kernel_stop(const char *devname)
|
||||||
|
{
|
||||||
|
gtp_dev_destroy(devname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
|
||||||
|
{
|
||||||
|
struct in_addr ms, sgsn;
|
||||||
|
struct gtp_tunnel *t;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pdp_debug(__func__, devname, pdp);
|
||||||
|
|
||||||
|
t = gtp_tunnel_alloc();
|
||||||
|
if (t == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
|
||||||
|
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
|
||||||
|
|
||||||
|
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
|
||||||
|
gtp_tunnel_set_version(t, pdp->version);
|
||||||
|
gtp_tunnel_set_ms_ip4(t, &ms);
|
||||||
|
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
|
||||||
|
if (pdp->version == 0) {
|
||||||
|
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
||||||
|
gtp_tunnel_set_flowid(t, pdp->flru);
|
||||||
|
} else {
|
||||||
|
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
||||||
|
/* use the TEI advertised by SGSN when sending packets
|
||||||
|
* towards the SGSN */
|
||||||
|
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
||||||
|
gtp_tunnel_free(t);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
|
||||||
|
{
|
||||||
|
struct gtp_tunnel *t;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pdp_debug(__func__, devname, pdp);
|
||||||
|
|
||||||
|
t = gtp_tunnel_alloc();
|
||||||
|
if (t == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
|
||||||
|
gtp_tunnel_set_version(t, pdp->version);
|
||||||
|
if (pdp->version == 0) {
|
||||||
|
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
||||||
|
gtp_tunnel_set_flowid(t, pdp->flru);
|
||||||
|
} else {
|
||||||
|
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
||||||
|
gtp_tunnel_free(t);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
38
lib/gtp-kernel.h
Normal file
38
lib/gtp-kernel.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef _GTP_KERNEL_H_
|
||||||
|
#define _GTP_KERNEL_H_
|
||||||
|
|
||||||
|
struct gengetopt_args_info;
|
||||||
|
|
||||||
|
extern int debug;
|
||||||
|
extern char *ipup;
|
||||||
|
|
||||||
|
#ifdef GTP_KERNEL
|
||||||
|
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_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) {}
|
||||||
|
|
||||||
|
static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif /* _GTP_KERNEL_H_ */
|
||||||
146
lib/in46_addr.c
146
lib/in46_addr.c
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../lib/in46_addr.h"
|
#include "../lib/in46_addr.h"
|
||||||
|
#include "../gtp/pdp.h"
|
||||||
|
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
|
|||||||
sin->sin_addr = in->v4;
|
sin->sin_addr = in->v4;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
sin6->sin6_family = AF_INET;
|
sin6->sin6_family = AF_INET6;
|
||||||
sin6->sin6_addr = in->v6;
|
sin6->sin6_addr = in->v6;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -194,22 +195,87 @@ int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Convert given PDP End User Address to in46_addr
|
static unsigned int ipv4_netmasklen(const struct in_addr *netmask)
|
||||||
* \returns 0 on success; negative on error */
|
|
||||||
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
|
|
||||||
{
|
{
|
||||||
|
uint32_t bits = netmask->s_addr;
|
||||||
|
uint8_t *b = (uint8_t*) &bits;
|
||||||
|
unsigned int i, prefix = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
while (b[i] & 0x80) {
|
||||||
|
prefix++;
|
||||||
|
b[i] = b[i] << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int ipv6_netmasklen(const struct in6_addr *netmask)
|
||||||
|
{
|
||||||
|
#if defined(__linux__)
|
||||||
|
#define ADDRFIELD(i) s6_addr32[i]
|
||||||
|
#else
|
||||||
|
#define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned int i, j, prefix = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < 4; j++) {
|
||||||
|
uint32_t bits = netmask->ADDRFIELD(j);
|
||||||
|
uint8_t *b = (uint8_t*) &bits;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
while (b[i] & 0x80) {
|
||||||
|
prefix++;
|
||||||
|
b[i] = b[i] << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef ADDRFIELD
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Convert netmask to prefix length representation
|
||||||
|
* \param[in] netmask in46_addr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit)
|
||||||
|
* \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask)
|
||||||
|
*/
|
||||||
|
unsigned int in46a_netmasklen(const struct in46_addr *netmask)
|
||||||
|
{
|
||||||
|
switch (netmask->len) {
|
||||||
|
case 4:
|
||||||
|
return ipv4_netmasklen(&netmask->v4);
|
||||||
|
case 16:
|
||||||
|
return ipv6_netmasklen(&netmask->v6);
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! 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)
|
||||||
|
{
|
||||||
|
const struct in46_addr *src_v4, *src_v6;
|
||||||
|
if (size == 1) {
|
||||||
switch (src->len) {
|
switch (src->len) {
|
||||||
case 4:
|
case 4:
|
||||||
eua->l = 6;
|
eua->l = 6;
|
||||||
eua->v[0] = 0xf1; /* IETF */
|
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||||
eua->v[1] = 0x21; /* IPv4 */
|
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||||
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
case 16:
|
case 16:
|
||||||
eua->l = 18;
|
eua->l = 18;
|
||||||
eua->v[0] = 0xf1; /* IETF */
|
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||||
eua->v[1] = 0x57; /* IPv6 */
|
eua->v[1] = PDP_EUA_TYPE_v6;
|
||||||
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -219,8 +285,34 @@ int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Convert given in46_addr to PDP End User Address
|
if (src[0].len == src[1].len)
|
||||||
* \returns 0 on success; negative on error */
|
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 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)
|
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
|
||||||
{
|
{
|
||||||
if (eua->l < 2)
|
if (eua->l < 2)
|
||||||
@@ -230,28 +322,52 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
switch (eua->v[1]) {
|
switch (eua->v[1]) {
|
||||||
case 0x21:
|
case PDP_EUA_TYPE_v4:
|
||||||
dst->len = 4;
|
dst->len = 4;
|
||||||
if (eua->l >= 6)
|
if (eua->l >= 6)
|
||||||
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
|
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
|
||||||
else
|
else
|
||||||
dst->v4.s_addr = 0;
|
dst->v4.s_addr = 0;
|
||||||
break;
|
return 1;
|
||||||
case 0x57:
|
case PDP_EUA_TYPE_v6:
|
||||||
dst->len = 16;
|
dst->len = 16;
|
||||||
if (eua->l >= 18)
|
if (eua->l >= 18)
|
||||||
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
|
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
|
||||||
else
|
else
|
||||||
memset(&dst->v6, 0, 16);
|
memset(&dst->v6, 0, 16);
|
||||||
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
dst[0].len = 4;
|
||||||
|
dst[1].len = 16;
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
default_to_dyn_v4:
|
default_to_dyn_v4:
|
||||||
/* assume dynamic IPv4 by default */
|
/* assume dynamic IPv4 by default */
|
||||||
dst->len = 4;
|
dst->len = 4;
|
||||||
dst->v4.s_addr = 0;
|
dst->v4.s_addr = 0;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ extern const char *in46p_ntoa(const struct in46_prefix *in46p);
|
|||||||
extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
|
extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
|
||||||
extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b);
|
extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b);
|
||||||
extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
|
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);
|
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
|
||||||
|
|||||||
40
lib/ippool.c
40
lib/ippool.c
@@ -184,9 +184,19 @@ void in46a_inc(struct in46_addr *addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool addr_in_prefix_list(struct in46_addr *addr, struct in46_prefix *list, size_t list_size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < list_size; i++) {
|
||||||
|
if (in46a_prefix_equal(addr, &list[i].addr))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create new address pool */
|
/* Create new address pool */
|
||||||
int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const struct in46_prefix *stat,
|
int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const struct in46_prefix *stat,
|
||||||
int flags)
|
int flags, struct in46_prefix *blacklist, size_t blacklist_size)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* Parse only first instance of pool for now */
|
/* Parse only first instance of pool for now */
|
||||||
@@ -210,18 +220,16 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
|
|||||||
if (addr.len == sizeof(struct in6_addr))
|
if (addr.len == sizeof(struct in6_addr))
|
||||||
addr.len = 64/8;
|
addr.len = 64/8;
|
||||||
|
|
||||||
/* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
|
dynsize = (1 << (addr.len*8 - addrprefixlen));
|
||||||
if (flags & IPPOOL_NOGATEWAY) {
|
|
||||||
flags |= IPPOOL_NONETWORK;
|
|
||||||
}
|
|
||||||
|
|
||||||
dynsize = (1 << (addr.len*8 - addrprefixlen)) -1;
|
|
||||||
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
|
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
|
||||||
dynsize--;
|
dynsize--;
|
||||||
if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
|
|
||||||
dynsize--;
|
|
||||||
if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
|
if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
|
||||||
dynsize--;
|
dynsize--;
|
||||||
|
/* Exclude included blacklist addresses from pool */
|
||||||
|
for (i = 0; i < blacklist_size; i++) {
|
||||||
|
if (in46a_within_mask(&blacklist[i].addr, &addr, addrprefixlen))
|
||||||
|
dynsize--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stat || stat->addr.len == 0) {
|
if (!stat || stat->addr.len == 0) {
|
||||||
@@ -232,7 +240,7 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
|
|||||||
stataddr = stat->addr;
|
stataddr = stat->addr;
|
||||||
stataddrprefixlen = stat->prefixlen;
|
stataddrprefixlen = stat->prefixlen;
|
||||||
|
|
||||||
statsize = (1 << (addr.len - stataddrprefixlen + 1)) -1;
|
statsize = (1 << (stataddr.len*8 - stataddrprefixlen));
|
||||||
if (statsize > IPPOOL_STATSIZE)
|
if (statsize > IPPOOL_STATSIZE)
|
||||||
statsize = IPPOOL_STATSIZE;
|
statsize = IPPOOL_STATSIZE;
|
||||||
}
|
}
|
||||||
@@ -278,13 +286,17 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
|
|||||||
|
|
||||||
(*this)->firstdyn = NULL;
|
(*this)->firstdyn = NULL;
|
||||||
(*this)->lastdyn = NULL;
|
(*this)->lastdyn = NULL;
|
||||||
if (flags & IPPOOL_NOGATEWAY) {
|
if (flags & IPPOOL_NONETWORK) {
|
||||||
in46a_inc(&addr);
|
|
||||||
in46a_inc(&addr);
|
|
||||||
} else if (flags & IPPOOL_NONETWORK) {
|
|
||||||
in46a_inc(&addr);
|
in46a_inc(&addr);
|
||||||
}
|
}
|
||||||
for (i = 0; i < dynsize; i++) {
|
for (i = 0; i < dynsize; i++) {
|
||||||
|
if (addr_in_prefix_list(&addr, blacklist, blacklist_size)) {
|
||||||
|
SYS_ERR(DIP, LOGL_DEBUG, 0,
|
||||||
|
"addr blacklisted from pool: %s", in46a_ntoa(&addr));
|
||||||
|
in46a_inc(&addr);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
(*this)->member[i].addr = addr;
|
(*this)->member[i].addr = addr;
|
||||||
in46a_inc(&addr);
|
in46a_inc(&addr);
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
#define IPPOOL_NONETWORK 0x01
|
#define IPPOOL_NONETWORK 0x01
|
||||||
#define IPPOOL_NOBROADCAST 0x02
|
#define IPPOOL_NOBROADCAST 0x02
|
||||||
#define IPPOOL_NOGATEWAY 0x04
|
|
||||||
|
|
||||||
#define IPPOOL_STATSIZE 0x10000
|
#define IPPOOL_STATSIZE 0x10000
|
||||||
|
|
||||||
@@ -72,7 +71,8 @@ extern unsigned long int ippool_hash(struct in46_addr *addr);
|
|||||||
|
|
||||||
/* Create new address pool */
|
/* Create new address pool */
|
||||||
extern int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn,
|
extern int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn,
|
||||||
const struct in46_prefix *stat, int flags);
|
const struct in46_prefix *stat, int flags,
|
||||||
|
struct in46_prefix *blacklist, size_t blacklist_size);
|
||||||
|
|
||||||
/* Delete existing address pool */
|
/* Delete existing address pool */
|
||||||
extern int ippool_free(struct ippool_t *this);
|
extern int ippool_free(struct ippool_t *this);
|
||||||
|
|||||||
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);
|
||||||
593
lib/tun.c
593
lib/tun.c
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* TUN interface functions.
|
* TUN interface functions.
|
||||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
* 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
|
* The contents of this file may be used under the terms of the GNU
|
||||||
* General Public License Version 2, provided that the above copyright
|
* General Public License Version 2, provided that the above copyright
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@@ -42,8 +43,6 @@
|
|||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#include <linux/if_tun.h>
|
#include <linux/if_tun.h>
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <linux/rtnetlink.h>
|
|
||||||
|
|
||||||
#elif defined (__FreeBSD__)
|
#elif defined (__FreeBSD__)
|
||||||
#include <net/if_tun.h>
|
#include <net/if_tun.h>
|
||||||
@@ -59,531 +58,98 @@
|
|||||||
|
|
||||||
#include "tun.h"
|
#include "tun.h"
|
||||||
#include "syserr.h"
|
#include "syserr.h"
|
||||||
|
#include "gtp-kernel.h"
|
||||||
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
|
|
||||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
|
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
|
||||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
int rc;
|
||||||
int fd;
|
rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
memset(&ifr, '\0', sizeof(ifr));
|
if (addr)
|
||||||
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) { /* Set the interface address */
|
|
||||||
this->addr.s_addr = addr->s_addr;
|
this->addr.s_addr = addr->s_addr;
|
||||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
|
if (dstaddr)
|
||||||
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 */
|
|
||||||
this->dstaddr.s_addr = dstaddr->s_addr;
|
this->dstaddr.s_addr = dstaddr->s_addr;
|
||||||
memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
|
if (netmask)
|
||||||
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 */
|
|
||||||
this->netmask.s_addr = netmask->s_addr;
|
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++;
|
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__)
|
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||||
tun_addroute(this, dstaddr, addr, &this->netmask);
|
|
||||||
this->routes = 1;
|
this->routes = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
|
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
|
||||||
size_t prefixlen)
|
size_t prefixlen)
|
||||||
{
|
{
|
||||||
struct in6_ifreq ifr;
|
int rc;
|
||||||
int fd;
|
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||||
|
if (rc < 0)
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
return rc;
|
||||||
|
|
||||||
#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(&this->addr, addr, sizeof(*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 alreadsy exists");
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* FIXME: looks like this is not possible/necessary for IPv6? */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
|
||||||
if (addr)
|
|
||||||
memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
|
|
||||||
if (dstaddr)
|
if (dstaddr)
|
||||||
memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
|
memcpy(&this->dstaddr, dstaddr, sizeof(*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++;
|
this->addrs++;
|
||||||
|
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||||
/* 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);
|
|
||||||
this->routes = 1;
|
this->routes = 1;
|
||||||
#endif
|
#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)
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
|
||||||
|
rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
this->addrs++;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tun_addaddr6(struct tun_t *this,
|
||||||
|
struct in6_addr *addr,
|
||||||
|
struct in6_addr *dstaddr, int prefixlen)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||||
|
return tun_setaddr6(this, addr, dstaddr, prefixlen);
|
||||||
|
|
||||||
|
rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
this->addrs++;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
|
||||||
{
|
{
|
||||||
struct in_addr netmask;
|
struct in_addr netmask;
|
||||||
switch (addr->len) {
|
switch (addr->len) {
|
||||||
case 4:
|
case 4:
|
||||||
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||||
return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
||||||
case 16:
|
case 16:
|
||||||
return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tun_route(struct tun_t *this,
|
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
|
||||||
struct in_addr *dst,
|
|
||||||
struct in_addr *gateway, struct in_addr *mask, int delete)
|
|
||||||
{
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
|
|
||||||
struct rtentry r;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
@@ -606,6 +172,7 @@ int tun_new(struct tun_t **tun, const char *dev_name)
|
|||||||
(*tun)->routes = 0;
|
(*tun)->routes = 0;
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
if (!use_kernel) {
|
||||||
/* Open the actual tun device */
|
/* Open the actual tun device */
|
||||||
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
|
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
|
||||||
@@ -628,9 +195,27 @@ int tun_new(struct tun_t **tun, const char *dev_name)
|
|||||||
|
|
||||||
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
|
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
#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 */
|
/* Find suitable device */
|
||||||
for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
|
for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
|
||||||
snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
|
snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
|
||||||
@@ -682,12 +267,16 @@ int tun_free(struct tun_t *tun)
|
|||||||
{
|
{
|
||||||
|
|
||||||
if (tun->routes) {
|
if (tun->routes) {
|
||||||
tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
|
netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tun->fd >= 0) {
|
||||||
if (close(tun->fd)) {
|
if (close(tun->fd)) {
|
||||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
|
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtp_kernel_stop(tun->devname);
|
||||||
|
|
||||||
/* TODO: For solaris we need to unlink streams */
|
/* TODO: For solaris we need to unlink streams */
|
||||||
|
|
||||||
@@ -748,3 +337,21 @@ int tun_runscript(struct tun_t *tun, char *script)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! 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.
|
||||||
|
* \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 tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
|
||||||
|
{
|
||||||
|
return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
|
||||||
|
}
|
||||||
|
|||||||
44
lib/tun.h
44
lib/tun.h
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* TUN interface functions.
|
* TUN interface functions.
|
||||||
* Copyright (C) 2002, 2003 Mondru AB.
|
* 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
|
* The contents of this file may be used under the terms of the GNU
|
||||||
* General Public License Version 2, provided that the above copyright
|
* General Public License Version 2, provided that the above copyright
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#ifndef _TUN_H
|
#ifndef _TUN_H
|
||||||
#define _TUN_H
|
#define _TUN_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
|
||||||
#include "../lib/in46_addr.h"
|
#include "../lib/in46_addr.h"
|
||||||
@@ -20,33 +21,9 @@
|
|||||||
#define PACKET_MAX 8196 /* Maximum packet size we receive */
|
#define PACKET_MAX 8196 /* Maximum packet size we receive */
|
||||||
#define TUN_SCRIPTSIZE 256
|
#define TUN_SCRIPTSIZE 256
|
||||||
#define TUN_ADDRSIZE 128
|
#define TUN_ADDRSIZE 128
|
||||||
#define TUN_NLBUFSIZE 1024
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifndef HAVE_IPHDR
|
#include "netdev.h"
|
||||||
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 */
|
|
||||||
|
|
||||||
/* ***********************************************************
|
/* ***********************************************************
|
||||||
* Information storage for each tun instance
|
* Information storage for each tun instance
|
||||||
@@ -65,19 +42,13 @@ struct tun_t {
|
|||||||
void *priv;
|
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_free(struct tun_t *tun);
|
||||||
extern int tun_decaps(struct tun_t *this);
|
extern int tun_decaps(struct tun_t *this);
|
||||||
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
|
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
|
||||||
|
|
||||||
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
|
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
|
||||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
struct in46_addr *dstaddr, size_t prefixlen);
|
||||||
|
|
||||||
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_set_cb_ind(struct tun_t *this,
|
extern int tun_set_cb_ind(struct tun_t *this,
|
||||||
int (*cb_ind) (struct tun_t * tun, void *pack,
|
int (*cb_ind) (struct tun_t * tun, void *pack,
|
||||||
@@ -85,4 +56,7 @@ extern int tun_set_cb_ind(struct tun_t *this,
|
|||||||
|
|
||||||
extern int tun_runscript(struct tun_t *tun, char *script);
|
extern int tun_runscript(struct tun_t *tun, char *script);
|
||||||
|
|
||||||
|
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
|
||||||
|
size_t prefix_size, int flags);
|
||||||
|
|
||||||
#endif /* !_TUN_H */
|
#endif /* !_TUN_H */
|
||||||
|
|||||||
@@ -5,5 +5,11 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
|
|||||||
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
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)
|
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_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
|
||||||
sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h
|
sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h
|
||||||
|
|||||||
2493
sgsnemu/cmdline.c
2493
sgsnemu/cmdline.c
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,19 @@
|
|||||||
# notice and this permission notice is included in all copies or
|
# notice and this permission notice is included in all copies or
|
||||||
# substantial portions of the software.
|
# substantial portions of the software.
|
||||||
#
|
#
|
||||||
# Use "gengetopt --conf-parser < cmdline.ggo"
|
# Use
|
||||||
|
# gengetopt --conf-parser < cmdline.ggo
|
||||||
|
# linux-2.6/scripts/Lindent cmdline.c
|
||||||
|
# linux-2.6/scripts/Lindent cmdline.h
|
||||||
|
# sed -i -e 's/int qose1_arg;/unsigned long long int qose1_arg;/' cmdline.h
|
||||||
# to generate cmdline.c and cmdline.h
|
# to generate cmdline.c and cmdline.h
|
||||||
|
|
||||||
|
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 "debug" d "Run in debug mode" flag off
|
||||||
|
|
||||||
option "conf" c "Read configuration file" string no
|
option "conf" c "Read configuration file" string no
|
||||||
@@ -16,7 +26,7 @@ option "pidfile" - "Filename of process id file" string default="./sgsn
|
|||||||
option "statedir" - "Directory of nonvolatile data" string default="./" no
|
option "statedir" - "Directory of nonvolatile data" string default="./" no
|
||||||
|
|
||||||
option "dns" - "DNS Server to use" string 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 "remote" r "Remote host" string no
|
||||||
|
|
||||||
option "contexts" - "Number of contexts" int default="1" no
|
option "contexts" - "Number of contexts" int default="1" no
|
||||||
@@ -25,23 +35,36 @@ option "timelimit" - "Exit after timelimit seconds" int default="0" no
|
|||||||
option "gtpversion" - "GTP version to use" int default="1" no
|
option "gtpversion" - "GTP version to use" int default="1" no
|
||||||
option "apn" a "Access point name" string default="internet" no
|
option "apn" a "Access point name" string default="internet" no
|
||||||
option "selmode" - "Selection mode" int default="0x01" no
|
option "selmode" - "Selection mode" int default="0x01" no
|
||||||
|
option "rattype" - "Radio Access Technology Type" int default="1" no typestr="1..5"
|
||||||
|
option "userloc" - "User Location Information" string default="02509946241207" no typestr="type.MCC.MNC.LAC.CIorSACorRAC"
|
||||||
|
option "rai" - "Routing Area Information" string default="02509946241207" no typestr="MCC.MNC.LAC.RAC"
|
||||||
|
option "mstz" - "MS Time Zone" string default="0" no typestr="sign.NbQuartersOfAnHour.DSTAdjustment"
|
||||||
|
option "imeisv" - "IMEI(SV) International Mobile Equipment Identity (and Software Version)" string default="2143658709214365" no
|
||||||
|
option "norecovery" - "Do not send recovery" flag off
|
||||||
option "imsi" i "IMSI" string default="240010123456789" no
|
option "imsi" i "IMSI" string default="240010123456789" no
|
||||||
option "nsapi" - "NSAPI" int default="0" no
|
option "nsapi" - "NSAPI" int default="0" no
|
||||||
option "msisdn" m "Mobile Station ISDN number" string default="46702123456" no
|
option "msisdn" m "Mobile Station ISDN number" string default="46702123456" no
|
||||||
option "qos" q "Requested quality of service" int default="0x0b921f" no
|
option "qos" q "Requested quality of service" int default="0x000b921f" no
|
||||||
|
option "qose1" - "Requested quality of service Extension 1" int default="0x9396404074f9ffff" no
|
||||||
|
option "qose2" - "Requested quality of service Extension 2" int default="0x11" no
|
||||||
|
option "qose3" - "Requested quality of service Extension 3" int default="0x0101" no
|
||||||
|
option "qose4" - "Requested quality of service Extension 4" int default="0x4040" no
|
||||||
option "charging" - "Charging characteristics" int default="0x0800" no
|
option "charging" - "Charging characteristics" int default="0x0800" no
|
||||||
option "uid" u "Login user ID" string default="mig" no
|
option "uid" u "Login user ID" string default="mig" no
|
||||||
option "pwd" p "Login password" string default="hemmelig" no
|
option "pwd" p "Login password" string default="hemmelig" no
|
||||||
|
|
||||||
option "createif" - "Create local network interface" flag off
|
modeoption "createif" - "Create local network interface" flag off mode="createif"
|
||||||
option "net" n "Network address for local interface" string no
|
modeoption "net" n "Network address for local interface" string dependon="createif" no mode="createif"
|
||||||
option "defaultroute" - "Create default route" flag off
|
modeoption "defaultroute" - "Create default route" flag dependon="createif" off mode="createif"
|
||||||
option "ipup" - "Script to run after link-up" string no
|
modeoption "ipup" - "Script to run after link-up" string dependon="createif" no mode="createif"
|
||||||
option "ipdown" - "Script to run after link-down" string no
|
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
|
modeoption "pinghost" - "Ping remote host" string no mode="pinghost"
|
||||||
option "pingrate" - "Number of ping req per second" unsigned int default="1" no
|
modeoption "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no mode="pinghost"
|
||||||
option "pingsize" - "Number of ping data bytes" unsigned int default="56" no
|
modeoption "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no mode="pinghost"
|
||||||
option "pingcount" - "Number of ping req to send" unsigned int default="0" no
|
modeoption "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no mode="pinghost"
|
||||||
option "pingquiet" - "Do not print ping packet info" flag off
|
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)"
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
/* cmdline.h */
|
/** @file cmdline.h
|
||||||
|
* @brief The header file for the command line option parser
|
||||||
/* File autogenerated by gengetopt version 2.17 */
|
* generated by GNU Gengetopt version 2.22.6
|
||||||
|
* http://www.gnu.org/software/gengetopt.
|
||||||
|
* DO NOT modify this file, since it can be overwritten
|
||||||
|
* @author GNU Gengetopt by Lorenzo Bettini */
|
||||||
|
|
||||||
#ifndef CMDLINE_H
|
#ifndef CMDLINE_H
|
||||||
#define CMDLINE_H
|
#define CMDLINE_H
|
||||||
@@ -10,197 +13,517 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h> /* for FILE */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
#ifndef CMDLINE_PARSER_PACKAGE
|
#ifndef CMDLINE_PARSER_PACKAGE
|
||||||
#define CMDLINE_PARSER_PACKAGE PACKAGE
|
/** @brief the program name (used for printing errors) */
|
||||||
|
#define CMDLINE_PARSER_PACKAGE "sgsnemu"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CMDLINE_PARSER_PACKAGE_NAME
|
||||||
|
/** @brief the complete program name (used for help and version) */
|
||||||
|
#define CMDLINE_PARSER_PACKAGE_NAME "sgsnemu"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CMDLINE_PARSER_VERSION
|
#ifndef CMDLINE_PARSER_VERSION
|
||||||
|
/** @brief the program version */
|
||||||
#define CMDLINE_PARSER_VERSION VERSION
|
#define CMDLINE_PARSER_VERSION VERSION
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** @brief Where the command line options are stored */
|
||||||
struct gengetopt_args_info {
|
struct gengetopt_args_info {
|
||||||
const char *help_help; /* Print help and exit help description. */
|
const char *help_help;
|
||||||
const char *version_help; /* Print version and exit help description. */
|
/**< @brief Print help and exit help description. */
|
||||||
int debug_flag; /* Run in debug mode (default=off). */
|
const char *version_help;
|
||||||
const char *debug_help; /* Run in debug mode help description. */
|
/**< @brief Print version and exit help description. */
|
||||||
char *conf_arg; /* Read configuration file. */
|
int debug_flag;
|
||||||
char *conf_orig; /* Read configuration file original value given at command line. */
|
/**< @brief Run in debug mode (default=off). */
|
||||||
const char *conf_help; /* Read configuration file help description. */
|
const char *debug_help;
|
||||||
char *pidfile_arg; /* Filename of process id file (default='./sgsnemu.pid'). */
|
/**< @brief Run in debug mode help description. */
|
||||||
char *pidfile_orig; /* Filename of process id file original value given at command line. */
|
char *conf_arg;
|
||||||
const char *pidfile_help; /* Filename of process id file help description. */
|
/**< @brief Read configuration file. */
|
||||||
char *statedir_arg; /* Directory of nonvolatile data (default='./'). */
|
char *conf_orig;
|
||||||
char *statedir_orig; /* Directory of nonvolatile data original value given at command line. */
|
/**< @brief Read configuration file original value given at command line. */
|
||||||
const char *statedir_help; /* Directory of nonvolatile data help description. */
|
const char *conf_help;
|
||||||
char *dns_arg; /* DNS Server to use. */
|
/**< @brief Read configuration file help description. */
|
||||||
char *dns_orig; /* DNS Server to use original value given at command line. */
|
char *pidfile_arg;
|
||||||
const char *dns_help; /* DNS Server to use help description. */
|
/**< @brief Filename of process id file (default='./sgsnemu.pid'). */
|
||||||
char *listen_arg; /* Local interface. */
|
char *pidfile_orig;
|
||||||
char *listen_orig; /* Local interface original value given at command line. */
|
/**< @brief Filename of process id file original value given at command line. */
|
||||||
const char *listen_help; /* Local interface help description. */
|
const char *pidfile_help;
|
||||||
char *remote_arg; /* Remote host. */
|
/**< @brief Filename of process id file help description. */
|
||||||
char *remote_orig; /* Remote host original value given at command line. */
|
char *statedir_arg;
|
||||||
const char *remote_help; /* Remote host help description. */
|
/**< @brief Directory of nonvolatile data (default='./'). */
|
||||||
int contexts_arg; /* Number of contexts (default='1'). */
|
char *statedir_orig;
|
||||||
char *contexts_orig; /* Number of contexts original value given at command line. */
|
/**< @brief Directory of nonvolatile data original value given at command line. */
|
||||||
const char *contexts_help; /* Number of contexts help description. */
|
const char *statedir_help;
|
||||||
int timelimit_arg; /* Exit after timelimit seconds (default='0'). */
|
/**< @brief Directory of nonvolatile data help description. */
|
||||||
char *timelimit_orig; /* Exit after timelimit seconds original value given at command line. */
|
char *dns_arg;
|
||||||
const char *timelimit_help; /* Exit after timelimit seconds help description. */
|
/**< @brief DNS Server to use. */
|
||||||
int gtpversion_arg; /* GTP version to use (default='1'). */
|
char *dns_orig;
|
||||||
char *gtpversion_orig; /* GTP version to use original value given at command line. */
|
/**< @brief DNS Server to use original value given at command line. */
|
||||||
const char *gtpversion_help; /* GTP version to use help description. */
|
const char *dns_help;
|
||||||
char *apn_arg; /* Access point name (default='internet'). */
|
/**< @brief DNS Server to use help description. */
|
||||||
char *apn_orig; /* Access point name original value given at command line. */
|
char *listen_arg;
|
||||||
const char *apn_help; /* Access point name help description. */
|
/**< @brief Local host. */
|
||||||
int selmode_arg; /* Selection mode (default='0x01'). */
|
char *listen_orig;
|
||||||
char *selmode_orig; /* Selection mode original value given at command line. */
|
/**< @brief Local host original value given at command line. */
|
||||||
const char *selmode_help; /* Selection mode help description. */
|
const char *listen_help;
|
||||||
char *rattype_arg; /* Radio Access Technology Type (optional). */
|
/**< @brief Local host help description. */
|
||||||
|
char *remote_arg;
|
||||||
|
/**< @brief Remote host. */
|
||||||
|
char *remote_orig;
|
||||||
|
/**< @brief Remote host original value given at command line. */
|
||||||
|
const char *remote_help;
|
||||||
|
/**< @brief Remote host help description. */
|
||||||
|
int contexts_arg;
|
||||||
|
/**< @brief Number of contexts (default='1'). */
|
||||||
|
char *contexts_orig;
|
||||||
|
/**< @brief Number of contexts original value given at command line. */
|
||||||
|
const char *contexts_help;
|
||||||
|
/**< @brief Number of contexts help description. */
|
||||||
|
int timelimit_arg;
|
||||||
|
/**< @brief Exit after timelimit seconds (default='0'). */
|
||||||
|
char *timelimit_orig;
|
||||||
|
/**< @brief Exit after timelimit seconds original value given at command line. */
|
||||||
|
const char *timelimit_help;
|
||||||
|
/**< @brief Exit after timelimit seconds help description. */
|
||||||
|
int gtpversion_arg;
|
||||||
|
/**< @brief GTP version to use (default='1'). */
|
||||||
|
char *gtpversion_orig;
|
||||||
|
/**< @brief GTP version to use original value given at command line. */
|
||||||
|
const char *gtpversion_help;
|
||||||
|
/**< @brief GTP version to use help description. */
|
||||||
|
char *apn_arg;
|
||||||
|
/**< @brief Access point name (default='internet'). */
|
||||||
|
char *apn_orig;
|
||||||
|
/**< @brief Access point name original value given at command line. */
|
||||||
|
const char *apn_help;
|
||||||
|
/**< @brief Access point name help description. */
|
||||||
|
int selmode_arg;
|
||||||
|
/**< @brief Selection mode (default='0x01'). */
|
||||||
|
char *selmode_orig;
|
||||||
|
/**< @brief Selection mode original value given at command line. */
|
||||||
|
const char *selmode_help;
|
||||||
|
/**< @brief Selection mode help description. */
|
||||||
|
int rattype_arg;
|
||||||
|
/**< @brief Radio Access Technology Type (default='1'). */
|
||||||
char *rattype_orig;
|
char *rattype_orig;
|
||||||
char *rattype_help;
|
/**< @brief Radio Access Technology Type original value given at command line. */
|
||||||
char *userloc_arg; /* User Location Information (optional). */
|
const char *rattype_help;
|
||||||
|
/**< @brief Radio Access Technology Type help description. */
|
||||||
|
char *userloc_arg;
|
||||||
|
/**< @brief User Location Information (default='02509946241207'). */
|
||||||
char *userloc_orig;
|
char *userloc_orig;
|
||||||
char *userloc_help;
|
/**< @brief User Location Information original value given at command line. */
|
||||||
char *rai_arg; /* Routing Area Information (optional). */
|
const char *userloc_help;
|
||||||
|
/**< @brief User Location Information help description. */
|
||||||
|
char *rai_arg;
|
||||||
|
/**< @brief Routing Area Information (default='02509946241207'). */
|
||||||
char *rai_orig;
|
char *rai_orig;
|
||||||
char *rai_help;
|
/**< @brief Routing Area Information original value given at command line. */
|
||||||
char *mstz_arg; /* MS Time Zone (optional). */
|
const char *rai_help;
|
||||||
|
/**< @brief Routing Area Information help description. */
|
||||||
|
char *mstz_arg;
|
||||||
|
/**< @brief MS Time Zone (default='0'). */
|
||||||
char *mstz_orig;
|
char *mstz_orig;
|
||||||
char *mstz_help;
|
/**< @brief MS Time Zone original value given at command line. */
|
||||||
char *imeisv_arg; /* IMEI(SV) (optional). */
|
const char *mstz_help;
|
||||||
|
/**< @brief MS Time Zone help description. */
|
||||||
|
char *imeisv_arg;
|
||||||
|
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) (default='2143658709214365'). */
|
||||||
char *imeisv_orig;
|
char *imeisv_orig;
|
||||||
char *imeisv_help;
|
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) original value given at command line. */
|
||||||
char *imsi_arg; /* IMSI (default='240010123456789'). */
|
const char *imeisv_help;
|
||||||
char *imsi_orig; /* IMSI original value given at command line. */
|
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) help description. */
|
||||||
const char *imsi_help; /* IMSI help description. */
|
int norecovery_flag;
|
||||||
int nsapi_arg; /* NSAPI (default='0'). */
|
/**< @brief Do not send recovery (default=off). */
|
||||||
char *nsapi_orig; /* NSAPI original value given at command line. */
|
const char *norecovery_help;
|
||||||
const char *nsapi_help; /* NSAPI help description. */
|
/**< @brief Do not send recovery help description. */
|
||||||
char *msisdn_arg; /* Mobile Station ISDN number (default='46702123456'). */
|
char *imsi_arg;
|
||||||
char *msisdn_orig; /* Mobile Station ISDN number original value given at command line. */
|
/**< @brief IMSI (default='240010123456789'). */
|
||||||
const char *msisdn_help; /* Mobile Station ISDN number help description. */
|
char *imsi_orig;
|
||||||
int qos_arg; /* Requested quality of service (default='0x0b921f'). */
|
/**< @brief IMSI original value given at command line. */
|
||||||
char *qos_orig; /* Requested quality of service original value given at command line. */
|
const char *imsi_help;
|
||||||
const char *qos_help; /* Requested quality of service help description. */
|
/**< @brief IMSI help description. */
|
||||||
unsigned long long int qose1_arg; /* Requested quality of service Extension 1 */
|
int nsapi_arg;
|
||||||
char *qose1_orig; /* Requested quality of service Extension 1 original value given at command line. */
|
/**< @brief NSAPI (default='0'). */
|
||||||
int qose2_arg; /* Requested quality of service Extension 2 */
|
char *nsapi_orig;
|
||||||
char *qose2_orig; /* Requested quality of service Extension 2 original value given at command line. */
|
/**< @brief NSAPI original value given at command line. */
|
||||||
int qose3_arg; /* Requested quality of service Extension 3 */
|
const char *nsapi_help;
|
||||||
char *qose3_orig; /* Requested quality of service Extension 3 original value given at command line. */
|
/**< @brief NSAPI help description. */
|
||||||
int qose4_arg; /* Requested quality of service Extension 4 */
|
char *msisdn_arg;
|
||||||
char *qose4_orig; /* Requested quality of service Extension 4 original value given at command line. */
|
/**< @brief Mobile Station ISDN number (default='46702123456'). */
|
||||||
int charging_arg; /* Charging characteristics (default='0x0800'). */
|
char *msisdn_orig;
|
||||||
char *charging_orig; /* Charging characteristics original value given at command line. */
|
/**< @brief Mobile Station ISDN number original value given at command line. */
|
||||||
const char *charging_help; /* Charging characteristics help description. */
|
const char *msisdn_help;
|
||||||
char *uid_arg; /* Login user ID (default='mig'). */
|
/**< @brief Mobile Station ISDN number help description. */
|
||||||
char *uid_orig; /* Login user ID original value given at command line. */
|
int qos_arg;
|
||||||
const char *uid_help; /* Login user ID help description. */
|
/**< @brief Requested quality of service (default='0x000b921f'). */
|
||||||
char *pwd_arg; /* Login password (default='hemmelig'). */
|
char *qos_orig;
|
||||||
char *pwd_orig; /* Login password original value given at command line. */
|
/**< @brief Requested quality of service original value given at command line. */
|
||||||
const char *pwd_help; /* Login password help description. */
|
const char *qos_help;
|
||||||
int createif_flag; /* Create local network interface (default=off). */
|
/**< @brief Requested quality of service help description. */
|
||||||
const char *createif_help; /* Create local network interface help description. */
|
unsigned long long int qose1_arg;
|
||||||
char *net_arg; /* Network address for local interface. */
|
/**< @brief Requested quality of service Extension 1 (default='0x9396404074f9ffff'). */
|
||||||
char *net_orig; /* Network address for local interface original value given at command line. */
|
char *qose1_orig;
|
||||||
const char *net_help; /* Network address for local interface help description. */
|
/**< @brief Requested quality of service Extension 1 original value given at command line. */
|
||||||
int defaultroute_flag; /* Create default route (default=off). */
|
const char *qose1_help;
|
||||||
const char *defaultroute_help; /* Create default route help description. */
|
/**< @brief Requested quality of service Extension 1 help description. */
|
||||||
char *ipup_arg; /* Script to run after link-up. */
|
int qose2_arg;
|
||||||
char *ipup_orig; /* Script to run after link-up original value given at command line. */
|
/**< @brief Requested quality of service Extension 2 (default='0x11'). */
|
||||||
const char *ipup_help; /* Script to run after link-up help description. */
|
char *qose2_orig;
|
||||||
char *ipdown_arg; /* Script to run after link-down. */
|
/**< @brief Requested quality of service Extension 2 original value given at command line. */
|
||||||
char *ipdown_orig; /* Script to run after link-down original value given at command line. */
|
const char *qose2_help;
|
||||||
const char *ipdown_help; /* Script to run after link-down help description. */
|
/**< @brief Requested quality of service Extension 2 help description. */
|
||||||
char *pinghost_arg; /* Ping remote host. */
|
int qose3_arg;
|
||||||
char *pinghost_orig; /* Ping remote host original value given at command line. */
|
/**< @brief Requested quality of service Extension 3 (default='0x0101'). */
|
||||||
const char *pinghost_help; /* Ping remote host help description. */
|
char *qose3_orig;
|
||||||
int pingrate_arg; /* Number of ping req per second (default='1'). */
|
/**< @brief Requested quality of service Extension 3 original value given at command line. */
|
||||||
char *pingrate_orig; /* Number of ping req per second original value given at command line. */
|
const char *qose3_help;
|
||||||
const char *pingrate_help; /* Number of ping req per second help description. */
|
/**< @brief Requested quality of service Extension 3 help description. */
|
||||||
int pingsize_arg; /* Number of ping data bytes (default='56'). */
|
int qose4_arg;
|
||||||
char *pingsize_orig; /* Number of ping data bytes original value given at command line. */
|
/**< @brief Requested quality of service Extension 4 (default='0x4040'). */
|
||||||
const char *pingsize_help; /* Number of ping data bytes help description. */
|
char *qose4_orig;
|
||||||
int pingcount_arg; /* Number of ping req to send (default='0'). */
|
/**< @brief Requested quality of service Extension 4 original value given at command line. */
|
||||||
char *pingcount_orig; /* Number of ping req to send original value given at command line. */
|
const char *qose4_help;
|
||||||
const char *pingcount_help; /* Number of ping req to send help description. */
|
/**< @brief Requested quality of service Extension 4 help description. */
|
||||||
int pingquiet_flag; /* Do not print ping packet info (default=off). */
|
int charging_arg;
|
||||||
const char *pingquiet_help; /* Do not print ping packet info help description. */
|
/**< @brief Charging characteristics (default='0x0800'). */
|
||||||
int norecovery_flag; /* Do not print ping packet info (default=off). */
|
char *charging_orig;
|
||||||
const char *norecovery_help; /* Do not print ping packet info help description. */
|
/**< @brief Charging characteristics original value given at command line. */
|
||||||
|
const char *charging_help;
|
||||||
|
/**< @brief Charging characteristics help description. */
|
||||||
|
char *uid_arg;
|
||||||
|
/**< @brief Login user ID (default='mig'). */
|
||||||
|
char *uid_orig;
|
||||||
|
/**< @brief Login user ID original value given at command line. */
|
||||||
|
const char *uid_help;
|
||||||
|
/**< @brief Login user ID help description. */
|
||||||
|
char *pwd_arg;
|
||||||
|
/**< @brief Login password (default='hemmelig'). */
|
||||||
|
char *pwd_orig;
|
||||||
|
/**< @brief Login password original value given at command line. */
|
||||||
|
const char *pwd_help;
|
||||||
|
/**< @brief Login password help description. */
|
||||||
|
int createif_flag;
|
||||||
|
/**< @brief Create local network interface (default=off). */
|
||||||
|
const char *createif_help;
|
||||||
|
/**< @brief Create local network interface help description. */
|
||||||
|
char *net_arg;
|
||||||
|
/**< @brief Network address for local interface. */
|
||||||
|
char *net_orig;
|
||||||
|
/**< @brief Network address for local interface original value given at command line. */
|
||||||
|
const char *net_help;
|
||||||
|
/**< @brief Network address for local interface help description. */
|
||||||
|
int defaultroute_flag;
|
||||||
|
/**< @brief Create default route (default=off). */
|
||||||
|
const char *defaultroute_help;
|
||||||
|
/**< @brief Create default route help description. */
|
||||||
|
char *ipup_arg;
|
||||||
|
/**< @brief Script to run after link-up. */
|
||||||
|
char *ipup_orig;
|
||||||
|
/**< @brief Script to run after link-up original value given at command line. */
|
||||||
|
const char *ipup_help;
|
||||||
|
/**< @brief Script to run after link-up help description. */
|
||||||
|
char *ipdown_arg;
|
||||||
|
/**< @brief Script to run after link-down. */
|
||||||
|
char *ipdown_orig;
|
||||||
|
/**< @brief Script to run after link-down original value given at command line. */
|
||||||
|
const char *ipdown_help;
|
||||||
|
/**< @brief Script to run after link-down help description. */
|
||||||
|
char *tun_device_arg;
|
||||||
|
/**< @brief Name of the local network interface. */
|
||||||
|
char *tun_device_orig;
|
||||||
|
/**< @brief Name of the local network interface original value given at command line. */
|
||||||
|
const char *tun_device_help;
|
||||||
|
/**< @brief Name of the local network interface help description. */
|
||||||
|
char *pinghost_arg;
|
||||||
|
/**< @brief Ping remote host. */
|
||||||
|
char *pinghost_orig;
|
||||||
|
/**< @brief Ping remote host original value given at command line. */
|
||||||
|
const char *pinghost_help;
|
||||||
|
/**< @brief Ping remote host help description. */
|
||||||
|
int pingrate_arg;
|
||||||
|
/**< @brief Number of ping req per second (default='1'). */
|
||||||
|
char *pingrate_orig;
|
||||||
|
/**< @brief Number of ping req per second original value given at command line. */
|
||||||
|
const char *pingrate_help;
|
||||||
|
/**< @brief Number of ping req per second help description. */
|
||||||
|
int pingsize_arg;
|
||||||
|
/**< @brief Number of ping data bytes (default='56'). */
|
||||||
|
char *pingsize_orig;
|
||||||
|
/**< @brief Number of ping data bytes original value given at command line. */
|
||||||
|
const char *pingsize_help;
|
||||||
|
/**< @brief Number of ping data bytes help description. */
|
||||||
|
int pingcount_arg;
|
||||||
|
/**< @brief Number of ping req to send (default='0'). */
|
||||||
|
char *pingcount_orig;
|
||||||
|
/**< @brief Number of ping req to send original value given at command line. */
|
||||||
|
const char *pingcount_help;
|
||||||
|
/**< @brief Number of ping req to send help description. */
|
||||||
|
int pingquiet_flag;
|
||||||
|
/**< @brief Do not print ping packet info (default=off). */
|
||||||
|
const char *pingquiet_help;
|
||||||
|
/**< @brief Do not print ping packet info help description. */
|
||||||
|
int no_tx_gpdu_seq_flag;
|
||||||
|
/**< @brief Don't transmit G-PDU sequence nums (default=off). */
|
||||||
|
const char *no_tx_gpdu_seq_help;
|
||||||
|
/**< @brief Don't transmit G-PDU sequence nums help description. */
|
||||||
|
char *pdp_type_arg;
|
||||||
|
/**< @brief PDP Type (default='v4'). */
|
||||||
|
char *pdp_type_orig;
|
||||||
|
/**< @brief PDP Type original value given at command line. */
|
||||||
|
const char *pdp_type_help;
|
||||||
|
/**< @brief PDP Type help description. */
|
||||||
|
|
||||||
int help_given; /* Whether help was given. */
|
unsigned int help_given;
|
||||||
int version_given; /* Whether version was given. */
|
/**< @brief Whether help was given. */
|
||||||
int debug_given; /* Whether debug was given. */
|
unsigned int version_given;
|
||||||
int conf_given; /* Whether conf was given. */
|
/**< @brief Whether version was given. */
|
||||||
int pidfile_given; /* Whether pidfile was given. */
|
unsigned int debug_given;
|
||||||
int statedir_given; /* Whether statedir was given. */
|
/**< @brief Whether debug was given. */
|
||||||
int dns_given; /* Whether dns was given. */
|
unsigned int conf_given;
|
||||||
int listen_given; /* Whether listen was given. */
|
/**< @brief Whether conf was given. */
|
||||||
int remote_given; /* Whether remote was given. */
|
unsigned int pidfile_given;
|
||||||
int contexts_given; /* Whether contexts was given. */
|
/**< @brief Whether pidfile was given. */
|
||||||
int timelimit_given; /* Whether timelimit was given. */
|
unsigned int statedir_given;
|
||||||
int gtpversion_given; /* Whether gtpversion was given. */
|
/**< @brief Whether statedir was given. */
|
||||||
int apn_given; /* Whether apn was given. */
|
unsigned int dns_given;
|
||||||
int selmode_given; /* Whether selmode was given. */
|
/**< @brief Whether dns was given. */
|
||||||
int rattype_given; /* Whether rattype was given. */
|
unsigned int listen_given;
|
||||||
int userloc_given; /* Whether userloc was given. */
|
/**< @brief Whether listen was given. */
|
||||||
int rai_given; /* Whether RAI was given. */
|
unsigned int remote_given;
|
||||||
int mstz_given; /* Whether mstz was given. */
|
/**< @brief Whether remote was given. */
|
||||||
int imeisv_given; /* Whether imeisv was given. */
|
unsigned int contexts_given;
|
||||||
int imsi_given; /* Whether imsi was given. */
|
/**< @brief Whether contexts was given. */
|
||||||
int nsapi_given; /* Whether nsapi was given. */
|
unsigned int timelimit_given;
|
||||||
int msisdn_given; /* Whether msisdn was given. */
|
/**< @brief Whether timelimit was given. */
|
||||||
int qos_given; /* Whether qos was given. */
|
unsigned int gtpversion_given;
|
||||||
int qose1_given; /* Whether qos Extension 1 was given. */
|
/**< @brief Whether gtpversion was given. */
|
||||||
int qose2_given; /* Whether qos Extension 2 was given. */
|
unsigned int apn_given;
|
||||||
int qose3_given; /* Whether qos Extension 3 was given. */
|
/**< @brief Whether apn was given. */
|
||||||
int qose4_given; /* Whether qos Extension 4 was given. */
|
unsigned int selmode_given;
|
||||||
int charging_given; /* Whether charging was given. */
|
/**< @brief Whether selmode was given. */
|
||||||
int uid_given; /* Whether uid was given. */
|
unsigned int rattype_given;
|
||||||
int pwd_given; /* Whether pwd was given. */
|
/**< @brief Whether rattype was given. */
|
||||||
int createif_given; /* Whether createif was given. */
|
unsigned int userloc_given;
|
||||||
int net_given; /* Whether net was given. */
|
/**< @brief Whether userloc was given. */
|
||||||
int defaultroute_given; /* Whether defaultroute was given. */
|
unsigned int rai_given;
|
||||||
int ipup_given; /* Whether ipup was given. */
|
/**< @brief Whether rai was given. */
|
||||||
int ipdown_given; /* Whether ipdown was given. */
|
unsigned int mstz_given;
|
||||||
int pinghost_given; /* Whether pinghost was given. */
|
/**< @brief Whether mstz was given. */
|
||||||
int pingrate_given; /* Whether pingrate was given. */
|
unsigned int imeisv_given;
|
||||||
int pingsize_given; /* Whether pingsize was given. */
|
/**< @brief Whether imeisv was given. */
|
||||||
int pingcount_given; /* Whether pingcount was given. */
|
unsigned int norecovery_given;
|
||||||
int pingquiet_given; /* Whether pingquiet was given. */
|
/**< @brief Whether norecovery was given. */
|
||||||
int norecovery_given; /* Whether norecovery was given. */
|
unsigned int imsi_given;
|
||||||
|
/**< @brief Whether imsi was given. */
|
||||||
|
unsigned int nsapi_given;
|
||||||
|
/**< @brief Whether nsapi was given. */
|
||||||
|
unsigned int msisdn_given;
|
||||||
|
/**< @brief Whether msisdn was given. */
|
||||||
|
unsigned int qos_given;
|
||||||
|
/**< @brief Whether qos was given. */
|
||||||
|
unsigned int qose1_given;
|
||||||
|
/**< @brief Whether qose1 was given. */
|
||||||
|
unsigned int qose2_given;
|
||||||
|
/**< @brief Whether qose2 was given. */
|
||||||
|
unsigned int qose3_given;
|
||||||
|
/**< @brief Whether qose3 was given. */
|
||||||
|
unsigned int qose4_given;
|
||||||
|
/**< @brief Whether qose4 was given. */
|
||||||
|
unsigned int charging_given;
|
||||||
|
/**< @brief Whether charging was given. */
|
||||||
|
unsigned int uid_given;
|
||||||
|
/**< @brief Whether uid was given. */
|
||||||
|
unsigned int pwd_given;
|
||||||
|
/**< @brief Whether pwd was given. */
|
||||||
|
unsigned int createif_given;
|
||||||
|
/**< @brief Whether createif was given. */
|
||||||
|
unsigned int net_given;
|
||||||
|
/**< @brief Whether net was given. */
|
||||||
|
unsigned int defaultroute_given;
|
||||||
|
/**< @brief Whether defaultroute was given. */
|
||||||
|
unsigned int ipup_given;
|
||||||
|
/**< @brief Whether ipup was given. */
|
||||||
|
unsigned int ipdown_given;
|
||||||
|
/**< @brief Whether ipdown was given. */
|
||||||
|
unsigned int tun_device_given;
|
||||||
|
/**< @brief Whether tun-device was given. */
|
||||||
|
unsigned int pinghost_given;
|
||||||
|
/**< @brief Whether pinghost was given. */
|
||||||
|
unsigned int pingrate_given;
|
||||||
|
/**< @brief Whether pingrate was given. */
|
||||||
|
unsigned int pingsize_given;
|
||||||
|
/**< @brief Whether pingsize was given. */
|
||||||
|
unsigned int pingcount_given;
|
||||||
|
/**< @brief Whether pingcount was given. */
|
||||||
|
unsigned int pingquiet_given;
|
||||||
|
/**< @brief Whether pingquiet was given. */
|
||||||
|
unsigned int no_tx_gpdu_seq_given;
|
||||||
|
/**< @brief Whether no-tx-gpdu-seq was given. */
|
||||||
|
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 */
|
||||||
|
struct cmdline_parser_params {
|
||||||
|
int override;
|
||||||
|
/**< @brief whether to override possibly already present options (default 0) */
|
||||||
|
int initialize;
|
||||||
|
/**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
|
||||||
|
int check_required;
|
||||||
|
/**< @brief whether to check that all required options were provided (default 1) */
|
||||||
|
int check_ambiguity;
|
||||||
|
/**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
|
||||||
|
int print_errors;
|
||||||
|
/**< @brief whether getopt_long should print an error message for a bad option (default 1) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief the purpose string of the program */
|
||||||
extern const char *gengetopt_args_info_purpose;
|
extern const char *gengetopt_args_info_purpose;
|
||||||
|
/** @brief the usage string of the program */
|
||||||
extern const char *gengetopt_args_info_usage;
|
extern const char *gengetopt_args_info_usage;
|
||||||
|
/** @brief the description string of the program */
|
||||||
|
extern const char *gengetopt_args_info_description;
|
||||||
|
/** @brief all the lines making the help output */
|
||||||
extern const char *gengetopt_args_info_help[];
|
extern const char *gengetopt_args_info_help[];
|
||||||
|
|
||||||
int cmdline_parser(int argc, char *const *argv,
|
/**
|
||||||
|
* The command line parser
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser(int argc, char **argv,
|
||||||
struct gengetopt_args_info *args_info);
|
struct gengetopt_args_info *args_info);
|
||||||
int cmdline_parser2(int argc, char *const *argv,
|
|
||||||
|
/**
|
||||||
|
* The command line parser (version with additional parameters - deprecated)
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param override whether to override possibly already present options
|
||||||
|
* @param initialize whether to initialize the option structure my_args_info
|
||||||
|
* @param check_required whether to check that all required options were provided
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
* @deprecated use cmdline_parser_ext() instead
|
||||||
|
*/
|
||||||
|
int cmdline_parser2(int argc, char **argv,
|
||||||
struct gengetopt_args_info *args_info,
|
struct gengetopt_args_info *args_info,
|
||||||
int override, int initialize, int check_required);
|
int override, int initialize, int check_required);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command line parser (version with additional parameters)
|
||||||
|
* @param argc the number of command line options
|
||||||
|
* @param argv the command line options
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param params additional parameters for the parser
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_ext(int argc, char **argv,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the contents of the option struct into an already open FILE stream.
|
||||||
|
* @param outfile the stream where to dump options
|
||||||
|
* @param args_info the option struct to dump
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_dump(FILE * outfile,
|
||||||
|
struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the contents of the option struct into a (text) file.
|
||||||
|
* This file can be read by the config file parser (if generated by gengetopt)
|
||||||
|
* @param filename the file where to save
|
||||||
|
* @param args_info the option struct to save
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
int cmdline_parser_file_save(const char *filename,
|
int cmdline_parser_file_save(const char *filename,
|
||||||
struct gengetopt_args_info *args_info);
|
struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the help
|
||||||
|
*/
|
||||||
void cmdline_parser_print_help(void);
|
void cmdline_parser_print_help(void);
|
||||||
|
/**
|
||||||
|
* Print the version
|
||||||
|
*/
|
||||||
void cmdline_parser_print_version(void);
|
void cmdline_parser_print_version(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all the fields a cmdline_parser_params structure
|
||||||
|
* to their default values
|
||||||
|
* @param params the structure to initialize
|
||||||
|
*/
|
||||||
|
void cmdline_parser_params_init(struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates dynamically a cmdline_parser_params structure and initializes
|
||||||
|
* all its fields to their default values
|
||||||
|
* @return the created and initialized cmdline_parser_params structure
|
||||||
|
*/
|
||||||
|
struct cmdline_parser_params *cmdline_parser_params_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the passed gengetopt_args_info structure's fields
|
||||||
|
* (also set default values for options that have a default)
|
||||||
|
* @param args_info the structure to initialize
|
||||||
|
*/
|
||||||
void cmdline_parser_init(struct gengetopt_args_info *args_info);
|
void cmdline_parser_init(struct gengetopt_args_info *args_info);
|
||||||
|
/**
|
||||||
|
* Deallocates the string fields of the gengetopt_args_info structure
|
||||||
|
* (but does not deallocate the structure itself)
|
||||||
|
* @param args_info the structure to deallocate
|
||||||
|
*/
|
||||||
void cmdline_parser_free(struct gengetopt_args_info *args_info);
|
void cmdline_parser_free(struct gengetopt_args_info *args_info);
|
||||||
|
|
||||||
int cmdline_parser_configfile(char *const filename,
|
/**
|
||||||
|
* The config file parser (deprecated version)
|
||||||
|
* @param filename the name of the config file
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param override whether to override possibly already present options
|
||||||
|
* @param initialize whether to initialize the option structure my_args_info
|
||||||
|
* @param check_required whether to check that all required options were provided
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
* @deprecated use cmdline_parser_config_file() instead
|
||||||
|
*/
|
||||||
|
int cmdline_parser_configfile(const char *filename,
|
||||||
struct gengetopt_args_info *args_info,
|
struct gengetopt_args_info *args_info,
|
||||||
int override, int initialize,
|
int override, int initialize,
|
||||||
int check_required);
|
int check_required);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config file parser
|
||||||
|
* @param filename the name of the config file
|
||||||
|
* @param args_info the structure where option information will be stored
|
||||||
|
* @param params additional parameters for the parser
|
||||||
|
* @return 0 if everything went fine, NON 0 if an error took place
|
||||||
|
*/
|
||||||
|
int cmdline_parser_config_file(const char *filename,
|
||||||
|
struct gengetopt_args_info *args_info,
|
||||||
|
struct cmdline_parser_params *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that all the required options were specified
|
||||||
|
* @param args_info the structure to check
|
||||||
|
* @param prog_name the name of the program that will be used to print
|
||||||
|
* possible errors
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int cmdline_parser_required(struct gengetopt_args_info *args_info,
|
int cmdline_parser_required(struct gengetopt_args_info *args_info,
|
||||||
const char *prog_name);
|
const char *prog_name);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* OsmoGGSN - Gateway GPRS Support Node
|
* OsmoGGSN - Gateway GPRS Support Node
|
||||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
* 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
|
* The contents of this file may be used under the terms of the GNU
|
||||||
* General Public License Version 2, provided that the above copyright
|
* General Public License Version 2, provided that the above copyright
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -77,12 +80,14 @@ struct gsn_t *gsn = NULL; /* GSN instance */
|
|||||||
struct tun_t *tun = NULL; /* TUN instance */
|
struct tun_t *tun = NULL; /* TUN instance */
|
||||||
int maxfd = 0; /* For select() */
|
int maxfd = 0; /* For select() */
|
||||||
int echoversion = 1; /* First try this version */
|
int echoversion = 1; /* First try this version */
|
||||||
|
void *tall_sgsnemu_ctx; /* root talloc ctx */
|
||||||
|
|
||||||
/* Struct with local versions of gengetopt options */
|
/* Struct with local versions of gengetopt options */
|
||||||
struct {
|
struct {
|
||||||
int debug; /* Print debug messages */
|
int debug; /* Print debug messages */
|
||||||
int createif; /* Create local network interface */
|
int createif; /* Create local network interface */
|
||||||
struct in_addr netaddr, destaddr, net; /* Network interface */
|
char *tun_dev_name;
|
||||||
|
struct in46_addr netaddr, destaddr, net; /* Network interface */
|
||||||
size_t prefixlen;
|
size_t prefixlen;
|
||||||
char *ipup, *ipdown; /* Filename of scripts */
|
char *ipup, *ipdown; /* Filename of scripts */
|
||||||
int defaultroute; /* Set up default route */
|
int defaultroute; /* Set up default route */
|
||||||
@@ -117,6 +122,8 @@ struct {
|
|||||||
int imeisv_given;
|
int imeisv_given;
|
||||||
struct ul16_t msisdn;
|
struct ul16_t msisdn;
|
||||||
int norecovery_given;
|
int norecovery_given;
|
||||||
|
int tx_gpdu_seq;
|
||||||
|
uint8_t pdp_type;
|
||||||
} options;
|
} options;
|
||||||
|
|
||||||
/* Definitions to use for PING. Most of the ping code was derived from */
|
/* Definitions to use for PING. Most of the ping code was derived from */
|
||||||
@@ -156,13 +163,13 @@ int tsum = 0;
|
|||||||
int pingseq = 0; /* Ping sequence counter */
|
int pingseq = 0; /* Ping sequence counter */
|
||||||
struct timeval firstping;
|
struct timeval firstping;
|
||||||
|
|
||||||
void signal_handler(int signo)
|
static void signal_handler(int signo)
|
||||||
{
|
{
|
||||||
if (state == 2)
|
if (state == 2)
|
||||||
state = 3; /* Tell main loop to finish. */
|
state = 3; /* Tell main loop to finish. */
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
|
static int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
|
||||||
{
|
{
|
||||||
int hash = ippool_hash(addr) % MAXCONTEXTS;
|
int hash = ippool_hash(addr) % MAXCONTEXTS;
|
||||||
struct iphash_t *h;
|
struct iphash_t *h;
|
||||||
@@ -178,7 +185,7 @@ int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipdel(struct iphash_t *ipaddr)
|
static int ipdel(struct iphash_t *ipaddr)
|
||||||
{
|
{
|
||||||
int hash = ippool_hash(&ipaddr->addr) % MAXCONTEXTS;
|
int hash = ippool_hash(&ipaddr->addr) % MAXCONTEXTS;
|
||||||
struct iphash_t *h;
|
struct iphash_t *h;
|
||||||
@@ -196,7 +203,7 @@ int ipdel(struct iphash_t *ipaddr)
|
|||||||
return EOF; /* End of linked list and not found */
|
return EOF; /* End of linked list and not found */
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
|
static int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
|
||||||
{
|
{
|
||||||
int hash = ippool_hash(addr) % MAXCONTEXTS;
|
int hash = ippool_hash(addr) % MAXCONTEXTS;
|
||||||
struct iphash_t *h;
|
struct iphash_t *h;
|
||||||
@@ -210,7 +217,7 @@ int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Used to write process ID to file. Assume someone else will delete */
|
/* Used to write process ID to file. Assume someone else will delete */
|
||||||
void log_pid(char *pidfile)
|
static void log_pid(char *pidfile)
|
||||||
{
|
{
|
||||||
FILE *file;
|
FILE *file;
|
||||||
mode_t oldmask;
|
mode_t oldmask;
|
||||||
@@ -224,7 +231,7 @@ void log_pid(char *pidfile)
|
|||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_options(int argc, char **argv)
|
static int process_options(int argc, char **argv)
|
||||||
{
|
{
|
||||||
/* gengeopt declarations */
|
/* gengeopt declarations */
|
||||||
struct gengetopt_args_info args_info;
|
struct gengetopt_args_info args_info;
|
||||||
@@ -285,6 +292,8 @@ int process_options(int argc, char **argv)
|
|||||||
printf("contexts: %d\n", args_info.contexts_arg);
|
printf("contexts: %d\n", args_info.contexts_arg);
|
||||||
printf("timelimit: %d\n", args_info.timelimit_arg);
|
printf("timelimit: %d\n", args_info.timelimit_arg);
|
||||||
printf("createif: %d\n", args_info.createif_flag);
|
printf("createif: %d\n", args_info.createif_flag);
|
||||||
|
if (args_info.tun_device_arg)
|
||||||
|
printf("tun-device: %d\n", args_info.tun_device_arg);
|
||||||
if (args_info.ipup_arg)
|
if (args_info.ipup_arg)
|
||||||
printf("ipup: %s\n", args_info.ipup_arg);
|
printf("ipup: %s\n", args_info.ipup_arg);
|
||||||
if (args_info.ipdown_arg)
|
if (args_info.ipdown_arg)
|
||||||
@@ -297,6 +306,7 @@ int process_options(int argc, char **argv)
|
|||||||
printf("pingcount: %d\n", args_info.pingcount_arg);
|
printf("pingcount: %d\n", args_info.pingcount_arg);
|
||||||
printf("pingquiet: %d\n", args_info.pingquiet_flag);
|
printf("pingquiet: %d\n", args_info.pingquiet_flag);
|
||||||
printf("norecovery: %d\n", args_info.norecovery_flag);
|
printf("norecovery: %d\n", args_info.norecovery_flag);
|
||||||
|
printf("no-tx-gpdu-seq: %d\n", args_info.no_tx_gpdu_seq_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try out our new parser */
|
/* Try out our new parser */
|
||||||
@@ -340,6 +350,8 @@ int process_options(int argc, char **argv)
|
|||||||
printf("contexts: %d\n", args_info.contexts_arg);
|
printf("contexts: %d\n", args_info.contexts_arg);
|
||||||
printf("timelimit: %d\n", args_info.timelimit_arg);
|
printf("timelimit: %d\n", args_info.timelimit_arg);
|
||||||
printf("createif: %d\n", args_info.createif_flag);
|
printf("createif: %d\n", args_info.createif_flag);
|
||||||
|
if (args_info.tun_device_arg)
|
||||||
|
printf("tun-device: %s\n", args_info.tun_device_arg);
|
||||||
if (args_info.ipup_arg)
|
if (args_info.ipup_arg)
|
||||||
printf("ipup: %s\n", args_info.ipup_arg);
|
printf("ipup: %s\n", args_info.ipup_arg);
|
||||||
if (args_info.ipdown_arg)
|
if (args_info.ipdown_arg)
|
||||||
@@ -354,6 +366,7 @@ int process_options(int argc, char **argv)
|
|||||||
printf("pingcount: %d\n", args_info.pingcount_arg);
|
printf("pingcount: %d\n", args_info.pingcount_arg);
|
||||||
printf("pingquiet: %d\n", args_info.pingquiet_flag);
|
printf("pingquiet: %d\n", args_info.pingquiet_flag);
|
||||||
printf("norecovery: %d\n", args_info.norecovery_flag);
|
printf("norecovery: %d\n", args_info.norecovery_flag);
|
||||||
|
printf("no-tx-gpdu-seq: %d\n", args_info.no_tx_gpdu_seq_flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,9 +578,9 @@ int process_options(int argc, char **argv)
|
|||||||
/* rattype */
|
/* rattype */
|
||||||
if (args_info.rattype_given == 1) {
|
if (args_info.rattype_given == 1) {
|
||||||
options.rattype_given = 1;
|
options.rattype_given = 1;
|
||||||
options.rattype.l = strlen(args_info.rattype_arg);
|
options.rattype.l = 1;
|
||||||
options.rattype.v[0] = atoi(args_info.rattype_arg);
|
options.rattype.v[0] = args_info.rattype_arg;
|
||||||
printf("Using RAT Type: %s\n", args_info.rattype_arg);
|
printf("Using RAT Type: %d\n", args_info.rattype_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* userloc */
|
/* userloc */
|
||||||
@@ -857,27 +870,26 @@ int process_options(int argc, char **argv)
|
|||||||
|
|
||||||
/* createif */
|
/* createif */
|
||||||
options.createif = args_info.createif_flag;
|
options.createif = args_info.createif_flag;
|
||||||
|
options.tun_dev_name = args_info.tun_device_arg;
|
||||||
|
|
||||||
/* net */
|
/* net */
|
||||||
/* Store net as in_addr net and mask */
|
/* Store net as in_addr net and mask */
|
||||||
if (args_info.net_arg) {
|
if (args_info.net_arg) {
|
||||||
struct in46_addr in46;
|
|
||||||
if (ippool_aton
|
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,
|
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||||
"Invalid network address: %s!",
|
"Invalid network address: %s!",
|
||||||
args_info.net_arg);
|
args_info.net_arg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
options.net.s_addr = in46.v4.s_addr;
|
options.netaddr = options.net;
|
||||||
options.netaddr.s_addr = options.net.s_addr;
|
options.destaddr = options.net;
|
||||||
options.destaddr.s_addr = options.net.s_addr;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
options.net.s_addr = 0;
|
memset(&options.net, 0, sizeof(options.net));
|
||||||
options.prefixlen = 0;
|
options.prefixlen = 0;
|
||||||
options.netaddr.s_addr = 0;
|
memset(&options.netaddr, 0, sizeof(options.netaddr));
|
||||||
options.destaddr.s_addr = 0;
|
memset(&options.destaddr, 0, sizeof(options.destaddr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ipup */
|
/* ipup */
|
||||||
@@ -918,38 +930,77 @@ int process_options(int argc, char **argv)
|
|||||||
/* norecovery */
|
/* norecovery */
|
||||||
options.norecovery_given = args_info.norecovery_flag;
|
options.norecovery_given = args_info.norecovery_flag;
|
||||||
|
|
||||||
|
if (args_info.no_tx_gpdu_seq_flag)
|
||||||
|
options.tx_gpdu_seq = 0;
|
||||||
|
else
|
||||||
|
options.tx_gpdu_seq = 1;
|
||||||
|
|
||||||
|
/* PDP Type */
|
||||||
|
if (!strcmp(args_info.pdp_type_arg, "v6"))
|
||||||
|
options.pdp_type = PDP_EUA_TYPE_v6;
|
||||||
|
else if (!strcmp(args_info.pdp_type_arg, "v4"))
|
||||||
|
options.pdp_type = PDP_EUA_TYPE_v4;
|
||||||
|
else {
|
||||||
|
SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unsupported/unknown PDP Type '%s'\n",
|
||||||
|
args_info.pdp_type_arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.pingcount && options.pdp_type != PDP_EUA_TYPE_v4) {
|
||||||
|
SYS_ERR(DSGSN, LOGL_ERROR, 0, "built-in ping only works with IPv4, use tun-device");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int encaps_printf(struct pdp_t *pdp, void *pack, unsigned len)
|
/* read a single value from a /procc file, up to 255 bytes, callee-allocated */
|
||||||
|
static char *proc_read(const char *path)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
char *ret = NULL;
|
||||||
printf("The packet looks like this:\n");
|
FILE *f;
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
printf("%02x ", (unsigned char)*(char *)(pack + i));
|
f = fopen(path, "r");
|
||||||
if (!((i + 1) % 16))
|
if (!f)
|
||||||
printf("\n");
|
return NULL;
|
||||||
};
|
|
||||||
printf("\n");
|
ret = malloc(256);
|
||||||
return 0;
|
if (!ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!fgets(ret, 256, f)) {
|
||||||
|
free(ret);
|
||||||
|
ret = NULL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *print_ipprot(int t)
|
out:
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read value of a /proc/sys/net/ipv6/conf file for given device.
|
||||||
|
* Memory is dynamically allocated, caller must free it later. */
|
||||||
|
static char *proc_ipv6_conf_read(const char *dev, const char *file)
|
||||||
{
|
{
|
||||||
switch (t) {
|
const char *fmt = "/proc/sys/net/ipv6/conf/%s/%s";
|
||||||
case 1:
|
char path[strlen(fmt) + strlen(dev) + strlen(file)+1];
|
||||||
return "ICMP";
|
snprintf(path, sizeof(path), fmt, dev, file);
|
||||||
case 6:
|
return proc_read(path);
|
||||||
return "TCP";
|
}
|
||||||
case 17:
|
|
||||||
return "UDP";
|
static char *print_ipprot(int t)
|
||||||
default:
|
{
|
||||||
|
struct protoent *pe = getprotobynumber(t);
|
||||||
|
|
||||||
|
if (!pe)
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
};
|
else
|
||||||
|
return pe->p_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *print_icmptype(int t)
|
static char *print_icmptype(int t)
|
||||||
{
|
{
|
||||||
static char *ttab[] = {
|
static char *ttab[] = {
|
||||||
"Echo Reply",
|
"Echo Reply",
|
||||||
@@ -975,7 +1026,7 @@ char *print_icmptype(int t)
|
|||||||
return (ttab[t]);
|
return (ttab[t]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
|
static int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
|
||||||
{
|
{
|
||||||
unsigned int n;
|
unsigned int n;
|
||||||
uint64_t i64 = 0;
|
uint64_t i64 = 0;
|
||||||
@@ -1019,7 +1070,7 @@ int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int imsi_add(uint64_t src, uint64_t * dst, int add)
|
static int imsi_add(uint64_t src, uint64_t * dst, int add)
|
||||||
{
|
{
|
||||||
/* TODO: big endian / small endian ??? */
|
/* TODO: big endian / small endian ??? */
|
||||||
uint64_t i64 = 0;
|
uint64_t i64 = 0;
|
||||||
@@ -1054,7 +1105,7 @@ int imsi_add(uint64_t src, uint64_t * dst, int add)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate time left until we have to send off next ping packet */
|
/* Calculate time left until we have to send off next ping packet */
|
||||||
int ping_timeout(struct timeval *tp)
|
static int ping_timeout(struct timeval *tp)
|
||||||
{
|
{
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -1076,7 +1127,7 @@ int ping_timeout(struct timeval *tp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Print out statistics when at the end of ping sequence */
|
/* Print out statistics when at the end of ping sequence */
|
||||||
int ping_finish()
|
static int ping_finish()
|
||||||
{
|
{
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -1109,7 +1160,7 @@ int ping_finish()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a received ping packet. Print out line and update statistics. */
|
/* Handle a received ping packet. Print out line and update statistics. */
|
||||||
int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -1178,7 +1229,7 @@ int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new ping packet and send it off to peer. */
|
/* Create a new ping packet and send it off to peer. */
|
||||||
int create_ping(void *gsn, struct pdp_t *pdp,
|
static int create_ping(void *gsn, struct pdp_t *pdp,
|
||||||
struct in_addr *dst, int seq, unsigned int datasize)
|
struct in_addr *dst, int seq, unsigned int datasize)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -1257,14 +1308,14 @@ int create_ping(void *gsn, struct pdp_t *pdp,
|
|||||||
return gtp_data_req(gsn, pdp, &pack, 28 + datasize);
|
return gtp_data_req(gsn, pdp, &pack, 28 + datasize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int delete_context(struct pdp_t *pdp)
|
static int delete_context(struct pdp_t *pdp)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (tun && options.ipdown)
|
if (tun && options.ipdown)
|
||||||
tun_runscript(tun, options.ipdown);
|
tun_runscript(tun, options.ipdown);
|
||||||
|
|
||||||
ipdel((struct iphash_t *)pdp->peer);
|
ipdel((struct iphash_t *)pdp->peer[0]);
|
||||||
memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */
|
memset(pdp->peer[0], 0, sizeof(struct iphash_t)); /* To be sure */
|
||||||
|
|
||||||
if (1 == options.contexts)
|
if (1 == options.contexts)
|
||||||
state = 5; /* Disconnected */
|
state = 5; /* Disconnected */
|
||||||
@@ -1272,19 +1323,46 @@ int delete_context(struct pdp_t *pdp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Link-Local address prefix fe80::/64 */
|
||||||
|
static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
|
||||||
|
|
||||||
/* Callback for receiving messages from tun */
|
/* Callback for receiving messages from tun */
|
||||||
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
struct iphash_t *ipm;
|
struct iphash_t *ipm;
|
||||||
struct in46_addr src;
|
struct in46_addr src;
|
||||||
struct iphdr *iph = (struct iphdr *)pack;
|
struct iphdr *iph = (struct iphdr *)pack;
|
||||||
|
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||||
|
|
||||||
|
if (iph->version == 4) {
|
||||||
|
if (len < sizeof(*iph) || len < 4*iph->ihl) {
|
||||||
|
printf("Dropping packet with too short IP header\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
src.len = 4;
|
src.len = 4;
|
||||||
src.v4.s_addr = iph->saddr;
|
src.v4.s_addr = iph->saddr;
|
||||||
|
} else if (iph->version == 6) {
|
||||||
|
/* We only have a single entry in the hash table, and it consists of the link-local
|
||||||
|
* address "fe80::prefix". So we need to make sure to convert non-link-local source
|
||||||
|
* addresses to that format before looking up the hash table via ippool_getip() */
|
||||||
|
src.len = 16;
|
||||||
|
if (!memcmp(ip6h->ip6_src.s6_addr, ll_prefix, sizeof(ll_prefix))) {
|
||||||
|
/* is a link-local address, we can do the hash lookup 1:1 */
|
||||||
|
src.v6 = ip6h->ip6_src;
|
||||||
|
} else {
|
||||||
|
/* it is not a link-local address, so we must convert from the /64 prefix
|
||||||
|
* to the link-local format that's used in the hash table */
|
||||||
|
memcpy(&src.v6.s6_addr[0], ll_prefix, sizeof(ll_prefix));
|
||||||
|
memcpy(&src.v6.s6_addr[sizeof(ll_prefix)], ip6h->ip6_src.s6_addr, 16-sizeof(ll_prefix));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Dropping packet with invalid IP version %u\n", iph->version);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (ipget(&ipm, &src)) {
|
if (ipget(&ipm, &src)) {
|
||||||
printf("Dropping packet from invalid source address: %s\n",
|
printf("Dropping packet from invalid source address: %s\n",
|
||||||
inet_ntoa(src.v4));
|
in46a_ntoa(&src));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,7 +1371,7 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||||
{
|
{
|
||||||
struct in46_addr addr;
|
struct in46_addr addr;
|
||||||
|
|
||||||
@@ -1324,7 +1402,7 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
|||||||
return EOF; /* Not what we expected */
|
return EOF; /* Not what we expected */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in46a_from_eua(&pdp->eua, &addr)) {
|
if (in46a_from_eua(&pdp->eua, &addr) < 1) {
|
||||||
printf
|
printf
|
||||||
("Received create PDP context response. Cause value: %d\n",
|
("Received create PDP context response. Cause value: %d\n",
|
||||||
cause);
|
cause);
|
||||||
@@ -1335,41 +1413,71 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("Received create PDP context response. IP address: %s\n",
|
printf("Received create PDP context response. IP address: %s\n",
|
||||||
inet_ntoa(addr.v4));
|
in46a_ntoa(&addr));
|
||||||
|
|
||||||
if ((options.createif) && (!options.net.s_addr)) {
|
switch (addr.len) {
|
||||||
struct in_addr m;
|
case 16: /* IPv6 */
|
||||||
#ifdef HAVE_INET_ATON
|
/* we have to enable the kernel to perform stateless autoconfiguration,
|
||||||
inet_aton("255.255.255.255", &m);
|
* i.e. send a router solicitation using the lover 64bits of the allocated
|
||||||
#else
|
* EUA as interface identifier, as per 3GPP TS 29.061 Section 11.2.1.3.2 */
|
||||||
m.s_addr = -1;
|
memcpy(addr.v6.s6_addr, ll_prefix, sizeof(ll_prefix));
|
||||||
#endif
|
printf("Derived IPv6 link-local address: %s\n", in46a_ntoa(&addr));
|
||||||
|
break;
|
||||||
|
case 4: /* IPv4 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((options.createif) && (!options.net.len)) {
|
||||||
|
size_t prefixlen = 32;
|
||||||
|
if (addr.len == 16)
|
||||||
|
prefixlen = 64;
|
||||||
/* printf("Setting up interface and routing\n"); */
|
/* printf("Setting up interface and routing\n"); */
|
||||||
tun_addaddr(tun, &addr.v4, &addr.v4, &m);
|
tun_addaddr(tun, &addr, &addr, prefixlen);
|
||||||
if (options.defaultroute) {
|
if (options.defaultroute) {
|
||||||
struct in_addr rm;
|
struct in_addr rm;
|
||||||
rm.s_addr = 0;
|
rm.s_addr = 0;
|
||||||
tun_addroute(tun, &rm, &addr.v4, &rm);
|
netdev_addroute(&rm, &addr.v4, &rm);
|
||||||
}
|
}
|
||||||
if (options.ipup)
|
if (options.ipup)
|
||||||
tun_runscript(tun, options.ipup);
|
tun_runscript(tun, options.ipup);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipset((struct iphash_t *)pdp->peer, &addr);
|
/* now that ip-up has been executed, check if we are configured to
|
||||||
|
* accept router advertisements */
|
||||||
|
if (options.createif && options.pdp_type == PDP_EUA_TYPE_v6) {
|
||||||
|
char *accept_ra, *forwarding;
|
||||||
|
|
||||||
|
accept_ra = proc_ipv6_conf_read(tun->devname, "accept_ra");
|
||||||
|
forwarding = proc_ipv6_conf_read(tun->devname, "forwarding");
|
||||||
|
if (!accept_ra || !forwarding)
|
||||||
|
printf("Could not open proc file for %s ?!?\n", tun->devname);
|
||||||
|
else {
|
||||||
|
if (!strcmp(accept_ra, "0") ||
|
||||||
|
(!strcmp(forwarding, "1") && !strcmp(accept_ra, "1"))) {
|
||||||
|
printf("%s is %s, i.e. your tun device is not configured to accept "
|
||||||
|
"router advertisements; SLAAC will not suceed, please "
|
||||||
|
"fix your setup!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(accept_ra);
|
||||||
|
free(forwarding);
|
||||||
|
}
|
||||||
|
|
||||||
|
ipset(iph, &addr);
|
||||||
|
|
||||||
state = 2; /* Connected */
|
state = 2; /* Connected */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int delete_pdp_conf(struct pdp_t *pdp, int cause)
|
static int delete_pdp_conf(struct pdp_t *pdp, int cause)
|
||||||
{
|
{
|
||||||
printf("Received delete PDP context response. Cause value: %d\n",
|
printf("Received delete PDP context response. Cause value: %d\n",
|
||||||
cause);
|
cause);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int echo_conf(int recovery)
|
static int echo_conf(int recovery)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (recovery < 0) {
|
if (recovery < 0) {
|
||||||
@@ -1391,7 +1499,7 @@ int echo_conf(int recovery)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
|
static int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
|
||||||
{
|
{
|
||||||
/* if (cause < 0) return 0; Some error occurred. We don't care */
|
/* if (cause < 0) return 0; Some error occurred. We don't care */
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -1408,7 +1516,7 @@ int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||||
{
|
{
|
||||||
/* printf("encaps_tun. Packet received: forwarding to tun\n"); */
|
/* printf("encaps_tun. Packet received: forwarding to tun\n"); */
|
||||||
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
|
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
|
||||||
@@ -1433,7 +1541,9 @@ int main(int argc, char **argv)
|
|||||||
signal(SIGHUP, signal_handler);
|
signal(SIGHUP, signal_handler);
|
||||||
signal(SIGINT, 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 */
|
/* Process options given in configuration file and command line */
|
||||||
if (process_options(argc, argv))
|
if (process_options(argc, argv))
|
||||||
@@ -1461,7 +1571,7 @@ int main(int argc, char **argv)
|
|||||||
if (options.createif) {
|
if (options.createif) {
|
||||||
printf("Setting up interface\n");
|
printf("Setting up interface\n");
|
||||||
/* Create a tunnel interface */
|
/* Create a tunnel interface */
|
||||||
if (tun_new((struct tun_t **)&tun, NULL)) {
|
if (tun_new((struct tun_t **)&tun, options.tun_dev_name, false, -1, -1)) {
|
||||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||||
"Failed to create tun");
|
"Failed to create tun");
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -1471,15 +1581,13 @@ int main(int argc, char **argv)
|
|||||||
maxfd = tun->fd;
|
maxfd = tun->fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options.createif) && (options.net.s_addr)) {
|
if ((options.createif) && (options.net.len)) {
|
||||||
struct in_addr mask;
|
|
||||||
mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
|
|
||||||
/* printf("Setting up interface and routing\n"); */
|
/* 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) {
|
if (options.defaultroute) {
|
||||||
struct in_addr rm;
|
struct in_addr rm;
|
||||||
rm.s_addr = 0;
|
rm.s_addr = 0;
|
||||||
tun_addroute(tun, &rm, &options.destaddr, &rm);
|
netdev_addroute(&rm, &options.destaddr.v4, &rm);
|
||||||
}
|
}
|
||||||
if (options.ipup)
|
if (options.ipup)
|
||||||
tun_runscript(tun, options.ipup);
|
tun_runscript(tun, options.ipup);
|
||||||
@@ -1508,7 +1616,7 @@ int main(int argc, char **argv)
|
|||||||
/* Otherwise it is deallocated by gtplib */
|
/* Otherwise it is deallocated by gtplib */
|
||||||
pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
|
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 */
|
pdp->ipif = tun; /* TODO */
|
||||||
iparr[n].pdp = pdp;
|
iparr[n].pdp = pdp;
|
||||||
|
|
||||||
@@ -1572,7 +1680,10 @@ int main(int argc, char **argv)
|
|||||||
msisdn_add(&options.msisdn, &pdp->msisdn, n);
|
msisdn_add(&options.msisdn, &pdp->msisdn, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv42eua(&pdp->eua, NULL); /* Request dynamic IP address */
|
/* Request dynamic IP address */
|
||||||
|
pdp->eua.v[0] = PDP_EUA_ORG_IETF;
|
||||||
|
pdp->eua.v[1] = options.pdp_type;
|
||||||
|
pdp->eua.l = 2;
|
||||||
|
|
||||||
if (options.pco.l > sizeof(pdp->pco_req.v)) {
|
if (options.pco.l > sizeof(pdp->pco_req.v)) {
|
||||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||||
@@ -1591,6 +1702,8 @@ int main(int argc, char **argv)
|
|||||||
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
|
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
|
||||||
512 = Flat rate, 256 = Hot billing */
|
512 = Flat rate, 256 = Hot billing */
|
||||||
|
|
||||||
|
pdp->tx_gpdu_seq = options.tx_gpdu_seq;
|
||||||
|
|
||||||
/* Create context */
|
/* Create context */
|
||||||
/* We send this of once. Retransmissions are handled by gtplib */
|
/* We send this of once. Retransmissions are handled by gtplib */
|
||||||
gtp_create_context_req(gsn, pdp, &iparr[n]);
|
gtp_create_context_req(gsn, pdp, &iparr[n]);
|
||||||
|
|||||||
41
tests/Makefile.am
Normal file
41
tests/Makefile.am
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
SUBDIRS = \
|
||||||
|
lib \
|
||||||
|
gtp \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||||
|
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||||
|
:;{ \
|
||||||
|
echo '# Signature of the current package.' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_NAME],' && \
|
||||||
|
echo ' [$(PACKAGE_NAME)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
|
||||||
|
echo ' [$(PACKAGE_TARNAME)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_VERSION],' && \
|
||||||
|
echo ' [$(PACKAGE_VERSION)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_STRING],' && \
|
||||||
|
echo ' [$(PACKAGE_STRING)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
|
||||||
|
echo ' [$(PACKAGE_BUGREPORT)])'; \
|
||||||
|
echo 'm4_define([AT_PACKAGE_URL],' && \
|
||||||
|
echo ' [$(PACKAGE_URL)])'; \
|
||||||
|
} >'$(srcdir)/package.m4'
|
||||||
|
|
||||||
|
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
|
||||||
|
TESTSUITE = $(srcdir)/testsuite
|
||||||
|
DISTCLEANFILES = atconfig $(NULL)
|
||||||
|
|
||||||
|
check-local: atconfig $(TESTSUITE)
|
||||||
|
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||||
|
|
||||||
|
installcheck-local: atconfig $(TESTSUITE)
|
||||||
|
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' $(TESTSUITEFLAGS)
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean
|
||||||
|
|
||||||
|
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
|
||||||
|
AUTOTEST = $(AUTOM4TE) --language=autotest
|
||||||
|
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
|
||||||
|
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
|
||||||
|
mv $@.tmp $@
|
||||||
19
tests/gtp/Makefile.am
Normal file
19
tests/gtp/Makefile.am
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
gtpie_test.ok \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
noinst_PROGRAMS = \
|
||||||
|
gtpie_test \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
gtpie_test_SOURCES = \
|
||||||
|
gtpie_test.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
gtpie_test_LDADD = \
|
||||||
|
$(top_builddir)/lib/debug.o \
|
||||||
|
$(top_builddir)/gtp/libgtp.la \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(NULL)
|
||||||
132
tests/gtp/gtpie_test.c
Normal file
132
tests/gtp/gtpie_test.c
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/bits.h>
|
||||||
|
|
||||||
|
#include "../../lib/syserr.h"
|
||||||
|
#include "../../gtp/gtpie.h"
|
||||||
|
|
||||||
|
static const uint8_t in[] = { 1,2,3,4,5,6 };
|
||||||
|
static uint8_t buf[256];
|
||||||
|
static int rc;
|
||||||
|
|
||||||
|
static void test_gtpie_tlv()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tlv()\n");
|
||||||
|
|
||||||
|
/* normal / successful case */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tlv(buf, &len, sizeof(buf), 23, sizeof(in), in);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == sizeof(in) + 3);
|
||||||
|
OSMO_ASSERT(buf[0] == 23);
|
||||||
|
OSMO_ASSERT(osmo_load16be(&buf[1]) == sizeof(in));
|
||||||
|
OSMO_ASSERT(!memcmp(buf+3, in, sizeof(in)));
|
||||||
|
|
||||||
|
/* overflow */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tlv(buf, &len, 4, 23, sizeof(in), in);
|
||||||
|
OSMO_ASSERT(rc == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_gtpie_tv0()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tv0()\n");
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tv0(buf, &len, sizeof(buf), 42, sizeof(in), in);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == sizeof(in) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_gtpie_tv1()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tv1()\n");
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tv1(buf, &len, sizeof(buf), 42, 0xAD);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == 2);
|
||||||
|
OSMO_ASSERT(buf[0] == 42);
|
||||||
|
OSMO_ASSERT(buf[1] == 0xAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_gtpie_tv2()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tv2()\n");
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tv2(buf, &len, sizeof(buf), 42, 0xABCD);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == 3);
|
||||||
|
OSMO_ASSERT(buf[0] == 42);
|
||||||
|
OSMO_ASSERT(osmo_load16be(&buf[1]) == 0xABCD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_gtpie_tv4()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tv4()\n");
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tv4(buf, &len, sizeof(buf), 42, 0xABCD0123);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == 5);
|
||||||
|
OSMO_ASSERT(buf[0] == 42);
|
||||||
|
OSMO_ASSERT(osmo_load32be(&buf[1]) == 0xABCD0123);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_gtpie_tv8()
|
||||||
|
{
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
printf("Testing gtpie_tv8()\n");
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
rc = gtpie_tv8(buf, &len, sizeof(buf), 42, 0x0001020304050607ULL);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(len == 9);
|
||||||
|
OSMO_ASSERT(buf[0] == 42);
|
||||||
|
OSMO_ASSERT(osmo_load32be(&buf[1]) == 0x00010203);
|
||||||
|
OSMO_ASSERT(osmo_load32be(&buf[5]) == 0x04050607);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
||||||
|
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||||
|
osmo_init_logging2(tall_ctx, &log_info);
|
||||||
|
log_set_use_color(osmo_stderr_target, 0);
|
||||||
|
log_set_print_filename(osmo_stderr_target, 0);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
test_gtpie_tlv();
|
||||||
|
test_gtpie_tv0();
|
||||||
|
test_gtpie_tv1();
|
||||||
|
test_gtpie_tv2();
|
||||||
|
test_gtpie_tv4();
|
||||||
|
test_gtpie_tv8();
|
||||||
|
|
||||||
|
/* TODO: gtpie_decaps() */
|
||||||
|
/* TODO: gtpie_encaps() */
|
||||||
|
/* TODO: gtpie_encaps2() */
|
||||||
|
/* TODO: gtpie_getie(), gtpie_exist(), gtpie_get*() */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
6
tests/gtp/gtpie_test.ok
Normal file
6
tests/gtp/gtpie_test.ok
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Testing gtpie_tlv()
|
||||||
|
Testing gtpie_tv0()
|
||||||
|
Testing gtpie_tv1()
|
||||||
|
Testing gtpie_tv2()
|
||||||
|
Testing gtpie_tv4()
|
||||||
|
Testing gtpie_tv8()
|
||||||
28
tests/lib/Makefile.am
Normal file
28
tests/lib/Makefile.am
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ippool_test_SOURCES = \
|
||||||
|
ippool_test.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
ippool_test_LDADD = \
|
||||||
|
$(top_builddir)/lib/libmisc.a \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
in46a_test_SOURCES = \
|
||||||
|
in46a_test.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
in46a_test_LDADD = \
|
||||||
|
$(top_builddir)/lib/libmisc.a \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(NULL)
|
||||||
460
tests/lib/in46a_test.c
Normal file
460
tests/lib/in46a_test.c
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/bits.h>
|
||||||
|
|
||||||
|
#include "../../lib/in46_addr.h"
|
||||||
|
#include "../../lib/syserr.h"
|
||||||
|
|
||||||
|
static const struct in46_addr g_ia4 = {
|
||||||
|
.len = 4,
|
||||||
|
.v4.s_addr = 0x0d0c0b0a,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_in46a_to_af(void)
|
||||||
|
{
|
||||||
|
printf("Testing in46a_to_af() with IPv4 addresses\n");
|
||||||
|
|
||||||
|
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_to_sas(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_ntop(void)
|
||||||
|
{
|
||||||
|
struct in46_addr ia;
|
||||||
|
char buf[256];
|
||||||
|
const char *res;
|
||||||
|
|
||||||
|
printf("Testing in46a_ntop() with IPv4 addresses\n");
|
||||||
|
|
||||||
|
res = in46a_ntop(NULL, buf, sizeof(buf));
|
||||||
|
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
|
||||||
|
printf("res = %s\n", res);
|
||||||
|
|
||||||
|
ia.len = 0;
|
||||||
|
res = in46a_ntop(&ia, buf, sizeof(buf));
|
||||||
|
printf("res = %s\n", res);
|
||||||
|
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
|
||||||
|
|
||||||
|
ia.len = 4;
|
||||||
|
ia.v4.s_addr = htonl(0x01020304);
|
||||||
|
res = in46a_ntop(&ia, buf, sizeof(buf));
|
||||||
|
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
|
||||||
|
printf("res = %s\n", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46p_ntoa(void)
|
||||||
|
{
|
||||||
|
const struct in46_prefix ip46 = {
|
||||||
|
.prefixlen = 24,
|
||||||
|
.addr = {
|
||||||
|
.len = 4,
|
||||||
|
.v4.s_addr = htonl(0x10203000),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
printf("in46p_ntoa() returns %s\n", in46p_ntoa(&ip46));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_equal(void)
|
||||||
|
{
|
||||||
|
struct in46_addr b;
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
|
||||||
|
size_t prefixlen)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
printf("in46a_within_mask(%s, ", in46a_ntoa(addr));
|
||||||
|
printf("%s, %lu) = ", in46a_ntoa(net), prefixlen);
|
||||||
|
|
||||||
|
rc = in46a_within_mask(addr, net, prefixlen);
|
||||||
|
printf("%d\n", rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_within_mask(void)
|
||||||
|
{
|
||||||
|
struct in46_addr addr, mask;
|
||||||
|
|
||||||
|
printf("Testing in46a_within_mask() with IPv4 addresses\n");
|
||||||
|
|
||||||
|
addr = g_ia4;
|
||||||
|
mask = g_ia4;
|
||||||
|
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 32));
|
||||||
|
|
||||||
|
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfffffffC );
|
||||||
|
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
|
||||||
|
|
||||||
|
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfff80000 );
|
||||||
|
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 13));
|
||||||
|
|
||||||
|
addr.v4.s_addr = htonl(ntohl(addr.v4.s_addr) + 1);
|
||||||
|
mask = g_ia4;
|
||||||
|
OSMO_ASSERT(!log_in46a_within_mask(&addr, &mask, 32));
|
||||||
|
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfffffffC );
|
||||||
|
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_to_eua(void)
|
||||||
|
{
|
||||||
|
struct ul66_t eua;
|
||||||
|
|
||||||
|
printf("testing in46a_to_eua() with IPv4 addresses\n");
|
||||||
|
|
||||||
|
#if 0 /* triggers assert in current implementation */
|
||||||
|
const struct in46_addr ia_invalid = { .len = 3, };
|
||||||
|
OSMO_ASSERT(in46a_to_eua(&ia_invalid, &eua) < 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* IPv4 address */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_from_eua(void)
|
||||||
|
{
|
||||||
|
struct in46_addr ia;
|
||||||
|
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 };
|
||||||
|
memset(&eua, 0, sizeof(eua));
|
||||||
|
|
||||||
|
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
||||||
|
|
||||||
|
/* default: v4 unspec */
|
||||||
|
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||||
|
OSMO_ASSERT(ia.len == 4);
|
||||||
|
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||||
|
|
||||||
|
/* invalid */
|
||||||
|
eua.v[0] = 0x23;
|
||||||
|
eua.v[1] = PDP_EUA_TYPE_v4;
|
||||||
|
eua.l = 6;
|
||||||
|
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||||
|
|
||||||
|
/* invalid */
|
||||||
|
eua.v[0] = PDP_EUA_ORG_IETF;
|
||||||
|
eua.v[1] = 0x23;
|
||||||
|
eua.l = 6;
|
||||||
|
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||||
|
|
||||||
|
/* unspecified V4 */
|
||||||
|
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
||||||
|
eua.l = sizeof(v4_unspec);
|
||||||
|
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||||
|
OSMO_ASSERT(ia.len == 4);
|
||||||
|
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||||
|
|
||||||
|
/* specified V4 */
|
||||||
|
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
||||||
|
eua.l = sizeof(v4_spec);
|
||||||
|
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||||
|
OSMO_ASSERT(ia.len == 4);
|
||||||
|
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_in46a_netmasklen(void)
|
||||||
|
{
|
||||||
|
struct in46_addr netmask;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
printf("Testing in46a_netmasklen() with IPv4 addresses\n");
|
||||||
|
netmask.len = 4;
|
||||||
|
|
||||||
|
netmask.v4.s_addr = 0xffffffff;
|
||||||
|
len = in46a_netmasklen(&netmask);
|
||||||
|
OSMO_ASSERT(len == 32);
|
||||||
|
|
||||||
|
netmask.v4.s_addr = 0x00ffffff;
|
||||||
|
len = in46a_netmasklen(&netmask);
|
||||||
|
OSMO_ASSERT(len == 24);
|
||||||
|
|
||||||
|
netmask.v4.s_addr = 0x00f0ffff;
|
||||||
|
len = in46a_netmasklen(&netmask);
|
||||||
|
OSMO_ASSERT(len == 20);
|
||||||
|
|
||||||
|
netmask.v4.s_addr = 0x000000fe;
|
||||||
|
len = in46a_netmasklen(&netmask);
|
||||||
|
OSMO_ASSERT(len == 7);
|
||||||
|
|
||||||
|
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,
|
||||||
|
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
|
||||||
|
};
|
||||||
|
len = in46a_netmasklen(&netmaskA);
|
||||||
|
OSMO_ASSERT(len == 128);
|
||||||
|
|
||||||
|
const struct in46_addr netmaskB = {
|
||||||
|
.len = 16,
|
||||||
|
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00},
|
||||||
|
};
|
||||||
|
len = in46a_netmasklen(&netmaskB);
|
||||||
|
OSMO_ASSERT(len == 104);
|
||||||
|
|
||||||
|
const struct in46_addr netmaskC = {
|
||||||
|
.len = 16,
|
||||||
|
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00},
|
||||||
|
};
|
||||||
|
len = in46a_netmasklen(&netmaskC);
|
||||||
|
OSMO_ASSERT(len == 103);
|
||||||
|
|
||||||
|
const struct in46_addr netmaskD = {
|
||||||
|
.len = 16,
|
||||||
|
.v6.s6_addr = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||||
|
};
|
||||||
|
len = in46a_netmasklen(&netmaskD);
|
||||||
|
OSMO_ASSERT(len == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
||||||
|
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||||
|
osmo_init_logging2(tall_ctx, &log_info);
|
||||||
|
log_set_use_color(osmo_stderr_target, 0);
|
||||||
|
log_set_print_filename(osmo_stderr_target, 0);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
17
tests/lib/in46a_test.ok
Normal file
17
tests/lib/in46a_test.ok
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
|
in46p_ntoa() returns 16.32.48.0/24
|
||||||
|
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() with IPv4 addresses
|
||||||
|
Testing in46a_from_eua() with IPv4 addresses
|
||||||
|
Testing in46a_netmasklen() with IPv4 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
|
||||||
144
tests/lib/ippool_test.c
Normal file
144
tests/lib/ippool_test.c
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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"
|
||||||
|
#include "../../lib/syserr.h"
|
||||||
|
|
||||||
|
|
||||||
|
static struct ippool_t *create_pool(const char *prefix_str, unsigned int flags, char **blacklist, size_t blacklist_size)
|
||||||
|
{
|
||||||
|
struct in46_prefix *blacklist_pfx;
|
||||||
|
struct ippool_t *pool;
|
||||||
|
struct in46_prefix pfx;
|
||||||
|
size_t t;
|
||||||
|
int rc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
blacklist_pfx = calloc(blacklist_size, sizeof(struct in46_prefix));
|
||||||
|
for (i = 0; i < blacklist_size; i++) {
|
||||||
|
rc = ippool_aton(&blacklist_pfx[i].addr, &t, blacklist[i], 0);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
pfx.prefixlen = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dynamic-only v4 */
|
||||||
|
|
||||||
|
rc = ippool_aton(&pfx.addr, &t, prefix_str, 0);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
pfx.prefixlen = t;
|
||||||
|
|
||||||
|
rc = ippool_new(&pool, &pfx, NULL, flags, blacklist_pfx, blacklist_size);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
|
||||||
|
//ippool_printaddr(pool);
|
||||||
|
|
||||||
|
free(blacklist_pfx);
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pool_size(const char *pfx, unsigned int flags, char **blacklist, size_t blacklist_size, unsigned int expected_size)
|
||||||
|
{
|
||||||
|
struct ippool_t *pool;
|
||||||
|
struct ippoolm_t *member;
|
||||||
|
struct in46_addr addr;
|
||||||
|
int i, rc, n;
|
||||||
|
|
||||||
|
printf("testing pool for prefix %s, flags=0x%x, blacklist_size=%lu, expected_size=%u\n", pfx, flags, blacklist_size, expected_size);
|
||||||
|
pool = create_pool(pfx, flags, blacklist, blacklist_size);
|
||||||
|
OSMO_ASSERT(pool->listsize == expected_size);
|
||||||
|
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.len = pool->member[0].addr.len;
|
||||||
|
|
||||||
|
/* allocate all addresses */
|
||||||
|
for (i = 0; i < expected_size; i++) {
|
||||||
|
member = NULL;
|
||||||
|
rc = ippool_newip(pool, &member, &addr, 0);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(member);
|
||||||
|
printf("allocated address %s\n", in46a_ntoa(&member->addr));
|
||||||
|
}
|
||||||
|
/* allocate one more, expect that to fail */
|
||||||
|
rc = ippool_newip(pool, &member, &addr, 0);
|
||||||
|
OSMO_ASSERT(rc < 0);
|
||||||
|
|
||||||
|
/* release a (random) number N of (random) member address */
|
||||||
|
n = rand() % pool->listsize;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
int r;
|
||||||
|
/* chose a random index that is in use */
|
||||||
|
do {
|
||||||
|
r = rand() % pool->listsize;
|
||||||
|
} while (!pool->member[r].inuse);
|
||||||
|
/* and free it... */
|
||||||
|
rc = ippool_freeip(pool, &pool->member[r]);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate all N previously released addresses */
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
member = NULL;
|
||||||
|
rc = ippool_newip(pool, &member, &addr, 0);
|
||||||
|
OSMO_ASSERT(rc == 0);
|
||||||
|
OSMO_ASSERT(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate one more, expect that to fail */
|
||||||
|
rc = ippool_newip(pool, &member, &addr, 0);
|
||||||
|
OSMO_ASSERT(rc < 0);
|
||||||
|
|
||||||
|
ippool_free(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pool_sizes(void)
|
||||||
|
{
|
||||||
|
/* 256 addresses [0..255] */
|
||||||
|
test_pool_size("192.168.23.0/24", 0, NULL, 0, 256);
|
||||||
|
|
||||||
|
/* 255 addresses [1..255] */
|
||||||
|
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK, NULL, 0, 255);
|
||||||
|
|
||||||
|
/* 254 addresses [1..254] */
|
||||||
|
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 254);
|
||||||
|
|
||||||
|
/* 65534 addresses [0.1..255.254] */
|
||||||
|
test_pool_size("192.168.0.0/16", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 65534);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (argc < 2 || strcmp(argv[1], "-v6")) {
|
||||||
|
test_pool_sizes();
|
||||||
|
} else {
|
||||||
|
test_pool_sizes_v6();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
10
tests/lib/ippool_test.err
Normal file
10
tests/lib/ippool_test.err
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
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
|
||||||
|
No more IP addresses available
|
||||||
|
No more IP addresses available
|
||||||
|
No more IP addresses available
|
||||||
|
No more IP addresses available
|
||||||
66557
tests/lib/ippool_test.ok
Normal file
66557
tests/lib/ippool_test.ok
Normal file
File diff suppressed because it is too large
Load Diff
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::
|
||||||
34
tests/testsuite.at
Normal file
34
tests/testsuite.at
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
AT_INIT
|
||||||
|
AT_BANNER([Regression tests.])
|
||||||
|
|
||||||
|
AT_SETUP([ippool])
|
||||||
|
AT_KEYWORDS([ippool])
|
||||||
|
cat $abs_srcdir/lib/ippool_test.ok > expout
|
||||||
|
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
|
||||||
|
AT_CHECK([$abs_top_builddir/tests/gtp/gtpie_test], [], [expout], [])
|
||||||
|
AT_CLEANUP
|
||||||
Reference in New Issue
Block a user