summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--configure.ac42
-rwxr-xr-xcontrib/jenkins.sh5
-rw-r--r--contrib/osmo-ggsn.service2
-rw-r--r--debian/changelog39
-rw-r--r--debian/control10
-rw-r--r--debian/gbp.conf2
-rw-r--r--debian/libgtp3.install (renamed from debian/libgtp2.install)0
-rw-r--r--debian/libgtp3.symbols (renamed from debian/libgtp2.symbols)19
-rw-r--r--debian/osmo-ggsn.cfg20
-rw-r--r--debian/osmo-ggsn.install1
-rw-r--r--debian/osmo-ggsn.manpages2
-rw-r--r--debian/patches/spelling.patch61
-rwxr-xr-xdebian/rules16
-rwxr-xr-xdebian/tests/can-show-help6
-rw-r--r--debian/tests/control4
-rw-r--r--doc/examples/osmo-ggsn.cfg2
-rw-r--r--ggsn/Makefile.am10
-rw-r--r--ggsn/ggsn.c452
-rw-r--r--ggsn/ggsn.h1
-rw-r--r--ggsn/ggsn_vty.c29
-rw-r--r--ggsn/gtp-kernel.c223
-rw-r--r--ggsn/gtp-kernel.h45
-rw-r--r--ggsn/icmpv6.c10
-rw-r--r--ggsn/icmpv6.h4
-rw-r--r--gtp/Makefile.am8
-rw-r--r--gtp/gtp.c207
-rw-r--r--gtp/gtp.h6
-rw-r--r--gtp/pdp.c62
-rw-r--r--gtp/pdp.h21
-rw-r--r--lib/Makefile.am9
-rw-r--r--lib/gtp-kernel.c160
-rw-r--r--lib/gtp-kernel.h38
-rw-r--r--lib/in46_addr.c109
-rw-r--r--lib/in46_addr.h2
-rw-r--r--lib/ippool.c2
-rw-r--r--lib/netdev.c727
-rw-r--r--lib/netdev.h72
-rw-r--r--lib/tun.c660
-rw-r--r--lib/tun.h51
-rw-r--r--sgsnemu/Makefile.am6
-rw-r--r--sgsnemu/cmdline.c97
-rw-r--r--sgsnemu/cmdline.ggo28
-rw-r--r--sgsnemu/cmdline.h10
-rw-r--r--sgsnemu/sgsnemu.c70
-rw-r--r--src/Makefile.in0
-rw-r--r--tests/gtp/gtpie_test.c17
-rw-r--r--tests/lib/Makefile.am8
-rw-r--r--tests/lib/in46a_test.c304
-rw-r--r--tests/lib/in46a_test.ok16
-rw-r--r--tests/lib/in46a_v6_test.ok10
-rw-r--r--tests/lib/ippool_test.c21
-rw-r--r--tests/lib/ippool_test.err2
-rw-r--r--tests/lib/ippool_test.ok257
-rw-r--r--tests/lib/ippool_v6_test.err2
-rw-r--r--tests/lib/ippool_v6_test.ok257
-rw-r--r--tests/testsuite.at13
57 files changed, 2554 insertions, 1706 deletions
diff --git a/README.md b/README.md
index 80126e0..583ad3a 100644
--- a/README.md
+++ b/README.md
@@ -263,8 +263,7 @@ following:
1. Install sgsnemu on a Linux Box. See under installation above.
2. Connect your Linux box with sgsnemu installed to the GPRS core
-network. Use the same LAN switch as the one your SGSN is connected
-to. You also need a free IP address that can be used by sgsnemu.
+network. You also need a free IP address that can be used by sgsnemu.
3. You need to configure networking in terms of interface address,
subnet mask and default route. See the Linux Networking HOWTO for
details.
diff --git a/configure.ac b/configure.ac
index ca455ce..62812ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,7 +18,6 @@ AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_AWK
AC_PROG_CPP
-AC_PROG_CXX
LT_INIT
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
@@ -66,7 +65,7 @@ AC_ARG_ENABLE([gtp-linux],
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
AS_IF([test "x$enable_gtp_linux" = "xyes"], [
- PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0])
+ PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.2.0])
])
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
@@ -136,9 +135,42 @@ adl_FUNC_GETOPT_LONG
AM_INIT_AUTOMAKE([foreign])
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
-PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
+
+AC_ARG_ENABLE(sanitize,
+ [AS_HELP_STRING(
+ [--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
doc/Makefile
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index b2f8452..19df974 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -18,6 +18,9 @@ osmo-clean-workspace.sh
mkdir "$deps" || true
+if [ "x$GTP" == "x--enable-gtp-linux" ]; then
+ osmo-build-dep.sh libgtpnl
+fi
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -35,7 +38,7 @@ set -x
cd "$base"
autoreconf --install --force
-./configure
+./configure --enable-sanitize --enable-werror $GTP
$MAKE $PARALLEL_MAKE
$MAKE distcheck
diff --git a/contrib/osmo-ggsn.service b/contrib/osmo-ggsn.service
index e6798b2..97eb658 100644
--- a/contrib/osmo-ggsn.service
+++ b/contrib/osmo-ggsn.service
@@ -6,7 +6,7 @@ Documentation=https://projects.osmocom.org/projects/openggsn
[Service]
Type=simple
Restart=always
-ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg -f
+ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
RestartSec=2
RestartPreventExitStatus=1
diff --git a/debian/changelog b/debian/changelog
index c8f5ad4..8f5eec4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,42 @@
+osmo-ggsn (1.2.2-2) unstable; urgency=low
+
+ * Team upload
+ * Upload to unstable
+ * debian/osmo-ggsn.manpages:
+ - Install upstream manpages
+ * debian/osmo-ggsn.cfg:
+ - Improve default configuration file
+
+ -- Ruben Undheim <ruben.undheim@gmail.com> Mon, 05 Nov 2018 22:25:53 +0100
+
+osmo-ggsn (1.2.2-2~exp1) experimental; urgency=medium
+
+ * Team upload
+ * Install default configuration file
+ * debian/control:
+ - New standards version 4.2.1 - no changes
+ * debian/gbp.conf: to enforce pristine-tar
+ * debian/rules:
+ - Create .tarball-version such that git-version-gen works
+ - Clean up some unneeded comments
+ - Do not start systemd service by default
+ * debian/tests:
+ - Added basic autopkgtest to check that 'osmo-ggsn -h' works after install
+
+ -- Ruben Undheim <ruben.undheim@gmail.com> Sun, 23 Sep 2018 17:19:54 +0200
+
+osmo-ggsn (1.2.2-1) experimental; urgency=medium
+
+ * New upstream release
+
+ -- Thorsten Alteholz <debian@alteholz.de> Thu, 21 Jun 2018 19:46:22 +0200
+
+osmo-ggsn (1.2.1-1) experimental; urgency=medium
+
+ * New upstream release
+
+ -- Thorsten Alteholz <debian@alteholz.de> Wed, 16 May 2018 22:57:24 +0200
+
osmo-ggsn (1.1.0-2) unstable; urgency=medium
* move to unstable
diff --git a/debian/control b/debian/control
index c5c957c..86aa051 100644
--- a/debian/control
+++ b/debian/control
@@ -5,9 +5,9 @@ Section: net
Priority: optional
Build-Depends: debhelper (>= 11),
pkg-config,
- libdpkg-perl, git,
- libosmocore-dev (>= 0.10.2)
-Standards-Version: 4.1.4
+ libdpkg-perl,
+ libosmocore-dev (>= 0.11.0)
+Standards-Version: 4.2.1
Vcs-Browser: https://salsa.debian.org/debian-mobcom-team/osmo-ggsn
Vcs-Git: https://salsa.debian.org/debian-mobcom-team/osmo-ggsn.git
Homepage: https://projects.osmocom.org/projects/openggsn
@@ -24,7 +24,7 @@ Description: Osmocom Gateway GPRS Support Node (GGSN)
operators as the interface between the Internet and the rest of the
mobile network infrastructure.
-Package: libgtp2
+Package: libgtp3
Architecture: any
Multi-Arch: same
Section: libs
@@ -43,7 +43,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
- libgtp2 (= ${binary:Version})
+ libgtp3 (= ${binary:Version})
Description: Development files for libgtp
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
operators as the interface between the Internet and the rest of the
diff --git a/debian/gbp.conf b/debian/gbp.conf
new file mode 100644
index 0000000..cec628c
--- /dev/null
+++ b/debian/gbp.conf
@@ -0,0 +1,2 @@
+[DEFAULT]
+pristine-tar = True
diff --git a/debian/libgtp2.install b/debian/libgtp3.install
index 3ddde58..3ddde58 100644
--- a/debian/libgtp2.install
+++ b/debian/libgtp3.install
diff --git a/debian/libgtp2.symbols b/debian/libgtp3.symbols
index 4593319..acb16fd 100644
--- a/debian/libgtp2.symbols
+++ b/debian/libgtp3.symbols
@@ -1,10 +1,8 @@
# SymbolsHelper-Confirmed: 1.1.0 amd64
-libgtp.so.2 libgtp2 #MINVER#
- char2ul_t@Base 1.1.0
+libgtp.so.3 libgtp3 #MINVER#
checksum@Base 1.1.0
eua2ipv4@Base 1.1.0
gsna2in_addr@Base 1.1.0
- gtp_conf@Base 1.1.0
gtp_create_context_req@Base 1.1.0
gtp_create_context_resp@Base 1.1.0
gtp_create_pdp_conf@Base 1.1.0
@@ -18,24 +16,15 @@ libgtp.so.2 libgtp2 #MINVER#
gtp_delete_pdp_conf@Base 1.1.0
gtp_delete_pdp_ind@Base 1.1.0
gtp_delete_pdp_resp@Base 1.1.0
- gtp_dublicate@Base 1.1.0
gtp_echo_conf@Base 1.1.0
gtp_echo_ind@Base 1.1.0
gtp_echo_req@Base 1.1.0
gtp_echo_resp@Base 1.1.0
- gtp_error_ind_conf@Base 1.1.0
- gtp_error_ind_resp@Base 1.1.0
- gtp_extheader_ind@Base 1.1.0
- gtp_extheader_req@Base 1.1.0
gtp_fd@Base 1.1.0
gtp_free@Base 1.1.0
gtp_freepdp@Base 1.1.0
- gtp_gpdu_ind@Base 1.1.0
gtp_new@Base 1.1.0
gtp_newpdp@Base 1.1.0
- gtp_notification@Base 1.1.0
- gtp_req@Base 1.1.0
- gtp_resp@Base 1.1.0
gtp_retrans@Base 1.1.0
gtp_retranstimeout@Base 1.1.0
gtp_set_cb_conf@Base 1.1.0
@@ -45,12 +34,10 @@ libgtp.so.2 libgtp2 #MINVER#
gtp_set_cb_extheader_ind@Base 1.1.0
gtp_set_cb_recovery@Base 1.1.0
gtp_set_cb_unsup_ind@Base 1.1.0
+ gtp_type_names@Base 1.2.1
gtp_unsup_ind@Base 1.1.0
gtp_unsup_req@Base 1.1.0
gtp_update_context@Base 1.1.0
- gtp_update_pdp_conf@Base 1.1.0
- gtp_update_pdp_ind@Base 1.1.0
- gtp_update_pdp_resp@Base 1.1.0
gtp_version@Base 1.1.0
gtpie_decaps@Base 1.1.0
gtpie_encaps2@Base 1.1.0
@@ -73,7 +60,6 @@ libgtp.so.2 libgtp2 #MINVER#
in_addr2gsna@Base 1.1.0
ipv42eua@Base 1.1.0
lookup@Base 1.1.0
- pdp_euaton@Base 1.1.0
pdp_freepdp@Base 1.1.0
pdp_getgtp0@Base 1.1.0
pdp_getgtp1@Base 1.1.0
@@ -83,7 +69,6 @@ libgtp.so.2 libgtp2 #MINVER#
pdp_gettid@Base 1.1.0
pdp_init@Base 1.1.0
pdp_newpdp@Base 1.1.0
- pdp_ntoeua@Base 1.1.0
pdp_set_imsi_nsapi@Base 1.1.0
pdp_tiddel@Base 1.1.0
pdp_tidget@Base 1.1.0
diff --git a/debian/osmo-ggsn.cfg b/debian/osmo-ggsn.cfg
new file mode 100644
index 0000000..f2a8e6f
--- /dev/null
+++ b/debian/osmo-ggsn.cfg
@@ -0,0 +1,20 @@
+# this configuration requires the apn0 tun device to be configured and up, as
+# well as IP-forwarding and masquerading to be enabled
+
+ggsn ggsn0
+ # osmo-ggsn will bind to this address for connections from within osmocom:
+ # (note that osmo-sgsn must bind to another address since the same ports are
+ # used)
+ gtp bind-ip 127.0.0.1
+ apn internet
+ tun-device apn0
+ type-support v4
+ # The DNS servers to be provided by the network using IPCP/PCO:
+ ip dns 0 192.168.0.1
+ ip dns 1 8.8.8.8
+ # The IP address pool to be provided to the network (PDP contexts):
+ ip prefix dynamic 192.168.42.0/24
+
+ no shutdown
+ default-apn internet
+ no shutdown ggsn
diff --git a/debian/osmo-ggsn.install b/debian/osmo-ggsn.install
index 522a3d1..dd47653 100644
--- a/debian/osmo-ggsn.install
+++ b/debian/osmo-ggsn.install
@@ -1,3 +1,4 @@
/usr/bin/osmo-ggsn
/usr/bin/sgsnemu
/usr/share/man/man8/*
+debian/osmo-ggsn.cfg etc/osmocom/
diff --git a/debian/osmo-ggsn.manpages b/debian/osmo-ggsn.manpages
new file mode 100644
index 0000000..71deed0
--- /dev/null
+++ b/debian/osmo-ggsn.manpages
@@ -0,0 +1,2 @@
+doc/sgsnemu.8
+doc/osmo-ggsn.8
diff --git a/debian/patches/spelling.patch b/debian/patches/spelling.patch
index dd435b1..b41bbd4 100644
--- a/debian/patches/spelling.patch
+++ b/debian/patches/spelling.patch
@@ -1,9 +1,9 @@
Description: fix spelling detected by lintian
Author: Thorsten Alteholz <debian@alteholz.de>
-Index: osmo-ggsn-1.1.0/doc/sgsnemu.8
+Index: osmo-ggsn-1.2.1/doc/sgsnemu.8
===================================================================
---- osmo-ggsn-1.1.0.orig/doc/sgsnemu.8 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/doc/sgsnemu.8 2017-12-17 13:44:33.358899530 +0100
+--- osmo-ggsn-1.2.1.orig/doc/sgsnemu.8 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/doc/sgsnemu.8 2018-05-16 22:58:05.288293751 +0200
@@ -238,7 +238,7 @@
.I msisdn
to use when connecting to the GGSN (default = 46702123456). MSISDN is
@@ -13,11 +13,11 @@ Index: osmo-ggsn-1.1.0/doc/sgsnemu.8
leading 00 or 011. See the
.I contexts
option for the the use of the
-Index: osmo-ggsn-1.1.0/gtp/gtp.c
+Index: osmo-ggsn-1.2.1/gtp/gtp.c
===================================================================
---- osmo-ggsn-1.1.0.orig/gtp/gtp.c 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/gtp/gtp.c 2017-12-17 13:45:22.273638034 +0100
-@@ -317,7 +317,7 @@
+--- osmo-ggsn-1.2.1.orig/gtp/gtp.c 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/gtp/gtp.c 2018-05-16 22:58:05.292293751 +0200
+@@ -362,7 +362,7 @@
*
* Echo: Automatically dublicates the original response
* Create pdp context: The SGSN may send create context request even if
@@ -26,16 +26,7 @@ Index: osmo-ggsn-1.1.0/gtp/gtp.c
automatically dublicate the original response. It might however have
* side effects in the application which is asked twice to validate
* the login.
-@@ -327,7 +327,7 @@
- *
- * The correct solution will be to make a queue containing response messages.
- * This queue should be checked whenever a request is received. If the
-- * response is allready in the queue that response should be transmitted.
-+ * response is already in the queue that response should be transmitted.
- * It should be possible to find messages in this queue on the basis of
- * the sequence number and peer GSN IP address (The sequense number is unique
- * within each path). This need to be implemented by a hash table. Furthermore
-@@ -1524,7 +1524,7 @@
+@@ -1569,7 +1569,7 @@
if (!pdp_getimsi(&pdp_old, pdp->imsi, pdp->nsapi)) {
/* Found old pdp with same tid. Now the voodoo begins! */
/* 09.60 / 29.060 allows create on existing context to "steal" */
@@ -44,7 +35,7 @@ Index: osmo-ggsn-1.1.0/gtp/gtp.c
/* We check that the APN, selection mode and MSISDN is the same */
DEBUGP(DLGTP, "gtp_create_pdp_ind: Old context found\n");
if ((pdp->apn_req.l == pdp_old->apn_req.l)
-@@ -1970,7 +1970,7 @@
+@@ -2015,7 +2015,7 @@
/* Is this a dublicate ? */
if (!gtp_dublicate(gsn, version, peer, seq)) {
@@ -53,7 +44,7 @@ Index: osmo-ggsn-1.1.0/gtp/gtp.c
}
/* Decode information elements */
-@@ -2100,7 +2100,7 @@
+@@ -2145,7 +2145,7 @@
}
/* TEIC (conditional) */
@@ -62,7 +53,7 @@ Index: osmo-ggsn-1.1.0/gtp/gtp.c
/* TODO: From 29.060 it is not clear if TEI_C MUST be included for */
/* all updated contexts, or only for one of the linked contexts */
gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn);
-@@ -2446,7 +2446,7 @@
+@@ -2491,7 +2491,7 @@
/* Is this a dublicate ? */
if (!gtp_dublicate(gsn, version, peer, seq)) {
@@ -71,11 +62,11 @@ Index: osmo-ggsn-1.1.0/gtp/gtp.c
}
/* Find the linked context in question */
-Index: osmo-ggsn-1.1.0/gtp/pdp.h
+Index: osmo-ggsn-1.2.1/gtp/pdp.h
===================================================================
---- osmo-ggsn-1.1.0.orig/gtp/pdp.h 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/gtp/pdp.h 2017-12-17 13:45:27.497930517 +0100
-@@ -96,7 +96,7 @@
+--- osmo-ggsn-1.2.1.orig/gtp/pdp.h 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/gtp/pdp.h 2018-05-16 22:58:05.292293751 +0200
+@@ -97,7 +97,7 @@
* With GTP version 1 it is possible to establish multiple PDP
* contexts with the same IP address. With this scheme the first
* context is established as normal. Following contexts are
@@ -84,10 +75,10 @@ Index: osmo-ggsn-1.1.0/gtp/pdp.h
* context is uniquely identified by IMSI and NSAPI. Each context is
* allocated an tei_di, but they all share the same tei_c.
*
-Index: osmo-ggsn-1.1.0/gtp/queue.h
+Index: osmo-ggsn-1.2.1/gtp/queue.h
===================================================================
---- osmo-ggsn-1.1.0.orig/gtp/queue.h 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/gtp/queue.h 2017-12-17 13:45:10.929002902 +0100
+--- osmo-ggsn-1.2.1.orig/gtp/queue.h 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/gtp/queue.h 2018-05-16 22:58:05.292293751 +0200
@@ -51,7 +51,7 @@
int queue_new(struct queue_t **queue);
/* Deallocates queue structure */
@@ -97,10 +88,10 @@ Index: osmo-ggsn-1.1.0/gtp/queue.h
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
struct sockaddr_in *peer, uint16_t seq);
/* Remove an element from the queue. */
-Index: osmo-ggsn-1.1.0/lib/ippool.c
+Index: osmo-ggsn-1.2.1/lib/ippool.c
===================================================================
---- osmo-ggsn-1.1.0.orig/lib/ippool.c 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/lib/ippool.c 2017-12-17 13:45:31.558157831 +0100
+--- osmo-ggsn-1.2.1.orig/lib/ippool.c 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/lib/ippool.c 2018-05-16 22:58:05.292293751 +0200
@@ -457,7 +457,7 @@
if (p2) { /* Was allocated from dynamic address pool */
if (p2->inuse) {
@@ -110,11 +101,11 @@ Index: osmo-ggsn-1.1.0/lib/ippool.c
return -GTPCAUSE_SYS_FAIL; /* Allready in use / Should not happen */
}
-Index: osmo-ggsn-1.1.0/sgsnemu/sgsnemu.c
+Index: osmo-ggsn-1.2.1/sgsnemu/sgsnemu.c
===================================================================
---- osmo-ggsn-1.1.0.orig/sgsnemu/sgsnemu.c 2017-10-28 19:01:30.000000000 +0200
-+++ osmo-ggsn-1.1.0/sgsnemu/sgsnemu.c 2017-12-17 13:46:49.526522917 +0100
-@@ -1457,7 +1457,7 @@
+--- osmo-ggsn-1.2.1.orig/sgsnemu/sgsnemu.c 2018-05-16 22:58:05.304293751 +0200
++++ osmo-ggsn-1.2.1/sgsnemu/sgsnemu.c 2018-05-16 22:58:05.292293751 +0200
+@@ -1455,7 +1455,7 @@
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 "
@@ -122,4 +113,4 @@ Index: osmo-ggsn-1.1.0/sgsnemu/sgsnemu.c
+ "router advertisements; SLAAC will not succeed, please "
"fix your setup!\n");
}
- free(accept_ra);
+ }
diff --git a/debian/rules b/debian/rules
index 94a86d3..3e6e4f2 100755
--- a/debian/rules
+++ b/debian/rules
@@ -3,9 +3,7 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
-#DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
-#DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
-#VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
+include /usr/share/dpkg/pkg-info.mk
# This has to be exported to make some magic below work.
#export DH_OPTIONS
@@ -15,9 +13,13 @@ arch = $(shell dpkg-architecture -qDEB_BUILD_ARCH)
%:
dh $@ --with autoreconf
-#override_dh_autoreconf:
-# echo $(VERSION) > .tarball-version
-# dh_autoreconf
+override_dh_clean:
+ dh_clean
+ $(RM) .tarball-version
+
+override_dh_autoreconf:
+ echo $(DEB_VERSION_UPSTREAM) > .tarball-version
+ dh_autoreconf
# Print test results in case of a failure
override_dh_auto_test:
@@ -34,3 +36,5 @@ override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) \
fi
+override_dh_installsystemd:
+ dh_installsystemd --no-enable --no-start
diff --git a/debian/tests/can-show-help b/debian/tests/can-show-help
new file mode 100755
index 0000000..6449bcc
--- /dev/null
+++ b/debian/tests/can-show-help
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+osmo-ggsn -h
+echo "run: OK"
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..8c7ae45
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,4 @@
+Tests: can-show-help
+Depends: osmo-ggsn
+
+
diff --git a/doc/examples/osmo-ggsn.cfg b/doc/examples/osmo-ggsn.cfg
index 763e561..8317684 100644
--- a/doc/examples/osmo-ggsn.cfg
+++ b/doc/examples/osmo-ggsn.cfg
@@ -53,6 +53,7 @@ ggsn ggsn0
type-support v6
ipv6 prefix dynamic 2001:780:44:2000:0:0:0:0/56
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
no shutdown
apn inet46
@@ -65,6 +66,7 @@ ggsn ggsn0
ip ifconfig 176.16.46.0/24
ipv6 prefix dynamic 2001:780:44:2100:0:0:0:0/56
ipv6 dns 0 2001:4860:4860::8888
+ ipv6 dns 1 2001:4860:4860::8844
ipv6 ifconfig 2001:780:44:2100:0:0:0:0/56
no shutdown
default-apn internet
diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index 8a468a9..eca385f 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -7,13 +7,9 @@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb
osmo_ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
if ENABLE_GTP_KERNEL
-AM_CFLAGS += -DGTP_KERNEL
-osmo_ggsn_LDADD += -lgtpnl
+AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
+osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
endif
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
-osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
-
-if ENABLE_GTP_KERNEL
-osmo_ggsn_SOURCES += gtp-kernel.c
-endif
+osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 8b576ab..14bf04f 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -56,15 +56,16 @@
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
#include <osmocom/gsm/apn.h>
#include "../lib/tun.h"
#include "../lib/ippool.h"
#include "../lib/syserr.h"
#include "../lib/in46_addr.h"
+#include "../lib/gtp-kernel.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
-#include "gtp-kernel.h"
#include "icmpv6.h"
#include "ggsn.h"
@@ -124,9 +125,11 @@ int apn_stop(struct apn_ctx *apn, bool force)
LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
}
- /* release tun device */
- LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
- osmo_fd_unregister(&apn->tun.fd);
+ if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
+ /* release tun device */
+ LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
+ osmo_fd_unregister(&apn->tun.fd);
+ }
tun_free(apn->tun.tun);
apn->tun.tun = NULL;
}
@@ -147,31 +150,35 @@ int apn_stop(struct apn_ctx *apn, bool force)
}
-static int alloc_ippool_blacklist(struct apn_ctx *apn, const struct tun_t *tun, struct in46_prefix **blacklist, bool ipv6)
+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 = tun_ip_local_get(apn->tun.tun, NULL, 0, flags);
+ 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 = tun_ip_local_get(apn->tun.tun, *blacklist, len, flags);
+ 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 */
+ if (len2 > len) { /* iface was added between 2 calls, repeat operation */
talloc_free(*blacklist);
- else
+ *blacklist = NULL;
+ } else
break;
}
@@ -189,6 +196,8 @@ int apn_start(struct apn_ctx *apn)
struct in46_prefix ipv6_tun_linklocal_ip;
struct in46_prefix *blacklist;
int blacklist_size;
+ struct gsn_t *gsn = apn->ggsn->gsn;
+ int rc;
if (apn->started)
return 0;
@@ -197,7 +206,7 @@ int apn_start(struct apn_ctx *apn)
switch (apn->cfg.gtpu_mode) {
case APN_GTPU_MODE_TUN:
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
- if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name)) {
+ if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
return -1;
}
@@ -209,69 +218,100 @@ int apn_start(struct apn_ctx *apn)
/* Set TUN library callback */
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
-
- if (apn->v4.cfg.ifconfig_prefix.addr.len) {
- LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
- in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
- if (tun_setaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
- apn->v4.cfg.ifconfig_prefix.prefixlen)) {
- LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
- in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
- apn_stop(apn, false);
- return -1;
- }
+ 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 */
- if (apn->v6.cfg.ifconfig_prefix.addr.len) {
- LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
- in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
- if (tun_setaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
- apn->v6.cfg.ifconfig_prefix.prefixlen)) {
- LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
- "Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
- in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
- apn_stop(apn, false);
- return -1;
- }
+ /* set back-pointer from TUN device to APN */
+ apn->tun.tun->priv = apn;
+
+ if (apn->v4.cfg.ifconfig_prefix.addr.len) {
+ LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
+ in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
+ if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
+ apn->v4.cfg.ifconfig_prefix.prefixlen)) {
+ LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
+ in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
+ apn_stop(apn, false);
+ return -1;
}
+ }
- if (apn->tun.cfg.ipup_script) {
- LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
- apn->tun.cfg.ipup_script);
- tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
+ if (apn->v6.cfg.ifconfig_prefix.addr.len) {
+ LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
+ in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
+ if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
+ apn->v6.cfg.ifconfig_prefix.prefixlen)) {
+ LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
+ "Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
+ in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
+ apn_stop(apn, false);
+ return -1;
}
+ }
- if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
- if (tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK) < 1) {
- LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of "
- "interface: %s\n", strerror(errno));
- apn_stop(apn, false);
- return -1;
- }
- apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
+ 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;
+ }
- /* set back-pointer from TUN device to APN */
- apn->tun.tun->priv = apn;
- break;
- case APN_GTPU_MODE_KERNEL_GTP:
- LOGPAPN(LOGL_ERROR, apn, "FIXME: Kernel GTP\n");
-#if 0
- /* use GTP kernel module for data packet encapsulation */
- if (gtp_kernel_init(gsn, &net.v4, prefixlen, net_arg) < 0)
- goto err;
-#endif
- break;
- default:
- LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
- return -1;
+ if (apn->tun.cfg.ipup_script) {
+ LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
+ apn->tun.cfg.ipup_script);
+ tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
+ }
+
+ if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
+ apn->v6.cfg.ll_prefix.addr.len == 0) {
+ rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
+ if (rc < 1) {
+ LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
+ rc ? strerror(errno) : "tun interface has no link-local IP assigned");
+ apn_stop(apn, false);
+ return -1;
+ }
+ apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
}
/* Create IPv4 pool */
if (apn->v4.cfg.dynamic_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
in46p_ntoa(&apn->v4.cfg.dynamic_prefix));
- if ((blacklist_size = alloc_ippool_blacklist(apn, apn->tun.tun, &blacklist, false)) < 0)
+ 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,
&apn->v4.cfg.static_prefix, ippool_flags,
@@ -288,7 +328,7 @@ int apn_start(struct apn_ctx *apn)
if (apn->v6.cfg.dynamic_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n",
in46p_ntoa(&apn->v6.cfg.dynamic_prefix));
- if ((blacklist_size = alloc_ippool_blacklist(apn, apn->tun.tun, &blacklist, true)) < 0)
+ 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,
&apn->v6.cfg.static_prefix, ippool_flags,
@@ -325,20 +365,26 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
static int delete_context(struct pdp_t *pdp)
{
struct gsn_t *gsn = pdp->gsn;
- struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
+ struct apn_ctx *apn = pdp->priv;
+ struct ippoolm_t *member;
+ int i;
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
- struct ippoolm_t *member = pdp->peer;
- if (pdp->peer) {
- send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
- ippool_freeip(ipp->pool, ipp);
- } else
- LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
+ for (i = 0; i < 2; i++) {
+ if (pdp->peer[i]) {
+ member = pdp->peer[i];
+ send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
+ ippool_freeip(member->pool, member);
+ } else if(i == 0)
+ LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
+ }
- if (gtp_kernel_tunnel_del(pdp)) {
- LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
- strerror(errno));
+ if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
+ if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
+ LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
+ strerror(errno));
+ }
}
return 0;
@@ -346,6 +392,41 @@ static int delete_context(struct pdp_t *pdp)
#include <osmocom/gsm/tlv.h>
+/* RFC 1332 */
+enum ipcp_options {
+ IPCP_OPT_IPADDR = 3,
+ IPCP_OPT_PRIMARY_DNS = 129,
+ IPCP_OPT_SECONDARY_DNS = 131,
+};
+
+struct ipcp_option_hdr {
+ uint8_t type;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct ipcp_hdr {
+ uint8_t code;
+ uint8_t id;
+ uint16_t len;
+ uint8_t options[0];
+} __attribute__ ((packed));
+
+/* determine if IPCP contains given option */
+static struct ipcp_option_hdr *ipcp_contains_option(struct ipcp_hdr *ipcp, enum ipcp_options opt)
+{
+ uint8_t *cur = ipcp->options;
+
+ /* iterate over Options and check if protocol contained */
+ while (cur + 2 <= ((uint8_t *)ipcp) + ntohs(ipcp->len)) {
+ struct ipcp_option_hdr *cur_opt = (struct ipcp_option_hdr *) cur;
+ if (cur_opt->type == opt)
+ return cur_opt;
+ cur += cur_opt->len;
+ }
+ return NULL;
+}
+
/* 3GPP TS 24.008 10.6.5.3 */
enum pco_protocols {
PCO_P_LCP = 0xC021,
@@ -379,7 +460,7 @@ enum pco_protocols {
};
/* determine if PCO contains given protocol */
-static bool pco_contains_proto(struct ul255_t *pco, uint16_t prot)
+static uint8_t *pco_contains_proto(struct ul255_t *pco, uint16_t prot)
{
uint8_t *cur = pco->v + 1;
@@ -388,50 +469,69 @@ static bool pco_contains_proto(struct ul255_t *pco, uint16_t prot)
uint16_t cur_prot = osmo_load16be(cur);
uint8_t cur_len = cur[2];
if (cur_prot == prot)
- return true;
- if (cur_len == 0)
- break;
+ return cur;
cur += cur_len + 3;
}
- return false;
+ return NULL;
}
-/* determine if PDP context has IPv6 support */
-static bool pdp_has_v4(struct pdp_t *pdp)
-{
- if (pdp->eua.l == 4+2)
- return true;
- else
- return false;
+/*! Get the peer of pdp based on IP version used.
+ * \param[in] pdp PDP context to select the peer from.
+ * \param[in] v4v6 IP version to select. Valid values are 4 and 6.
+ * \returns The selected peer matching the given IP version. NULL if not present.
+ */
+static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
+ uint8_t len1, len2, i;
+
+ if (is_ipv6) {
+ len1 = 8;
+ len2 = 16;
+ } else {
+ len1 = sizeof(struct in_addr);
+ len2 = len1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ struct ippoolm_t * ippool = pdp->peer[i];
+ if (ippool && (ippool->addr.len == len1 || ippool->addr.len == len2))
+ return ippool;
+ }
+ return NULL;
}
-/* construct an IPCP PCO from up to two given DNS addreses */
-static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *dns1,
- const struct in46_addr *dns2)
+/* construct an IPCP PCO response from request*/
+static int 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];
+ const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
+ struct ipcp_hdr *ipcp;
+ uint8_t *len1, *len2, *pco_ipcp;
uint8_t *start = msg->tail;
unsigned int len_appended;
+ if (!(pco_ipcp = pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)))
+ return 0;
+ ipcp = (struct ipcp_hdr*) (pco_ipcp + 3); /* 2=type + 1=len */
+
/* Three byte T16L header */
msgb_put_u16(msg, 0x8021); /* IPCP */
len1 = msgb_put(msg, 1); /* Length of contents: delay */
msgb_put_u8(msg, 0x02); /* ACK */
- msgb_put_u8(msg, id); /* ID: Needs to match request */
+ msgb_put_u8(msg, ipcp->id); /* ID: Needs to match request */
msgb_put_u8(msg, 0x00); /* Length MSB */
len2 = msgb_put(msg, 1); /* Length LSB: delay */
- if (dns1 && dns1->len == 4) {
+ if (dns1->len == 4 && ipcp_contains_option(ipcp, IPCP_OPT_PRIMARY_DNS)) {
msgb_put_u8(msg, 0x81); /* DNS1 Tag */
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
- msgb_put_u32(msg, 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_OPT_SECONDARY_DNS)) {
msgb_put_u8(msg, 0x83); /* DNS2 Tag */
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
- msgb_put_u32(msg, dns2->v4.s_addr);
+ msgb_put_u32(msg, ntohl(dns2->v4.s_addr));
}
/* patch in length values */
@@ -446,16 +546,14 @@ static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *
static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
{
struct msgb *msg = msgb_alloc(256, "PCO");
+ struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
unsigned int i;
OSMO_ASSERT(msg);
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
- /* FIXME: also check if primary / secondary DNS was requested */
- if (pdp_has_v4(pdp) && pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)) {
- /* FIXME: properly implement this for IPCP */
- build_ipcp_pco(msg, 0, &apn->v4.cfg.dns[0], &apn->v4.cfg.dns[1]);
- }
+ if (peer_v4)
+ build_ipcp_pco(apn, pdp, msg);
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR)) {
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
@@ -503,10 +601,11 @@ int create_context_ind(struct pdp_t *pdp)
static char name_buf[256];
struct gsn_t *gsn = pdp->gsn;
struct ggsn_ctx *ggsn = gsn->priv;
- struct in46_addr addr;
- struct ippoolm_t *member;
+ struct in46_addr addr[2];
+ struct ippoolm_t *member = NULL, *addrv4 = NULL, *addrv6 = NULL;
+ char straddrv4[INET_ADDRSTRLEN], straddrv6[INET6_ADDRSTRLEN];
struct apn_ctx *apn;
- int rc;
+ int rc, num_addr, i;
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
@@ -541,55 +640,69 @@ int create_context_ind(struct pdp_t *pdp)
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
pdp->qos_neg.l = pdp->qos_req.l;
- if (in46a_from_eua(&pdp->eua, &addr)) {
+ memset(addr, 0, sizeof(addr));
+ if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
osmo_hexdump(pdp->eua.v, pdp->eua.l));
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
return 0;
}
- if (addr.len == sizeof(struct in_addr)) {
- /* does this APN actually have an IPv4 pool? */
- if (!apn_supports_ipv4(apn))
- goto err_wrong_af;
+ /* Allocate dynamic addresses from the pool */
+ for (i = 0; i < num_addr; i++) {
+ if (addr[i].len == sizeof(struct in_addr)) {
+ /* does this APN actually have an IPv4 pool? */
+ if (!apn_supports_ipv4(apn))
+ goto err_wrong_af;
+
+ rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
+ if (rc < 0)
+ goto err_pool_full;
+ /* copy back */
+ memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
+
+ addrv4 = member;
+
+ } else if (addr[i].len == sizeof(struct in6_addr)) {
+
+ /* does this APN actually have an IPv6 pool? */
+ if (!apn_supports_ipv6(apn))
+ goto err_wrong_af;
+
+ rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
+ if (rc < 0)
+ goto err_pool_full;
- rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
- if (rc < 0)
- goto err_pool_full;
- in46a_to_eua(&member->addr, &pdp->eua);
+ /* IPv6 doesn't really send the real/allocated address at this point, but just
+ * the link-identifier which the MS shall use for router solicitation */
+ /* initialize upper 64 bits to prefix, they are discarded by MS anyway */
+ memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
+ /* use allocated 64bit prefix as lower 64bit, used as link id by MS */
+ memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
+ addrv6 = member;
+ } else
+ OSMO_ASSERT(0);
+
+ pdp->peer[i] = member;
+ member->peer = pdp;
+ }
+
+ in46a_to_eua(addr, num_addr, &pdp->eua);
+
+ if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP && apn_supports_ipv4(apn)) {
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
- if (gtp_kernel_tunnel_add(pdp) < 0) {
+ if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
return 0;
}
- } else if (addr.len == sizeof(struct in6_addr)) {
- struct in46_addr tmp;
-
- /* does this APN actually have an IPv6 pool? */
- if (!apn_supports_ipv6(apn))
- goto err_wrong_af;
-
- rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
- if (rc < 0)
- goto err_pool_full;
-
- /* IPv6 doesn't really send the real/allocated address at this point, but just
- * the link-identifier which the MS shall use for router solicitation */
- tmp.len = addr.len;
- /* initialize upper 64 bits to prefix, they are discarded by MS anyway */
- memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
- /* use allocated 64bit prefix as lower 64bit, used as link id by MS */
- memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
- in46a_to_eua(&tmp, &pdp->eua);
- } else
- OSMO_ASSERT(0);
+ }
- pdp->peer = member;
pdp->ipif = apn->tun.tun; /* TODO */
- member->peer = pdp;
+ pdp->priv = apn;
+ /* TODO: change trap to send 2 IPs */
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
return 0;
@@ -600,8 +713,10 @@ int create_context_ind(struct pdp_t *pdp)
/* Transmit G-PDU sequence numbers (only) if configured in APN */
pdp->tx_gpdu_seq = apn->cfg.tx_gpdu_seq;
- LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IP=%s\n",
- name_buf, apn->cfg.name, pdp->teic_own, in46a_ntoa(&member->addr));
+ LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IPv4=%s, IPv6=%s\n",
+ name_buf, apn->cfg.name, pdp->teic_own,
+ addrv4 ? inet_ntop(AF_INET, &addrv4->addr.v4, straddrv4, sizeof(straddrv4)) : "none",
+ addrv6 ? inet_ntop(AF_INET6, &addrv6->addr.v6, straddrv6, sizeof(straddrv6)) : "none");
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
return 0; /* Success */
@@ -625,23 +740,31 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
struct ippool_t *pool;
+ char straddr[INET6_ADDRSTRLEN];
+ uint8_t pref_offset;
- if (iph->version == 4) {
+ switch (iph->version) {
+ case 4:
if (len < sizeof(*iph) || len < 4*iph->ihl)
return -1;
dst.len = 4;
dst.v4.s_addr = iph->daddr;
pool = apn->v4.pool;
- } else if (iph->version == 6) {
+ break;
+ case 6:
/* Due to the fact that 3GPP requires an allocation of a
* /64 prefix to each MS, we must instruct
* ippool_getip() below to match only the leading /64
- * prefix, i.e. the first 8 bytes of the address */
+ * prefix, i.e. the first 8 bytes of the address. If the ll addr
+ * is used, then the match should be done on the trailing 64
+ * bits. */
dst.len = 8;
- dst.v6 = ip6h->ip6_dst;
+ pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
+ memcpy(&dst.v6, ((uint8_t*)&ip6h->ip6_dst) + pref_offset, 8);
pool = apn->v6.pool;
- } else {
- LOGP(DTUN, LOGL_NOTICE, "non-IPv packet received from tun\n");
+ break;
+ default:
+ LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
return -1;
}
@@ -649,12 +772,15 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
if (!pool)
return 0;
- DEBUGP(DTUN, "Received packet from tun!\n");
+ DEBUGP(DTUN, "Received packet for APN(%s) from tun %s", apn->cfg.name, tun->devname);
if (ippool_getip(pool, &ipm, &dst)) {
- DEBUGP(DTUN, "Received packet with no PDP contex!!\n");
+ DEBUGPC(DTUN, " with no PDP contex! (%s)\n", iph->version == 4 ?
+ inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)) :
+ inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
return 0;
}
+ DEBUGPC(DTUN, "\n");
if (ipm->peer) /* Check if a peer protocol is defined */
gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
@@ -673,19 +799,53 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
struct tun_t *tun = (struct tun_t *)pdp->ipif;
struct apn_ctx *apn = tun->priv;
+ char straddr[INET6_ADDRSTRLEN];
+ struct ippoolm_t *peer;
+ uint8_t pref_offset;
OSMO_ASSERT(tun);
OSMO_ASSERT(apn);
- LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n");
+ LOGPPDP(LOGL_DEBUG, pdp, "Packet received on APN(%s): forwarding to tun %s\n", apn->cfg.name, tun->devname);
switch (iph->version) {
case 6:
+ peer = pdp_get_peer_ipv(pdp, true);
+ if (!peer) {
+ LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv6 with unassigned EUA: %s\n",
+ osmo_hexdump(pack, len));
+ return -1;
+ }
+
+ /* Validate packet comes from IPaddr assigned to the pdp ctx.
+ If packet is a LL addr, then EUA is in the lower 64 bits,
+ otherwise it's used as the 64 prefix */
+ pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ? 8 : 0;
+ if (memcmp(((uint8_t*)&ip6h->ip6_src) + pref_offset, &peer->addr.v6, 8)) {
+ LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv6: %s\n",
+ inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
+ return -1;
+ }
+
/* daddr: all-routers multicast addr */
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
- return handle_router_mcast(pdp->gsn, pdp, &apn->v6_lladdr, pack, len);
+ return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
+ &apn->v6_lladdr, pack, len);
break;
case 4:
+ peer = pdp_get_peer_ipv(pdp, false);
+ if (!peer) {
+ LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv4 with unassigned EUA: %s\n",
+ osmo_hexdump(pack, len));
+ return -1;
+ }
+
+ /* Validate packet comes from IPaddr assigned to the pdp ctx */
+ if (memcmp(&iph->saddr, &peer->addr.v4, sizeof(peer->addr.v4))) {
+ LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv4: %s\n",
+ inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)));
+ return -1;
+ }
break;
default:
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
@@ -917,6 +1077,7 @@ int main(int argc, char **argv)
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
+ g_vty_info.tall_ctx = tall_ggsn_ctx;
/* Handle keyboard interrupt SIGINT */
signal(SIGINT, &signal_handler);
@@ -926,11 +1087,12 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
- osmo_init_logging(&log_info);
+ osmo_init_logging2(tall_ggsn_ctx, &log_info);
osmo_stats_init(tall_ggsn_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds(NULL);
+ osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds(&log_info);
ggsn_vty_init();
ctrl_vty_init(tall_ggsn_ctx);
diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h
index c0774c4..e252548 100644
--- a/ggsn/ggsn.h
+++ b/ggsn/ggsn.h
@@ -22,6 +22,7 @@ struct ggsn_ctx;
struct apn_ctx_ip {
struct {
struct in46_prefix ifconfig_prefix;
+ struct in46_prefix ll_prefix;
struct in46_prefix static_prefix;
struct in46_prefix dynamic_prefix;
/* v4 DNS server names */
diff --git a/ggsn/ggsn_vty.c b/ggsn/ggsn_vty.c
index 9f343ec..53f4ebf 100644
--- a/ggsn/ggsn_vty.c
+++ b/ggsn/ggsn_vty.c
@@ -513,6 +513,24 @@ DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
+ "ipv6 link-local X:X::X:X/M",
+ IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
+ "no ipv6 link-local",
+ NO_STR IP6_STR IFCONFIG_STR)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
+ return CMD_SUCCESS;
+}
+
#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
@@ -668,6 +686,8 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
}
if (apn->v6.cfg.ifconfig_prefix.addr.len)
vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
+ if (apn->v6.cfg.ll_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
/* must be last */
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
@@ -856,8 +876,8 @@ int ggsn_vty_init(void)
install_element(CONFIG_NODE, &cfg_ggsn_cmd);
install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
+
install_node(&ggsn_node, config_write_ggsn);
- vty_install_default(GGSN_NODE);
install_element(GGSN_NODE, &cfg_description_cmd);
install_element(GGSN_NODE, &cfg_no_description_cmd);
install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
@@ -872,7 +892,6 @@ int ggsn_vty_init(void)
install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
install_node(&apn_node, NULL);
- vty_install_default(APN_NODE);
install_element(APN_NODE, &cfg_description_cmd);
install_element(APN_NODE, &cfg_no_description_cmd);
install_element(APN_NODE, &cfg_apn_shutdown_cmd);
@@ -894,6 +913,8 @@ int ggsn_vty_init(void)
install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
+ install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
@@ -927,6 +948,10 @@ static int ggsn_vty_go_parent(struct vty *vty)
vty->index_sub = &apn->ggsn->cfg.description;
}
break;
+ default:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
}
return vty->node;
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c
deleted file mode 100644
index f98586d..0000000
--- a/ggsn/gtp-kernel.c
+++ /dev/null
@@ -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;
-}
diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h
deleted file mode 100644
index 34fd7bf..0000000
--- a/ggsn/gtp-kernel.h
+++ /dev/null
@@ -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_ */
diff --git a/ggsn/icmpv6.c b/ggsn/icmpv6.c
index 11ced24..b7b97eb 100644
--- a/ggsn/icmpv6.c
+++ b/ggsn/icmpv6.c
@@ -180,17 +180,15 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
}
/* handle incoming packets to the all-routers multicast address */
-int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
+int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
+ const struct in6_addr *pdp_prefix,
+ const struct in6_addr *own_ll_addr,
const uint8_t *pack, unsigned len)
{
- struct ippoolm_t *member = pdp->peer;
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
struct msgb *msg;
- OSMO_ASSERT(pdp);
- OSMO_ASSERT(member);
-
if (len < sizeof(*ip6h)) {
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
return -1;
@@ -221,7 +219,7 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_a
/* Send router advertisement from GGSN link-local
* address to MS link-local address, including prefix
* allocated to this PDP context */
- msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, &member->addr.v6);
+ msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, pdp_prefix);
/* Send the constructed RA to the MS */
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
msgb_free(msg);
diff --git a/ggsn/icmpv6.h b/ggsn/icmpv6.h
index b6eec63..bf91e27 100644
--- a/ggsn/icmpv6.h
+++ b/ggsn/icmpv6.h
@@ -3,5 +3,7 @@
#include "../gtp/gtp.h"
#include "../gtp/pdp.h"
-int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
+int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
+ const struct in6_addr *pdp_prefix,
+ const struct in6_addr *own_ll_addr,
const uint8_t *pack, unsigned len);
diff --git a/gtp/Makefile.am b/gtp/Makefile.am
index 7a0ff0a..449292c 100644
--- a/gtp/Makefile.am
+++ b/gtp/Makefile.am
@@ -1,7 +1,9 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=2:0:0
+# If major=current-age is increased, remember to update the dh_strip line in debian/rules!
+LIBVERSION=3:0:0
+
lib_LTLIBRARIES = libgtp.la
include_HEADERS = gtp.h pdp.h gtpie.h
@@ -11,7 +13,3 @@ AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_
libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
-
-
-
-
diff --git a/gtp/gtp.c b/gtp/gtp.c
index 15b9f55..e6d5236 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -1,19 +1,19 @@
-/*
+/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2010-2011, 2016-2017 Harald Welte <laforge@gnumonks.org>
* Copyright (C) 2015-2017 sysmocom - s.f.m.c. GmbH
- *
+ *
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
- *
+ *
*/
/*
* gtp.c: Contains all GTP functionality. Should be able to handle multiple
- * tunnels in the same program.
+ * tunnels in the same program.
*
* TODO:
* - Do we need to handle fragmentation?
@@ -86,6 +86,51 @@ const char *gtp_version()
return VERSION;
}
+const struct value_string gtp_type_names[] = {
+ { GTP_ECHO_REQ, "Echo Request" },
+ { GTP_ECHO_RSP, "Echo Response" },
+ { GTP_NOT_SUPPORTED, "Version Not Supported" },
+ { GTP_ALIVE_REQ, "Node Alive Request" },
+ { GTP_ALIVE_RSP, "Node Alive Response" },
+ { GTP_REDIR_REQ, "Redirection Request" },
+ { GTP_REDIR_RSP, "Redirection Response" },
+ { GTP_CREATE_PDP_REQ, "Create PDP Context Request" },
+ { GTP_CREATE_PDP_RSP, "Create PDP Context Response" },
+ { GTP_UPDATE_PDP_REQ, "Update PDP Context Request" },
+ { GTP_UPDATE_PDP_RSP, "Update PDP Context Response" },
+ { GTP_DELETE_PDP_REQ, "Delete PDP Context Request" },
+ { GTP_DELETE_PDP_RSP, "Delete PDP Context Response" },
+ { GTP_ERROR, "Error Indication" },
+ { GTP_PDU_NOT_REQ, "PDU Notification Request" },
+ { GTP_PDU_NOT_RSP, "PDU Notification Response" },
+ { GTP_PDU_NOT_REJ_REQ, "PDU Notification Reject Request" },
+ { GTP_PDU_NOT_REJ_RSP, "PDU Notification Reject Response" },
+ { GTP_SUPP_EXT_HEADER, "Supported Extension Headers Notification" },
+ { GTP_SND_ROUTE_REQ, "Send Routeing Information for GPRS Request" },
+ { GTP_SND_ROUTE_RSP, "Send Routeing Information for GPRS Response" },
+ { GTP_FAILURE_REQ, "Failure Report Request" },
+ { GTP_FAILURE_RSP, "Failure Report Response" },
+ { GTP_MS_PRESENT_REQ, "Note MS GPRS Present Request" },
+ { GTP_MS_PRESENT_RSP, "Note MS GPRS Present Response" },
+ { GTP_IDEN_REQ, "Identification Request" },
+ { GTP_IDEN_RSP, "Identification Response" },
+ { GTP_SGSN_CONTEXT_REQ,"SGSN Context Request" },
+ { GTP_SGSN_CONTEXT_RSP,"SGSN Context Response" },
+ { GTP_SGSN_CONTEXT_ACK,"SGSN Context Acknowledge" },
+ { GTP_FWD_RELOC_REQ, "Forward Relocation Request" },
+ { GTP_FWD_RELOC_RSP, "Forward Relocation Response" },
+ { GTP_FWD_RELOC_COMPL, "Forward Relocation Complete" },
+ { GTP_RELOC_CANCEL_REQ,"Relocation Cancel Request" },
+ { GTP_RELOC_CANCEL_RSP,"Relocation Cancel Response" },
+ { GTP_FWD_SRNS, "Forward SRNS Context" },
+ { GTP_FWD_RELOC_ACK, "Forward Relocation Complete Acknowledge" },
+ { GTP_FWD_SRNS_ACK, "Forward SRNS Context Acknowledge" },
+ { GTP_DATA_TRAN_REQ, "Data Record Transfer Request" },
+ { GTP_DATA_TRAN_RSP, "Data Record Transfer Response" },
+ { GTP_GPDU, "G-PDU" },
+ { 0, NULL }
+};
+
/* gtp_new */
/* gtp_free */
@@ -152,7 +197,7 @@ int gtp_set_cb_recovery(struct gsn_t *gsn,
return 0;
}
-extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
+int gtp_set_cb_data_ind(struct gsn_t *gsn,
int (*cb_data_ind) (struct pdp_t * pdp,
void *pack, unsigned len))
{
@@ -168,7 +213,7 @@ extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
* to hold the packet header.
* returns the length of the header. 0 on error.
**/
-static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
+static unsigned int get_default_gtp(uint8_t version, uint8_t type, void *packet)
{
struct gtp0_header *gtp0_default = (struct gtp0_header *)packet;
struct gtp1_header_long *gtp1_default =
@@ -281,19 +326,19 @@ static uint32_t get_tei(void *pack)
/* ***********************************************************
* Reliable delivery of signalling messages
- *
+ *
* Sequence numbers are used for both signalling messages and
* data messages.
*
* For data messages each tunnel maintains a sequence counter,
* which is incremented by one each time a new data message
* is sent. The sequence number starts at (0) zero at tunnel
- * establishment, and wraps around at 65535 (29.060 9.3.1.1
+ * establishment, and wraps around at 65535 (29.060 9.3.1.1
* and 09.60 8.1.1.1). The sequence numbers are either ignored,
* or can be used to check the validity of the message in the
* receiver, or for reordering af packets.
*
- * For signalling messages the sequence number is used by
+ * For signalling messages the sequence number is used by
* signalling messages for which a response is defined. A response
* message should copy the sequence from the corresponding request
* message. The sequence number "unambiguously" identifies a request
@@ -311,7 +356,7 @@ static uint32_t get_tei(void *pack)
* with path setup and teardown.
*
* If a response message is lost, the request will be retransmitted, and
- * the receiving GSN will receive a "duplicated" request. The standard
+ * the receiving GSN will receive a "duplicated" request. The standard
* requires the receiving GSN to send a response, with the same information
* as in the original response. For most messages this happens automatically:
*
@@ -326,22 +371,22 @@ static uint32_t get_tei(void *pack)
* a nonexist reply message.
*
* The correct solution will be to make a queue containing response messages.
- * This queue should be checked whenever a request is received. If the
- * response is already in the queue that response should be transmitted.
+ * This queue should be checked whenever a request is received. If the
+ * response is allready in the queue that response should be transmitted.
* It should be possible to find messages in this queue on the basis of
* the sequence number and peer GSN IP address (The sequense number is unique
* within each path). This need to be implemented by a hash table. Furthermore
* it should be possibly to delete messages based on a timeout. This can be
* achieved by means of a linked list. The timeout value need to be larger
- * than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
+ * than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
* set in the peer GSN, so there is no way to know these parameters. On the
* other hand the timeout value need to be so small that we do not receive
* wraparound sequence numbere before the message is deleted. 60 seconds is
* probably not a bad choise.
- *
+ *
* This queue however is first really needed from gtp1.
*
- * gtp_req:
+ * gtp_req:
* Send off a signalling message with appropiate sequence
* number. Store packet in queue.
* gtp_conf:
@@ -357,7 +402,7 @@ static uint32_t get_tei(void *pack)
* a predefined timeout.
*************************************************************/
-int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
+static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
union gtp_packet *packet, int len,
struct in_addr *inetaddr, void *cbp)
{
@@ -432,7 +477,7 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
* Remove signalling packet from retransmission queue.
* return 0 on success, EOF if packet was not found */
-int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
union gtp_packet *packet, int len, uint8_t * type, void **cbp)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
@@ -523,7 +568,7 @@ int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
return 0;
}
-int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
+static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
{
@@ -581,7 +626,7 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
return 0;
}
-int gtp_notification(struct gsn_t *gsn, int version,
+static int gtp_notification(struct gsn_t *gsn, uint8_t version,
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq)
{
@@ -626,7 +671,7 @@ int gtp_notification(struct gsn_t *gsn, int version,
return 0;
}
-int gtp_dublicate(struct gsn_t *gsn, int version,
+static int gtp_dublicate(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, uint16_t seq)
{
struct qmsg_t *qmsg;
@@ -852,7 +897,7 @@ int gtp_free(struct gsn_t *gsn)
* For response messages we need to be able to respond to
* the relevant src port even if it is locally allocated by
* the peer.
- *
+ *
* The need for path management!
* We might need to keep a list of active paths. This might
* be in the form of remote IP address + UDP port numbers.
@@ -942,9 +987,9 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
/* This message is somewhat special in that it actually is a
* response to some other message with unsupported GTP version
* For this reason it has parameters like a response, and does
- * its own message transmission. No signalling queue is used
+ * its own message transmission. No signalling queue is used
* The reply is sent to the peer IP and peer UDP. This means that
- * the peer will be receiving a GTP0 message on a GTP1 port!
+ * the peer will be receiving a GTP0 message on a GTP1 port!
* In practice however this will never happen as a GTP0 GSN will
* only listen to the GTP0 port, and therefore will never receive
* anything else than GTP0 */
@@ -971,7 +1016,7 @@ int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
}
/* Send off an Supported Extension Headers Notification */
-int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+static int gtp_extheader_req(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
int fd, void *pack, unsigned len)
{
union gtp_packet packet;
@@ -992,7 +1037,7 @@ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
}
/* Handle a Supported Extension Headers Notification */
-int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+static int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
void *pack, unsigned len)
{
@@ -1007,7 +1052,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
* Messages: create, update and delete PDP context
*
* Information storage
- * Information storage for each PDP context is defined in
+ * Information storage for each PDP context is defined in
* 23.060 section 13.3. Includes IMSI, MSISDN, APN, PDP-type,
* PDP-address (IP address), sequence numbers, charging ID.
* For the SGSN it also includes radio related mobility
@@ -1015,7 +1060,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
*************************************************************/
/* API: Send Create PDP Context Request (7.3.1) */
-extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
+int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp)
{
union gtp_packet packet;
@@ -1093,7 +1138,7 @@ extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
pdp->cch_pdp);
}
- /* TODO
+ /* TODO
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
pdp->traceref);
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
@@ -1537,7 +1582,7 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
(!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l)))
{
/* OK! We are dealing with the same APN. We will copy new
- * parameters to the old pdp and send off confirmation
+ * parameters to the old pdp and send off confirmation
* We ignore the following information elements:
* QoS: MS will get originally negotiated QoS.
* End user address (EUA). MS will get old EUA anyway.
@@ -1853,14 +1898,14 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi);
- /* TODO
+ /* TODO
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
pdp->traceref);
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
pdp->tracetype); */
/* TODO if ggsn update message
- gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
pdp->eua.l, pdp->eua.v);
*/
@@ -1891,7 +1936,7 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
}
/* Send Update PDP Context Response */
-int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
+static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, uint8_t cause)
@@ -1932,8 +1977,8 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID,
pdp->teid_own);
- /* If ggsn
- gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
+ /* If ggsn
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
pdp->eua.l, pdp->eua.v); */
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
@@ -1953,7 +1998,7 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
}
/* Handle Update PDP Context Request */
-int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
+static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
@@ -2127,7 +2172,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing mandatory information field");
memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
- return gtp_update_pdp_resp(gsn, version, pdp,
+ return gtp_update_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
} */
@@ -2181,7 +2226,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
}
/* Handle Update PDP Context Response */
-int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
+static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, void *pack, unsigned len)
{
struct pdp_t *pdp;
@@ -2397,7 +2442,7 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
if (pdp_getgtp1
(&secondary_pdp,
linked_pdp->secondary_tei[n])) {
- LOGP(DLGTP, LOGL_ERROR,
+ LOGP(DLGTP, LOGL_ERROR,
"Unknown secondary PDP context\n");
return EOF;
}
@@ -2569,7 +2614,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
}
/* Send Error Indication (response to a GPDU message) - 3GPP TS 29.060 7.3.7 */
-int gtp_error_ind_resp(struct gsn_t *gsn, int version,
+static int gtp_error_ind_resp(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
@@ -2591,7 +2636,7 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version,
}
/* Handle Error Indication */
-int gtp_error_ind_conf(struct gsn_t *gsn, int version,
+static int gtp_error_ind_conf(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, void *pack, unsigned len)
{
union gtpie_member *ie[GTPIE_SIZE];
@@ -2635,22 +2680,28 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"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)
gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return 0;
}
-int gtp_gpdu_ind(struct gsn_t *gsn, int version,
+static int gtp_gpdu_ind(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd, void *pack, unsigned len)
{
- int hlen = GTP1_HEADER_SIZE_SHORT;
+ int hlen;
- /* Need to include code to verify packet src and dest addresses */
struct pdp_t *pdp;
- if (version == 0) {
+ switch (version) {
+ case 0:
if (pdp_getgtp0
(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) {
gsn->err_unknownpdp++;
@@ -2660,7 +2711,8 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
len);
}
hlen = GTP0_HEADER_SIZE;
- } else if (version == 1) {
+ break;
+ case 1:
if (pdp_getgtp1
(&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) {
gsn->err_unknownpdp++;
@@ -2675,9 +2727,11 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
hlen = GTP1_HEADER_SIZE_LONG;
else
hlen = GTP1_HEADER_SIZE_SHORT;
- } else {
+ break;
+ default:
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Unknown version: %d\n", version);
+ return EOF;
}
/* If the GPDU was not from the peer GSN tell him to delete context */
@@ -2694,10 +2748,10 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
return 0;
}
-/* Receives GTP packet and sends off for further processing
+/* Receives GTP packet and sends off for further processing
* Function will check the validity of the header. If the header
- * is not valid the packet is either dropped or a version not
- * supported is returned to the peer.
+ * is not valid the packet is either dropped or a version not
+ * supported is returned to the peer.
* TODO: Need to decide on return values! */
int gtp_decaps0(struct gsn_t *gsn)
{
@@ -2706,7 +2760,7 @@ int gtp_decaps0(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp0_header *pheader;
- int version = 0; /* GTP version should be determined from header! */
+ uint8_t version;
int fd = gsn->fd0;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -2742,15 +2796,17 @@ int gtp_decaps0(struct gsn_t *gsn)
pheader = (struct gtp0_header *)(buffer);
+ version = GTPHDR_F_GET_VER(pheader->flags);
+
/* Version should be gtp0 (or earlier) */
/* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */
/* GTP 0 messages. If other version message is received we reply that we */
/* only support version 0, implying that this is the only version */
/* supported on this port */
- if (GTPHDR_F_GET_VER(pheader->flags) > 0) {
+ if (version > 0) {
gsn->unsup++;
- GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
- status, "Unsupported GTP version\n");
+ GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
+ "Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
continue;
}
@@ -2851,7 +2907,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp1_header_short *pheader;
- int version = 1; /* TODO GTP version should be determined from header! */
+ uint8_t version;
int fd = gsn->fd1c;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -2887,11 +2943,13 @@ int gtp_decaps1c(struct gsn_t *gsn)
pheader = (struct gtp1_header_short *)(buffer);
+ version = GTPHDR_F_GET_VER(pheader->flags);
+
/* Version must be no larger than GTP 1 */
- if (GTPHDR_F_GET_VER(pheader->flags) > 1) {
+ if (version > 1) {
gsn->unsup++;
- GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
- status, "Unsupported GTP version\n");
+ GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
+ "Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, version, &peer, fd, buffer, status);
/*29.60: 11.1.1 */
continue;
@@ -2901,10 +2959,10 @@ int gtp_decaps1c(struct gsn_t *gsn)
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
/* GTP 1 messages. If GTP 0 message is received we silently discard */
/* the message */
- if (GTPHDR_F_GET_VER(pheader->flags) < 1) {
+ if (version < 1) {
gsn->unsup++;
- GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
- status, "Unsupported GTP version\n");
+ GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
+ "Unsupported GTP version %"PRIu8"\n", version);
continue;
}
@@ -3026,7 +3084,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp1_header_short *pheader;
- int version = 1; /* GTP version should be determined from header! */
+ uint8_t version;
int fd = gsn->fd1u;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -3063,11 +3121,13 @@ int gtp_decaps1u(struct gsn_t *gsn)
pheader = (struct gtp1_header_short *)(buffer);
+ version = GTPHDR_F_GET_VER(pheader->flags);
+
/* Version must be no larger than GTP 1 */
- if (GTPHDR_F_GET_VER(pheader->flags) > 1) {
+ if (version > 1) {
gsn->unsup++;
- GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
- status, "Unsupported GTP version\n");
+ GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
+ "Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */
continue;
}
@@ -3076,10 +3136,10 @@ int gtp_decaps1u(struct gsn_t *gsn)
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
/* GTP 1 messages. If GTP 0 message is received we silently discard */
/* the message */
- if (GTPHDR_F_GET_VER(pheader->flags) < 1) {
+ if (version < 1) {
gsn->unsup++;
- GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
- status, "Unsupported GTP version\n");
+ GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
+ "Unsupported GTP version %"PRIu8"\n", version);
continue;
}
@@ -3235,19 +3295,10 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
* Conversion functions
*************************************************************/
-int char2ul_t(char *src, struct ul_t dst)
-{
- dst.l = strlen(src) + 1;
- dst.v = malloc(dst.l);
- dst.v[0] = dst.l - 1;
- memcpy(&dst.v[1], src, dst.v[0]);
- return 0;
-}
-
/* ***********************************************************
* IP address conversion functions
* There exist several types of address representations:
- * - eua: End User Address. (29.060, 7.7.27, message type 128)
+ * - eua: End User Address. (29.060, 7.7.27, message type 128)
* Used for signalling address to mobile station. Supports IPv4
* IPv6 x.25 etc. etc.
* - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address
@@ -3301,7 +3352,7 @@ int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src)
* _network byte order_ to contain BCD digits ?!? */
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;
unsigned int i, j = 0;
diff --git a/gtp/gtp.h b/gtp/gtp.h
index d189ded..8f8e293 100644
--- a/gtp/gtp.h
+++ b/gtp/gtp.h
@@ -12,6 +12,8 @@
#ifndef _GTP_H
#define _GTP_H
+#include <osmocom/core/utils.h>
+
#define GTP_MODE_GGSN 1
#define GTP_MODE_SGSN 2
@@ -85,6 +87,10 @@
/* 242-254 For future use. */
#define GTP_GPDU 255 /* G-PDU */
+extern const struct value_string gtp_type_names[];
+static inline const char *gtp_type_name(uint8_t val)
+{ return get_value_string(gtp_type_names, val); }
+
/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
/* */
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
diff --git a/gtp/pdp.c b/gtp/pdp.c
index b1e1ff3..a630ee9 100644
--- a/gtp/pdp.c
+++ b/gtp/pdp.c
@@ -1,17 +1,17 @@
-/*
+/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017 Harald Welte <laforge@gnumonks.org>
- *
+ *
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
- *
+ *
*/
/*
- * pdp.c:
+ * pdp.c:
*
*/
@@ -44,17 +44,17 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Functions related to PDP storage
*
* Lifecycle
- * For a GGSN pdp context life begins with the reception of a
+ * For a GGSN pdp context life begins with the reception of a
* create pdp context request. It normally ends with the reception
* of a delete pdp context request, but will also end with the
- * reception of an error indication message.
+ * reception of an error indication message.
* Provisions should probably be made for terminating pdp contexts
- * based on either idle timeout, or by sending downlink probe
+ * based on either idle timeout, or by sending downlink probe
* messages (ping?) to see if the MS is still responding.
- *
+ *
* For an SGSN pdp context life begins with the application just
* before sending off a create pdp context request. It normally
- * ends when a delete pdp context response message is received
+ * ends when a delete pdp context response message is received
* from the GGSN, but should also end when with the reception of
* an error indication message.
*
@@ -64,15 +64,15 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Downlink packets received in the GGSN are identified only by their
* network interface together with their destination IP address (Two
* network interfaces can use the same private IP address). Each IMSI
- * (mobile station) can have several PDP contexts using the same IP
+ * (mobile station) can have several PDP contexts using the same IP
* address. In this case the traffic flow template (TFT) is used to
- * determine the correct PDP context for a particular IMSI. Also it
+ * determine the correct PDP context for a particular IMSI. Also it
* should be possible for each PDP context to use several IP adresses
* For fixed wireless access a mobile station might need a full class
* C network. Even in the case of several IP adresses the PDP context
* should be determined on the basis of the network IP address.
* Thus we need a hash table based on network interface + IP address.
- *
+ *
* Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
* is collectively called the tunnel identifier. There is also a 16 bit
* flow label that can be used for identification of uplink packets. This
@@ -85,7 +85,7 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
* be used for directly addressing the PDP context.
- * pdp_newpdp
+ * pdp_newpdp
* Gives you a pdp context with no hash references In some way
* this should have a limited lifetime.
*
@@ -296,7 +296,7 @@ int pdp_iphash(void* ipif, struct ul66_t *eua) {
/#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
}
-
+
int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
int hash;
struct pdp_t *pdp2;
@@ -304,7 +304,7 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
(unsigned) ipif, eua->l,
- eua->v[2], eua->v[3],
+ eua->v[2], eua->v[3],
eua->v[4], eua->v[5]);
pdp->ipnext = NULL;
@@ -316,9 +316,9 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
pdp_prev = pdp2;
- if (!pdp_prev)
+ if (!pdp_prev)
haship[hash] = pdp;
- else
+ else
pdp_prev->ipnext = pdp;
if (PDP_DEBUG) printf("End pdp_ipset\n");
return 0;
@@ -331,9 +331,9 @@ int pdp_ipdel(struct pdp_t *pdp) {
if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
if (pdp2 == pdp) {
- if (!pdp_prev)
+ if (!pdp_prev)
haship[hash] = pdp2->ipnext;
- else
+ else
pdp_prev->ipnext = pdp2->ipnext;
if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
return 0;
@@ -347,41 +347,23 @@ int pdp_ipdel(struct pdp_t *pdp) {
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
int hash = pdp_iphash(ipif, eua);
struct pdp_t *pdp2;
- /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
+ /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
- if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
+ if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
(memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
*pdp = pdp2;
/#printf("End pdp_ipget. Found\n");#/
return 0;
}
}
- if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
+ if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
(unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
return EOF; /# End of linked list and not found #/
}
*/
/* Various conversion functions */
-int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
-{
- eua->l = 6;
- eua->v[0] = PDP_EUA_ORG_IETF;
- eua->v[1] = PDP_EUA_TYPE_v4;
- memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */
- return 0;
-}
-
-int pdp_euaton(struct ul66_t *eua, struct in_addr *dst)
-{
- if ((eua->l != 6) || (eua->v[0] != PDP_EUA_ORG_IETF) || (eua->v[1] != PDP_EUA_TYPE_v4)) {
- return EOF;
- }
- memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
- return 0;
-}
-
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
{
return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
diff --git a/gtp/pdp.h b/gtp/pdp.h
index 55ab435..c2345d2 100644
--- a/gtp/pdp.h
+++ b/gtp/pdp.h
@@ -1,13 +1,13 @@
-/*
+/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003 Mondru AB.
* Copyright (C) 2017 Harald Welte <laforge@gnumonks.org>
- *
+ *
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
- *
+ *
*/
#ifndef _PDP_H
@@ -26,6 +26,7 @@ struct gsn_t;
#define PDP_EUA_ORG_IETF 0xF1
#define PDP_EUA_TYPE_v4 0x21
#define PDP_EUA_TYPE_v6 0x57
+#define PDP_EUA_TYPE_v4v6 0x8D
/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
@@ -71,16 +72,16 @@ struct ul255_t {
* and 09.60.
* 31 * 4 + 15 structs + = 120 + 15 structs ~ 2k / context
* Structs: IP address 16+4 bytes (6), APN 255 bytes (2)
- * QOS: 255 bytes (3), msisdn 16 bytes (1),
+ * QOS: 255 bytes (3), msisdn 16 bytes (1),
*
* TODO: We need to consider who manages the pdp_t hash tables
* Is it gtp_lib, or is it the application?
- * I suppose that it will be gtp_lib.
+ * I suppose that it will be gtp_lib.
* SGSN will ask gtplib for new pdp_t. Fill out the fields,
* and pass it on to gtp_create_pdp_req.
* GGSN will receive gtp_create_pdp_ind, create new pdp_t and
* send responce to SGSN.
- * SGSN will receive response and gtplib will find the
+ * SGSN will receive response and gtplib will find the
* original pdp_t corresponding to the request. This will be
* passed on to the application.
* Eventually the SGSN will close the connection, and the
@@ -88,10 +89,10 @@ struct ul255_t {
* This means that gtplib need to have functions to
* allocate, free, sort and find pdp_t
* (newpdp, freepdp, getpdp)
- * Hash tables: TID, IMSI, IP etc.)
+ * Hash tables: TID, IMSI, IP etc.)
*
*
- * Secondary PDP Context Activation Procedure
+ * Secondary PDP Context Activation Procedure
*
* With GTP version 1 it is possible to establish multiple PDP
* contexts with the same IP address. With this scheme the first
@@ -121,7 +122,7 @@ struct pdp_t {
/* Parameters shared by all PDP context belonging to the same MS */
void *ipif; /* IP network interface */
- void *peer; /* Pointer to peer protocol */
+ void *peer[2]; /* Pointer to peer protocol */
void *asap; /* Application specific service access point */
uint64_t imsi; /* International Mobile Subscriber Identity. */
@@ -266,8 +267,6 @@ int pdp_ipdel(struct pdp_t *pdp);
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua);
*/
-int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
-int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
#endif /* !_PDP_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 632990c..b6e7aba 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,7 +1,12 @@
noinst_LIBRARIES = libmisc.a
-noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h
+noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
-libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c
+libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c
+
+if ENABLE_GTP_KERNEL
+AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
+libmisc_a_SOURCES += gtp-kernel.c
+endif
diff --git a/lib/gtp-kernel.c b/lib/gtp-kernel.c
new file mode 100644
index 0000000..48811bc
--- /dev/null
+++ b/lib/gtp-kernel.c
@@ -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;
+}
diff --git a/lib/gtp-kernel.h b/lib/gtp-kernel.h
new file mode 100644
index 0000000..464352c
--- /dev/null
+++ b/lib/gtp-kernel.h
@@ -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_ */
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
index 36ad6af..f4bb8a2 100644
--- a/lib/in46_addr.c
+++ b/lib/in46_addr.c
@@ -253,33 +253,66 @@ unsigned int in46a_netmasklen(const struct in46_addr *netmask)
}
}
-/*! Convert given PDP End User Address to in46_addr
- * \returns 0 on success; negative on error */
-int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
+/*! Convert given array of in46_addr to PDP End User Address
+ * \param[in] src Array containing 1 or 2 in46_addr
+ * \param[out] eua End User Address structure to fill
+ * \returns 0 on success; negative on error
+ *
+ * In case size is 2, this function expects to find exactly one IPv4 and one
+ * IPv6 addresses in src. */
+int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua)
{
- switch (src->len) {
- case 4:
- eua->l = 6;
- eua->v[0] = PDP_EUA_ORG_IETF;
- eua->v[1] = PDP_EUA_TYPE_v4;
- memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
- break;
- case 8:
- case 16:
- eua->l = 18;
- eua->v[0] = PDP_EUA_ORG_IETF;
- eua->v[1] = PDP_EUA_TYPE_v6;
- memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
- break;
- default:
- OSMO_ASSERT(0);
- return -1;
+ const struct in46_addr *src_v4, *src_v6;
+ if (size == 1) {
+ switch (src->len) {
+ case 4:
+ eua->l = 6;
+ eua->v[0] = PDP_EUA_ORG_IETF;
+ eua->v[1] = PDP_EUA_TYPE_v4;
+ memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
+ break;
+ case 8:
+ case 16:
+ eua->l = 18;
+ eua->v[0] = PDP_EUA_ORG_IETF;
+ eua->v[1] = PDP_EUA_TYPE_v6;
+ memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ return -1;
+ }
+ return 0;
}
+
+ if (src[0].len == src[1].len)
+ return -1; /* we should have a v4 and a v6 address */
+
+ src_v4 = (src[0].len == 4) ? &src[0] : &src[1];
+ src_v6 = (src[0].len == 4) ? &src[1] : &src[0];
+
+ eua->l = 22;
+ eua->v[0] = PDP_EUA_ORG_IETF;
+ eua->v[1] = PDP_EUA_TYPE_v4v6;
+ memcpy(&eua->v[2], &src_v4->v4, 4);
+ memcpy(&eua->v[6], &src_v6->v6, 16);
+
return 0;
}
-/*! Convert given in46_addr to PDP End User Address
- * \returns 0 on success; negative on error */
+/*! Convert given PDP End User Address to an array of in46_addr
+ * \param[in] eua End User Address structure to parse
+ * \param[out] dst Array containing 2 in46_addr
+ * \returns number of parsed addresses (1 or 2) on success; negative on error
+ *
+ * This function expects to receive an End User Address struct together with an
+ * array of 2 zeroed in46_addr structs. The in46_addr structs are filled in
+ * order, hence if the function returns 1 the parsed address will be stored in
+ * the first struct and the second one will be left intact. If 2 is returned, it
+ * is guaranteed that one of them is an IPv4 and the other one is an IPv6, but
+ * the order in which they are presented is not specified and must be
+ * discovered for instance by checking the len field of each address.
+ */
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
{
if (eua->l < 2)
@@ -295,22 +328,46 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
else
dst->v4.s_addr = 0;
- break;
+ return 1;
case PDP_EUA_TYPE_v6:
dst->len = 16;
if (eua->l >= 18)
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
else
memset(&dst->v6, 0, 16);
- break;
+ return 1;
+ case PDP_EUA_TYPE_v4v6:
+ /* 3GPP TS 29.060, section 7.7.27 */
+ switch (eua->l) {
+ case 2: /* v4 & v6 dynamic */
+ dst[0].v4.s_addr = 0;
+ memset(&dst[1].v6, 0, 16);
+ break;
+ case 6: /* v4 static, v6 dynamic */
+ memcpy(&dst[0].v4, &eua->v[2], 4);
+ memset(&dst[1].v6, 0, 16);
+ break;
+ case 18: /* v4 dynamic, v6 static */
+ dst[0].v4.s_addr = 0;
+ memcpy(&dst[1].v6, &eua->v[2], 16);
+ break;
+ case 22: /* v4 & v6 static */
+ memcpy(&dst[0].v4, &eua->v[2], 4);
+ memcpy(&dst[1].v6, &eua->v[6], 16);
+ break;
+ default:
+ return -1;
+ }
+ dst[0].len = 4;
+ dst[1].len = 16;
+ return 2;
default:
return -1;
}
- return 0;
default_to_dyn_v4:
/* assume dynamic IPv4 by default */
dst->len = 4;
dst->v4.s_addr = 0;
- return 0;
+ return 1;
}
diff --git a/lib/in46_addr.h b/lib/in46_addr.h
index ff26521..e4654cc 100644
--- a/lib/in46_addr.h
+++ b/lib/in46_addr.h
@@ -29,5 +29,5 @@ extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr
extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
unsigned int in46a_netmasklen(const struct in46_addr *netmask);
-int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua);
+int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua);
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
diff --git a/lib/ippool.c b/lib/ippool.c
index 69a7995..1833413 100644
--- a/lib/ippool.c
+++ b/lib/ippool.c
@@ -240,7 +240,7 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
stataddr = stat->addr;
stataddrprefixlen = stat->prefixlen;
- statsize = (1 << (addr.len - stataddrprefixlen + 1)) -1;
+ statsize = (1 << (stataddr.len*8 - stataddrprefixlen));
if (statsize > IPPOOL_STATSIZE)
statsize = IPPOOL_STATSIZE;
}
diff --git a/lib/netdev.c b/lib/netdev.c
new file mode 100644
index 0000000..0011588
--- /dev/null
+++ b/lib/netdev.c
@@ -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;
+}
diff --git a/lib/netdev.h b/lib/netdev.h
new file mode 100644
index 0000000..74c42da
--- /dev/null
+++ b/lib/netdev.h
@@ -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);
diff --git a/lib/tun.c b/lib/tun.c
index 6bcc13b..fa4c37d 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -1,7 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003, 2004 Mondru AB.
- * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -42,8 +43,6 @@
#if defined(__linux__)
#include <linux/if_tun.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
#elif defined (__FreeBSD__)
#include <net/if_tun.h>
@@ -59,531 +58,98 @@
#include "tun.h"
#include "syserr.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
-
-}
+#include "gtp-kernel.h"
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask)
{
- struct ifreq ifr;
- int fd;
-
- memset(&ifr, '\0', sizeof(ifr));
- ifr.ifr_addr.sa_family = AF_INET;
- ifr.ifr_dstaddr.sa_family = AF_INET;
-
-#if defined(__linux__)
- ifr.ifr_netmask.sa_family = AF_INET;
-
-#elif defined(__FreeBSD__) || defined (__APPLE__)
- ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
- sizeof(struct sockaddr_in);
- ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
- sizeof(struct sockaddr_in);
-#endif
-
- strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
-
- /* Create a channel to the NET kernel. */
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
- return -1;
- }
+ int rc;
+ rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
+ if (rc < 0)
+ return rc;
- if (addr) { /* Set the interface address */
+ if (addr)
this->addr.s_addr = addr->s_addr;
- memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
- sizeof(*addr));
- if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
- if (errno != EEXIST) {
- SYS_ERR(DTUN, LOGL_ERROR, errno,
- "ioctl(SIOCSIFADDR) failed");
- } else {
- SYS_ERR(DTUN, LOGL_NOTICE, errno,
- "ioctl(SIOCSIFADDR): Address already exists");
- }
- close(fd);
- return -1;
- }
- }
-
- if (dstaddr) { /* Set the destination address */
+ if (dstaddr)
this->dstaddr.s_addr = dstaddr->s_addr;
- memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
- dstaddr, sizeof(*dstaddr));
- if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno,
- "ioctl(SIOCSIFDSTADDR) failed");
- close(fd);
- return -1;
- }
- }
-
- if (netmask) { /* Set the netmask */
+ if (netmask)
this->netmask.s_addr = netmask->s_addr;
-#if defined(__linux__)
- memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
- netmask, sizeof(*netmask));
-
-#elif defined(__FreeBSD__) || defined (__APPLE__)
- ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
- netmask->s_addr;
-#endif
-
- if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno,
- "ioctl(SIOCSIFNETMASK) failed");
- close(fd);
- return -1;
- }
- }
-
- close(fd);
this->addrs++;
-
- /* On linux the route to the interface is set automatically
- on FreeBSD we have to do this manually */
-
- /* TODO: How does it work on Solaris? */
-
- tun_sifflags(this, IFF_UP | IFF_RUNNING);
-
#if defined(__FreeBSD__) || defined (__APPLE__)
- tun_addroute(this, dstaddr, addr, &this->netmask);
this->routes = 1;
#endif
- return 0;
+ return rc;
}
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
size_t prefixlen)
{
- struct in6_ifreq ifr;
- int fd;
-
- memset(&ifr, 0, sizeof(ifr));
-
-#if defined(__linux__)
- ifr.ifr6_prefixlen = prefixlen;
- ifr.ifr6_ifindex = if_nametoindex(this->devname);
- if (ifr.ifr6_ifindex == 0) {
- SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
- return -1;
- }
-#elif defined(__FreeBSD__) || defined (__APPLE__)
- strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+ int rc;
+ rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
+ if (rc < 0)
+ return rc;
+ if (dstaddr)
+ memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
+ this->addrs++;
+#if defined(__FreeBSD__) || defined (__APPLE__)
+ this->routes = 1;
#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;
- }
- }
+ return rc;
+}
-#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
+static int tun_addaddr4(struct tun_t *this, struct in_addr *addr,
+ struct in_addr *dstaddr, struct in_addr *netmask)
+{
+ int rc;
-#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));
+ /* 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 */
- if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
- close(fd);
- return -1;
- }
-#endif
+ rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
+ if (rc < 0)
+ return rc;
- close(fd);
this->addrs++;
- /* On linux the route to the interface is set automatically
- on FreeBSD we have to do this manually */
+ return rc;
+}
- /* TODO: How does it work on Solaris? */
+static int tun_addaddr6(struct tun_t *this,
+ struct in6_addr *addr,
+ struct in6_addr *dstaddr, int prefixlen)
+{
+ int rc;
- tun_sifflags(this, IFF_UP | IFF_RUNNING);
+ if (!this->addrs) /* Use ioctl for first addr to make ping work */
+ return tun_setaddr6(this, addr, dstaddr, prefixlen);
-#if 0 /* FIXME */
-//#if defined(__FreeBSD__) || defined (__APPLE__)
- tun_addroute6(this, dstaddr, addr, prefixlen);
- this->routes = 1;
-#endif
+ rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
+ if (rc < 0)
+ return rc;
- return 0;
+ this->addrs++;
+
+ return rc;
}
-int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
+int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
{
struct in_addr netmask;
switch (addr->len) {
case 4:
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
- return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
+ return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
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:
return -1;
}
}
-static int tun_route(struct tun_t *this,
- 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)
+int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
{
#if defined(__linux__)
@@ -606,31 +172,50 @@ int tun_new(struct tun_t **tun, const char *dev_name)
(*tun)->routes = 0;
#if defined(__linux__)
- /* Open the actual tun device */
- if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
- goto err_free;
- }
+ if (!use_kernel) {
+ /* Open the actual tun device */
+ if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
+ goto err_free;
+ }
- /* Set device flags. For some weird reason this is also the method
- used to obtain the network interface name */
- memset(&ifr, 0, sizeof(ifr));
- if (dev_name)
- strcpy(ifr.ifr_name, dev_name);
- ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
- if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
- goto err_close;
- }
+ /* Set device flags. For some weird reason this is also the method
+ used to obtain the network interface name */
+ memset(&ifr, 0, sizeof(ifr));
+ if (dev_name)
+ strcpy(ifr.ifr_name, dev_name);
+ ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
+ if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
+ goto err_close;
+ }
- strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
- (*tun)->devname[IFNAMSIZ - 1] = 0;
+ strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
+ (*tun)->devname[IFNAMSIZ - 1] = 0;
- ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
- return 0;
+ ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
+ 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__)
+ if (use_kernel) {
+ LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
+ return -1;
+ }
+
/* Find suitable device */
for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
@@ -682,13 +267,17 @@ int tun_free(struct tun_t *tun)
{
if (tun->routes) {
- tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
+ netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
}
- if (close(tun->fd)) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
+ if (tun->fd >= 0) {
+ if (close(tun->fd)) {
+ SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
+ }
}
+ gtp_kernel_stop(tun->devname);
+
/* TODO: For solaris we need to unlink streams */
free(tun);
@@ -749,8 +338,6 @@ int tun_runscript(struct tun_t *tun, char *script)
return 0;
}
-#include <ifaddrs.h>
-
/*! 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.
@@ -766,58 +353,5 @@ 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)
{
- 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, tun->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;
+ return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
}
diff --git a/lib/tun.h b/lib/tun.h
index 0b960e5..6bf141f 100644
--- a/lib/tun.h
+++ b/lib/tun.h
@@ -1,7 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003 Mondru AB.
- * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -13,6 +13,7 @@
#ifndef _TUN_H
#define _TUN_H
+#include <stdbool.h>
#include <net/if.h>
#include "../lib/in46_addr.h"
@@ -20,43 +21,9 @@
#define PACKET_MAX 8196 /* Maximum packet size we receive */
#define TUN_SCRIPTSIZE 256
#define TUN_ADDRSIZE 128
-#define TUN_NLBUFSIZE 1024
#include "config.h"
-
-/* ipv6 ip type flags for tun_ipv6_local_get() */
-enum {
- IP_TYPE_IPv4 = 1,
- IP_TYPE_IPv6_LINK = 2,
- IP_TYPE_IPv6_NONLINK = 4,
-};
-#define IP_TYPE_IPv6 (IP_TYPE_IPv6_LINK | IP_TYPE_IPv6_NONLINK)
-
-
-#ifndef HAVE_IPHDR
-struct iphdr
- {
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- unsigned int ihl:4;
- unsigned int version:4;
-#elif __BYTE_ORDER == __BIG_ENDIAN
- unsigned int version:4;
- unsigned int ihl:4;
-#else
-# error "Please fix <bits/endian.h>"
-#endif
- u_int8_t tos;
- u_int16_t tot_len;
- u_int16_t id;
- u_int16_t frag_off;
- u_int8_t ttl;
- u_int8_t protocol;
- u_int16_t check;
- u_int32_t saddr;
- u_int32_t daddr;
- /*The options start here. */
- };
-#endif /* !HAVE_IPHDR */
+#include "netdev.h"
/* ***********************************************************
* Information storage for each tun instance
@@ -75,19 +42,13 @@ struct tun_t {
void *priv;
};
-extern int tun_new(struct tun_t **tun, const char *dev_name);
+extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u);
extern int tun_free(struct tun_t *tun);
extern int tun_decaps(struct tun_t *this);
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
-extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
- struct in_addr *dstaddr, struct in_addr *netmask);
-
-extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
- struct in46_addr *his_adr, size_t prefixlen);
-
-int tun_addroute(struct tun_t *this, struct in_addr *dst,
- struct in_addr *gateway, struct in_addr *mask);
+extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
+ struct in46_addr *dstaddr, size_t prefixlen);
extern int tun_set_cb_ind(struct tun_t *this,
int (*cb_ind) (struct tun_t * tun, void *pack,
diff --git a/sgsnemu/Makefile.am b/sgsnemu/Makefile.am
index 4d02eca..9f10cd2 100644
--- a/sgsnemu/Makefile.am
+++ b/sgsnemu/Makefile.am
@@ -5,5 +5,11 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
sgsnemu_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS)
+
+if ENABLE_GTP_KERNEL
+AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
+sgsnemu_LDADD += $(LIBGTPNL_LIBS)
+endif
+
sgsnemu_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h
diff --git a/sgsnemu/cmdline.c b/sgsnemu/cmdline.c
index 1c02baa..b062533 100644
--- a/sgsnemu/cmdline.c
+++ b/sgsnemu/cmdline.c
@@ -41,7 +41,7 @@ const char *gengetopt_args_info_help[] = {
" --pidfile=STRING Filename of process id file\n (default=`./sgsnemu.pid')",
" --statedir=STRING Directory of nonvolatile data (default=`./')",
" --dns=STRING DNS Server to use",
- " -l, --listen=STRING Local interface",
+ " -l, --listen=STRING Local host",
" -r, --remote=STRING Remote host",
" --contexts=INT Number of contexts (default=`1')",
" --timelimit=INT Exit after timelimit seconds (default=`0')",
@@ -65,12 +65,14 @@ const char *gengetopt_args_info_help[] = {
" --charging=INT Charging characteristics (default=`0x0800')",
" -u, --uid=STRING Login user ID (default=`mig')",
" -p, --pwd=STRING Login password (default=`hemmelig')",
+ "\n Mode: createif\n any option of this mode is related to tun interface, all payload going in and\n out via tunN interface",
" --createif Create local network interface (default=off)",
" -n, --net=STRING Network address for local interface",
" --defaultroute Create default route (default=off)",
" --ipup=STRING Script to run after link-up",
" --ipdown=STRING Script to run after link-down",
" --tun-device=STRING Name of the local network interface",
+ "\n Mode: pinghost\n generate ICMP payload inside G-PDU without setting up tun interface",
" --pinghost=STRING Ping remote host",
" --pingrate=INT Number of ping req per second (default=`1')",
" --pingsize=INT Number of ping data bytes (default=`56')",
@@ -168,6 +170,8 @@ void clear_given(struct gengetopt_args_info *args_info)
args_info->pingquiet_given = 0;
args_info->no_tx_gpdu_seq_given = 0;
args_info->pdp_type_given = 0;
+ args_info->createif_mode_counter = 0;
+ args_info->pinghost_mode_counter = 0;
}
static
@@ -290,19 +294,19 @@ void init_args_info(struct gengetopt_args_info *args_info)
args_info->charging_help = gengetopt_args_info_help[28];
args_info->uid_help = gengetopt_args_info_help[29];
args_info->pwd_help = gengetopt_args_info_help[30];
- args_info->createif_help = gengetopt_args_info_help[31];
- args_info->net_help = gengetopt_args_info_help[32];
- args_info->defaultroute_help = gengetopt_args_info_help[33];
- args_info->ipup_help = gengetopt_args_info_help[34];
- args_info->ipdown_help = gengetopt_args_info_help[35];
- args_info->tun_device_help = gengetopt_args_info_help[36];
- args_info->pinghost_help = gengetopt_args_info_help[37];
- args_info->pingrate_help = gengetopt_args_info_help[38];
- args_info->pingsize_help = gengetopt_args_info_help[39];
- args_info->pingcount_help = gengetopt_args_info_help[40];
- args_info->pingquiet_help = gengetopt_args_info_help[41];
- args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[42];
- args_info->pdp_type_help = gengetopt_args_info_help[43];
+ args_info->createif_help = gengetopt_args_info_help[32];
+ args_info->net_help = gengetopt_args_info_help[33];
+ args_info->defaultroute_help = gengetopt_args_info_help[34];
+ args_info->ipup_help = gengetopt_args_info_help[35];
+ args_info->ipdown_help = gengetopt_args_info_help[36];
+ args_info->tun_device_help = gengetopt_args_info_help[37];
+ args_info->pinghost_help = gengetopt_args_info_help[39];
+ args_info->pingrate_help = gengetopt_args_info_help[40];
+ args_info->pingsize_help = gengetopt_args_info_help[41];
+ args_info->pingcount_help = gengetopt_args_info_help[42];
+ args_info->pingquiet_help = gengetopt_args_info_help[43];
+ args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[44];
+ args_info->pdp_type_help = gengetopt_args_info_help[45];
}
@@ -361,8 +365,7 @@ void cmdline_parser_params_init(struct cmdline_parser_params *params)
struct cmdline_parser_params *cmdline_parser_params_create(void)
{
- struct cmdline_parser_params *params =
- (struct cmdline_parser_params *)
+ struct cmdline_parser_params *params = (struct cmdline_parser_params *)
malloc(sizeof(struct cmdline_parser_params));
cmdline_parser_params_init(params);
return params;
@@ -853,6 +856,30 @@ int update_arg(void *field, char **orig_field,
return 0; /* OK */
}
+static int check_modes(int given1[], const char *options1[],
+ int given2[], const char *options2[])
+{
+ int i = 0, j = 0, errors = 0;
+
+ while (given1[i] >= 0) {
+ if (given1[i]) {
+ while (given2[j] >= 0) {
+ if (given2[j]) {
+ ++errors;
+ fprintf(stderr,
+ "%s: option %s conflicts with option %s\n",
+ package_name, options1[i],
+ options2[j]);
+ }
+ ++j;
+ }
+ }
+ ++i;
+ }
+
+ return errors;
+}
+
int
cmdline_parser_internal(int argc, char **argv,
struct gengetopt_args_info *args_info,
@@ -976,7 +1003,7 @@ cmdline_parser_internal(int argc, char **argv,
goto failure;
break;
- case 'l': /* Local interface. */
+ case 'l': /* Local host. */
if (update_arg((void *)&(args_info->listen_arg),
&(args_info->listen_orig),
@@ -1073,6 +1100,7 @@ cmdline_parser_internal(int argc, char **argv,
break;
case 'n': /* Network address for local interface. */
+ args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->net_arg),
&(args_info->net_orig),
@@ -1391,6 +1419,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"createif") == 0) {
+ args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->createif_flag), 0,
@@ -1405,6 +1434,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"defaultroute") == 0) {
+ args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->defaultroute_flag), 0,
@@ -1419,6 +1449,7 @@ cmdline_parser_internal(int argc, char **argv,
/* Script to run after link-up. */
else if (strcmp(long_options[option_index].name, "ipup")
== 0) {
+ args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->ipup_arg),
&(args_info->ipup_orig),
@@ -1434,6 +1465,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"ipdown") == 0) {
+ args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->ipdown_arg),
&(args_info->ipdown_orig),
@@ -1449,6 +1481,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"tun-device") == 0) {
+ args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->tun_device_arg),
@@ -1465,6 +1498,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pinghost") == 0) {
+ args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pinghost_arg),
@@ -1481,6 +1515,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingrate") == 0) {
+ args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingrate_arg),
@@ -1496,6 +1531,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingsize") == 0) {
+ args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingsize_arg),
@@ -1512,6 +1548,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingcount") == 0) {
+ args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingcount_arg),
@@ -1527,6 +1564,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingquiet") == 0) {
+ args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingquiet_flag), 0,
@@ -1566,6 +1604,31 @@ cmdline_parser_internal(int argc, char **argv,
} /* switch */
} /* while */
+ if (args_info->createif_mode_counter
+ && args_info->pinghost_mode_counter) {
+ int createif_given[] =
+ { args_info->createif_given, args_info->net_given,
+ args_info->defaultroute_given, args_info->ipup_given,
+ args_info->ipdown_given, args_info->tun_device_given, -1
+ };
+ const char *createif_desc[] =
+ { "--createif", "--net", "--defaultroute", "--ipup",
+ "--ipdown", "--tun-device", 0
+ };
+ int pinghost_given[] =
+ { args_info->pinghost_given, args_info->pingrate_given,
+ args_info->pingsize_given, args_info->pingcount_given,
+ args_info->pingquiet_given, -1
+ };
+ const char *pinghost_desc[] =
+ { "--pinghost", "--pingrate", "--pingsize", "--pingcount",
+ "--pingquiet", 0
+ };
+ error_occurred +=
+ check_modes(createif_given, createif_desc, pinghost_given,
+ pinghost_desc);
+ }
+
if (check_required) {
error_occurred +=
cmdline_parser_required2(args_info, argv[0],
diff --git a/sgsnemu/cmdline.ggo b/sgsnemu/cmdline.ggo
index 8136d3a..0f415f5 100644
--- a/sgsnemu/cmdline.ggo
+++ b/sgsnemu/cmdline.ggo
@@ -15,6 +15,10 @@
package "sgsnemu"
+defmode "createif" modedesc="any option of this mode is related to tun interface, \
+all payload going in and out via tunN interface"
+defmode "pinghost" modedesc="generate ICMP payload inside G-PDU without setting up tun interface"
+
option "debug" d "Run in debug mode" flag off
option "conf" c "Read configuration file" string no
@@ -22,7 +26,7 @@ option "pidfile" - "Filename of process id file" string default="./sgsn
option "statedir" - "Directory of nonvolatile data" string default="./" no
option "dns" - "DNS Server to use" string no
-option "listen" l "Local interface" string no
+option "listen" l "Local host" string no
option "remote" r "Remote host" string no
option "contexts" - "Number of contexts" int default="1" no
@@ -49,18 +53,18 @@ option "charging" - "Charging characteristics" int default="0x0800
option "uid" u "Login user ID" string default="mig" no
option "pwd" p "Login password" string default="hemmelig" no
-option "createif" - "Create local network interface" flag off
-option "net" n "Network address for local interface" string dependon="createif" no
-option "defaultroute" - "Create default route" flag dependon="createif" off
-option "ipup" - "Script to run after link-up" string dependon="createif" no
-option "ipdown" - "Script to run after link-down" string dependon="createif" no
-option "tun-device" - "Name of the local network interface" string dependon="createif" no
+modeoption "createif" - "Create local network interface" flag off mode="createif"
+modeoption "net" n "Network address for local interface" string dependon="createif" no mode="createif"
+modeoption "defaultroute" - "Create default route" flag dependon="createif" off mode="createif"
+modeoption "ipup" - "Script to run after link-up" string dependon="createif" no mode="createif"
+modeoption "ipdown" - "Script to run after link-down" string dependon="createif" no mode="createif"
+modeoption "tun-device" - "Name of the local network interface" string dependon="createif" no mode="createif"
-option "pinghost" - "Ping remote host" string no
-option "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no
-option "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no
-option "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no
-option "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" off
+modeoption "pinghost" - "Ping remote host" string no mode="pinghost"
+modeoption "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no mode="pinghost"
+modeoption "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no mode="pinghost"
+modeoption "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no mode="pinghost"
+modeoption "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" off mode="pinghost"
option "no-tx-gpdu-seq" - "Don't transmit G-PDU sequence nums" flag off
option "pdp-type" t "PDP Type" string default="v4" no typestr="(v4|v6)"
diff --git a/sgsnemu/cmdline.h b/sgsnemu/cmdline.h
index 9dc210e..24f772b 100644
--- a/sgsnemu/cmdline.h
+++ b/sgsnemu/cmdline.h
@@ -69,11 +69,11 @@ extern "C" {
const char *dns_help;
/**< @brief DNS Server to use help description. */
char *listen_arg;
- /**< @brief Local interface. */
+ /**< @brief Local host. */
char *listen_orig;
- /**< @brief Local interface original value given at command line. */
+ /**< @brief Local host original value given at command line. */
const char *listen_help;
- /**< @brief Local interface help description. */
+ /**< @brief Local host help description. */
char *remote_arg;
/**< @brief Remote host. */
char *remote_orig;
@@ -370,6 +370,10 @@ extern "C" {
unsigned int pdp_type_given;
/**< @brief Whether pdp-type was given. */
+ int createif_mode_counter;
+ /**< @brief Counter for mode createif */
+ int pinghost_mode_counter;
+ /**< @brief Counter for mode pinghost */
};
/** @brief The additional parameters to pass to parser functions */
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 3d86dc8..0382705 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -1,13 +1,13 @@
-/*
+/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017 Harald Welte <laforge@gnumonks.org>
- *
+ *
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
- *
+ *
*/
/*
@@ -20,6 +20,7 @@
#endif
#include <osmocom/core/application.h>
+#include <osmocom/core/msgb.h>
#include <ctype.h>
#include <netdb.h>
@@ -79,13 +80,14 @@ struct gsn_t *gsn = NULL; /* GSN instance */
struct tun_t *tun = NULL; /* TUN instance */
int maxfd = 0; /* For select() */
int echoversion = 1; /* First try this version */
+void *tall_sgsnemu_ctx; /* root talloc ctx */
/* Struct with local versions of gengetopt options */
struct {
int debug; /* Print debug messages */
int createif; /* Create local network interface */
char *tun_dev_name;
- struct in_addr netaddr, destaddr, net; /* Network interface */
+ struct in46_addr netaddr, destaddr, net; /* Network interface */
size_t prefixlen;
char *ipup, *ipdown; /* Filename of scripts */
int defaultroute; /* Set up default route */
@@ -349,7 +351,7 @@ static int process_options(int argc, char **argv)
printf("timelimit: %d\n", args_info.timelimit_arg);
printf("createif: %d\n", args_info.createif_flag);
if (args_info.tun_device_arg)
- printf("tun-device: %d\n", args_info.tun_device_arg);
+ printf("tun-device: %s\n", args_info.tun_device_arg);
if (args_info.ipup_arg)
printf("ipup: %s\n", args_info.ipup_arg);
if (args_info.ipdown_arg)
@@ -372,10 +374,10 @@ static int process_options(int argc, char **argv)
/* foreground */
/* If fg flag not given run as a daemon */
- /* Do not allow sgsnemu to run as deamon
+ /* Do not allow sgsnemu to run as deamon
if (!args_info.fg_flag)
{
- closelog();
+ closelog();
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
freopen("/dev/null", "r", stdin);
@@ -873,23 +875,21 @@ static int process_options(int argc, char **argv)
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
- struct in46_addr in46;
if (ippool_aton
- (&in46, &options.prefixlen, args_info.net_arg, 0)) {
+ (&options.net, &options.prefixlen, args_info.net_arg, 0)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
"Invalid network address: %s!",
args_info.net_arg);
exit(1);
}
- options.net.s_addr = in46.v4.s_addr;
- options.netaddr.s_addr = options.net.s_addr;
- options.destaddr.s_addr = options.net.s_addr;
+ options.netaddr = options.net;
+ options.destaddr = options.net;
} else {
- options.net.s_addr = 0;
+ memset(&options.net, 0, sizeof(options.net));
options.prefixlen = 0;
- options.netaddr.s_addr = 0;
- options.destaddr.s_addr = 0;
+ memset(&options.netaddr, 0, sizeof(options.netaddr));
+ memset(&options.destaddr, 0, sizeof(options.destaddr));
}
/* ipup */
@@ -974,7 +974,6 @@ static char *proc_read(const char *path)
ret = NULL;
goto out;
}
- return ret;
out:
fclose(f);
@@ -986,7 +985,7 @@ out:
static char *proc_ipv6_conf_read(const char *dev, const char *file)
{
const char *fmt = "/proc/sys/net/ipv6/conf/%s/%s";
- char path[strlen(fmt) + strlen(dev) + strlen(file)];
+ char path[strlen(fmt) + strlen(dev) + strlen(file)+1];
snprintf(path, sizeof(path), fmt, dev, file);
return proc_read(path);
}
@@ -1315,8 +1314,8 @@ static int delete_context(struct pdp_t *pdp)
if (tun && options.ipdown)
tun_runscript(tun, options.ipdown);
- ipdel((struct iphash_t *)pdp->peer);
- memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */
+ ipdel((struct iphash_t *)pdp->peer[0]);
+ memset(pdp->peer[0], 0, sizeof(struct iphash_t)); /* To be sure */
if (1 == options.contexts)
state = 5; /* Disconnected */
@@ -1403,7 +1402,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
return EOF; /* Not what we expected */
}
- if (in46a_from_eua(&pdp->eua, &addr)) {
+ if (in46a_from_eua(&pdp->eua, &addr) < 1) {
printf
("Received create PDP context response. Cause value: %d\n",
cause);
@@ -1428,17 +1427,16 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
break;
}
- if ((options.createif) && (!options.net.s_addr)) {
+ if ((options.createif) && (!options.net.len)) {
size_t prefixlen = 32;
if (addr.len == 16)
prefixlen = 64;
/* printf("Setting up interface and routing\n"); */
- /* FIXME: use tun_addattr() not tun_setaddr() */
- tun_setaddr(tun, &addr, &addr, prefixlen);
+ tun_addaddr(tun, &addr, &addr, prefixlen);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
- tun_addroute(tun, &rm, &addr.v4, &rm);
+ netdev_addroute(&rm, &addr.v4, &rm);
}
if (options.ipup)
tun_runscript(tun, options.ipup);
@@ -1460,12 +1458,12 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
"router advertisements; SLAAC will not succeed, please "
"fix your setup!\n");
}
- free(accept_ra);
- free(forwarding);
}
+ free(accept_ra);
+ free(forwarding);
}
- ipset((struct iphash_t *)pdp->peer, &addr);
+ ipset(iph, &addr);
state = 2; /* Connected */
@@ -1543,7 +1541,9 @@ int main(int argc, char **argv)
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
- osmo_init_logging(&log_info);
+ tall_sgsnemu_ctx = talloc_named_const(NULL, 0, "sgsnemu");
+ msgb_talloc_ctx_init(tall_sgsnemu_ctx, 0);
+ osmo_init_logging2(tall_sgsnemu_ctx, &log_info);
/* Process options given in configuration file and command line */
if (process_options(argc, argv))
@@ -1571,7 +1571,7 @@ int main(int argc, char **argv)
if (options.createif) {
printf("Setting up interface\n");
/* Create a tunnel interface */
- if (tun_new((struct tun_t **)&tun, options.tun_dev_name)) {
+ if (tun_new((struct tun_t **)&tun, options.tun_dev_name, false, -1, -1)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
"Failed to create tun");
exit(1);
@@ -1581,15 +1581,13 @@ int main(int argc, char **argv)
maxfd = tun->fd;
}
- if ((options.createif) && (options.net.s_addr)) {
- struct in_addr mask;
- mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
+ if ((options.createif) && (options.net.len)) {
/* printf("Setting up interface and routing\n"); */
- tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
+ tun_addaddr(tun, &options.netaddr, &options.destaddr, options.prefixlen);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
- tun_addroute(tun, &rm, &options.destaddr, &rm);
+ netdev_addroute(&rm, &options.destaddr.v4, &rm);
}
if (options.ipup)
tun_runscript(tun, options.ipup);
@@ -1618,7 +1616,7 @@ int main(int argc, char **argv)
/* Otherwise it is deallocated by gtplib */
pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
- pdp->peer = &iparr[n];
+ pdp->peer[0] = &iparr[n]; /* FIXME: support v4v6, have 2 peers */
pdp->ipif = tun; /* TODO */
iparr[n].pdp = pdp;
@@ -1701,7 +1699,7 @@ int main(int argc, char **argv)
pdp->hisaddr0 = options.remote;
pdp->hisaddr1 = options.remote;
- pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
+ pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
512 = Flat rate, 256 = Hot billing */
pdp->tx_gpdu_seq = options.tx_gpdu_seq;
diff --git a/src/Makefile.in b/src/Makefile.in
deleted file mode 100644
index e69de29..0000000
--- a/src/Makefile.in
+++ /dev/null
diff --git a/tests/gtp/gtpie_test.c b/tests/gtp/gtpie_test.c
index fe2d502..95dd900 100644
--- a/tests/gtp/gtpie_test.c
+++ b/tests/gtp/gtpie_test.c
@@ -7,6 +7,8 @@
#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"
@@ -27,7 +29,7 @@ static void test_gtpie_tlv()
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == sizeof(in) + 3);
OSMO_ASSERT(buf[0] == 23);
- OSMO_ASSERT(ntohs(*(uint16_t *) &buf[1]) == sizeof(in));
+ OSMO_ASSERT(osmo_load16be(&buf[1]) == sizeof(in));
OSMO_ASSERT(!memcmp(buf+3, in, sizeof(in)));
/* overflow */
@@ -73,7 +75,7 @@ static void test_gtpie_tv2()
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 3);
OSMO_ASSERT(buf[0] == 42);
- OSMO_ASSERT(ntohs(*(uint16_t *) &buf[1]) == 0xABCD);
+ OSMO_ASSERT(osmo_load16be(&buf[1]) == 0xABCD);
}
static void test_gtpie_tv4()
@@ -87,7 +89,7 @@ static void test_gtpie_tv4()
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 5);
OSMO_ASSERT(buf[0] == 42);
- OSMO_ASSERT(ntohl(*(uint32_t *) &buf[1]) == 0xABCD0123);
+ OSMO_ASSERT(osmo_load32be(&buf[1]) == 0xABCD0123);
}
static void test_gtpie_tv8()
@@ -101,13 +103,15 @@ static void test_gtpie_tv8()
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 9);
OSMO_ASSERT(buf[0] == 42);
- OSMO_ASSERT(ntohl(*(uint32_t *) &buf[1]) == 0x00010203);
- OSMO_ASSERT(ntohl(*(uint32_t *) &buf[5]) == 0x04050607);
+ OSMO_ASSERT(osmo_load32be(&buf[1]) == 0x00010203);
+ OSMO_ASSERT(osmo_load32be(&buf[5]) == 0x04050607);
}
int main(int argc, char **argv)
{
- osmo_init_logging(&log_info);
+ void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
+ msgb_talloc_ctx_init(tall_ctx, 0);
+ osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
@@ -124,4 +128,5 @@ int main(int argc, char **argv)
/* TODO: gtpie_encaps() */
/* TODO: gtpie_encaps2() */
/* TODO: gtpie_getie(), gtpie_exist(), gtpie_get*() */
+ return 0;
}
diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am
index 95d6901..ee46468 100644
--- a/tests/lib/Makefile.am
+++ b/tests/lib/Makefile.am
@@ -1,7 +1,11 @@
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
-EXTRA_DIST = ippool_test.ok ippool_test.err \
- in46a_test.ok
+EXTRA_DIST = ippool_test.ok \
+ ippool_test.err \
+ ippool_v6_test.ok \
+ ippool_v6_test.err \
+ in46a_test.ok \
+ in46a_v6_test.ok
noinst_PROGRAMS = ippool_test in46a_test
diff --git a/tests/lib/in46a_test.c b/tests/lib/in46a_test.c
index 42a1768..407ac5d 100644
--- a/tests/lib/in46a_test.c
+++ b/tests/lib/in46a_test.c
@@ -10,6 +10,8 @@
#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"
@@ -19,41 +21,24 @@ static const struct in46_addr g_ia4 = {
.v4.s_addr = 0x0d0c0b0a,
};
-static const struct in46_addr g_ia6 = {
- .len = 16,
- .v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
-};
-
static void test_in46a_to_af(void)
{
- struct in46_addr ia;
-
- printf("Testing in46a_to_af()\n");
+ printf("Testing in46a_to_af() with IPv4 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
- OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
-
- ia.len = 8;
- OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
}
static void test_in46a_to_sas(void)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
- printf("Testing in46a_to_sas()\n");
+ printf("Testing in46a_to_sas() with IPv4 addresses\n");
memset(&ss, 0, sizeof(ss));
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia4) == 0);
OSMO_ASSERT(sin->sin_family == AF_INET);
OSMO_ASSERT(sin->sin_addr.s_addr == g_ia4.v4.s_addr);
-
- memset(&ss, 0, sizeof(ss));
- OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
- OSMO_ASSERT(sin6->sin6_family == AF_INET6);
- OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
}
static void test_in46a_ntop(void)
@@ -62,7 +47,7 @@ static void test_in46a_ntop(void)
char buf[256];
const char *res;
- printf("Testing in46a_ntop()\n");
+ printf("Testing in46a_ntop() with IPv4 addresses\n");
res = in46a_ntop(NULL, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
@@ -78,10 +63,6 @@ static void test_in46a_ntop(void)
res = in46a_ntop(&ia, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
printf("res = %s\n", res);
-
- res = in46a_ntop(&g_ia6, buf, sizeof(buf));
- OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
- printf("res = %s\n", res);
}
static void test_in46p_ntoa(void)
@@ -100,21 +81,14 @@ static void test_in46a_equal(void)
{
struct in46_addr b;
- printf("Testing in46a_equal()\n");
+ printf("Testing in46a_equal() with IPv4 addresses\n");
memset(&b, 0xff, sizeof(b));
b.len = g_ia4.len;
b.v4.s_addr = g_ia4.v4.s_addr;
OSMO_ASSERT(in46a_equal(&g_ia4, &b));
-
- memset(&b, 0xff, sizeof(b));
- b.len = g_ia6.len;
- b.v6 = g_ia6.v6;
- OSMO_ASSERT(in46a_equal(&g_ia6, &b));
-
}
-
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
size_t prefixlen)
{
@@ -133,7 +107,7 @@ static void test_in46a_within_mask(void)
{
struct in46_addr addr, mask;
- printf("Testing in46a_within_mask()\n");
+ printf("Testing in46a_within_mask() with IPv4 addresses\n");
addr = g_ia4;
mask = g_ia4;
@@ -154,13 +128,9 @@ static void test_in46a_within_mask(void)
static void test_in46a_to_eua(void)
{
- const struct in46_addr ia_v6_8 = {
- .len = 8,
- .v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
- };
struct ul66_t eua;
- printf("testing in46a_to_eua()\n");
+ printf("testing in46a_to_eua() with IPv4 addresses\n");
#if 0 /* triggers assert in current implementation */
const struct in46_addr ia_invalid = { .len = 3, };
@@ -168,22 +138,10 @@ static void test_in46a_to_eua(void)
#endif
/* IPv4 address */
- OSMO_ASSERT(in46a_to_eua(&g_ia4, &eua) == 0);
+ OSMO_ASSERT(in46a_to_eua(&g_ia4, 1, &eua) == 0);
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
- OSMO_ASSERT(*(uint32_t *) &eua.v[2] == g_ia4.v4.s_addr);
-
- /* IPv6 address */
- OSMO_ASSERT(in46a_to_eua(&g_ia6, &eua) == 0);
- OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
- OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
- OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
-
- /* IPv6 address with prefix / length 8 */
- OSMO_ASSERT(in46a_to_eua(&ia_v6_8, &eua) == 0);
- OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
- OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
- OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
+ OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
}
static void test_in46a_from_eua(void)
@@ -192,16 +150,12 @@ static void test_in46a_from_eua(void)
struct ul66_t eua;
const uint8_t v4_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4 };
const uint8_t v4_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4, 1,2,3,4 };
- const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
- const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
- 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
-
memset(&eua, 0, sizeof(eua));
- printf("Testing in46a_from_eua()\n");
+ printf("Testing in46a_from_eua() with IPv4 addresses\n");
/* default: v4 unspec */
- OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+ OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == 0);
@@ -220,30 +174,16 @@ static void test_in46a_from_eua(void)
/* unspecified V4 */
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
eua.l = sizeof(v4_unspec);
- OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+ OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == 0);
/* specified V4 */
memcpy(eua.v, v4_spec, sizeof(v4_spec));
eua.l = sizeof(v4_spec);
- OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+ OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
-
- /* unspecified V6 */
- memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
- eua.l = sizeof(v6_unspec);
- OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
- OSMO_ASSERT(ia.len == 16);
- OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
-
- /* specified V6 */
- memcpy(eua.v, v6_spec, sizeof(v6_spec));
- eua.l = sizeof(v6_spec);
- OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
- OSMO_ASSERT(ia.len == 16);
- OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
}
static void test_in46a_netmasklen(void)
@@ -273,7 +213,188 @@ static void test_in46a_netmasklen(void)
netmask.v4.s_addr = 0x00000000;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 0);
+}
+
+/* IPv6 specific tests */
+
+static const struct in46_addr g_ia6 = {
+ .len = 16,
+ .v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
+};
+
+static void test_in46a_to_af_v6(void)
+{
+ struct in46_addr ia;
+
+ printf("Testing in46a_to_af() with IPv6 addresses\n");
+
+ OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
+
+ ia.len = 8;
+ OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
+}
+
+static void test_in46a_to_sas_v6(void)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+
+ printf("Testing in46a_to_sas() with IPv6 addresses\n");
+
+ memset(&ss, 0, sizeof(ss));
+ OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
+ OSMO_ASSERT(sin6->sin6_family == AF_INET6);
+ OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
+}
+
+static void test_in46a_ntop_v6(void)
+{
+ char buf[256];
+ const char *res;
+
+ printf("Testing in46a_ntop() with IPv6 addresses\n");
+
+ res = in46a_ntop(&g_ia6, buf, sizeof(buf));
+ OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
+ printf("res = %s\n", res);
+}
+
+static void test_in46a_equal_v6(void)
+{
+ struct in46_addr b;
+
+ printf("Testing in46a_equal() with IPv6 addresses\n");
+
+ memset(&b, 0xff, sizeof(b));
+ b.len = g_ia6.len;
+ b.v6 = g_ia6.v6;
+ OSMO_ASSERT(in46a_equal(&g_ia6, &b));
+}
+
+static void test_in46a_to_eua_v6(void)
+{
+ const struct in46_addr ia_v6_8 = {
+ .len = 8,
+ .v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
+ };
+ struct ul66_t eua;
+
+ printf("Testing in46a_to_eua() with IPv6 addresses\n");
+
+ /* IPv6 address */
+ OSMO_ASSERT(in46a_to_eua(&g_ia6, 1, &eua) == 0);
+ OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
+ OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
+ OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
+
+ /* IPv6 address with prefix / length 8 */
+ OSMO_ASSERT(in46a_to_eua(&ia_v6_8, 1, &eua) == 0);
+ OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
+ OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
+ OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
+}
+static void test_in46a_to_eua_v4v6() {
+ const struct in46_addr ia_v4v6[2] = {
+ {
+ .len = 16,
+ .v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
+ },
+ {
+ .len = 4,
+ .v4.s_addr = 0x0d0c0b0a,
+ }
+ };
+ struct ul66_t eua;
+ printf("Testing in46a_to_eua() with IPv4v6 addresses\n");
+
+ /* IPv4 address */
+ OSMO_ASSERT(in46a_to_eua(ia_v4v6, 2, &eua) == 0);
+ OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
+ OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4v6);
+ OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
+ OSMO_ASSERT(!memcmp(&eua.v[6], &g_ia6.v6, 16));
+}
+
+static void test_in46a_from_eua_v6(void)
+{
+ struct in46_addr ia;
+ struct ul66_t eua;
+ const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
+ const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
+ 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
+
+ memset(&eua, 0, sizeof(eua));
+
+ printf("Testing in46a_from_eua() with IPv6 addresses\n");
+
+ /* unspecified V6 */
+ memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
+ eua.l = sizeof(v6_unspec);
+ OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
+ OSMO_ASSERT(ia.len == 16);
+ OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
+
+ /* specified V6 */
+ memcpy(eua.v, v6_spec, sizeof(v6_spec));
+ eua.l = sizeof(v6_spec);
+ OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
+ OSMO_ASSERT(ia.len == 16);
+ OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
+}
+
+static void test_in46a_from_eua_v4v6(void) {
+ struct in46_addr ia[2];
+ struct ul66_t eua;
+ const uint8_t v4_unspec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6 };
+ const uint8_t v4_spec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4 };
+ const uint8_t v4_unspec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
+ const uint8_t v4_spec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
+
+ memset(&eua, 0, sizeof(eua));
+
+ printf("Testing in46a_from_eua() with IPv4v6 addresses\n");
+
+ /* unspecified V4 & V6 */
+ memcpy(eua.v, v4_unspec_v6_unspec, sizeof(v4_unspec_v6_unspec));
+ eua.l = sizeof(v4_unspec_v6_unspec);
+ OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+ OSMO_ASSERT(ia[0].len == 4);
+ OSMO_ASSERT(ia[1].len == 16);
+ OSMO_ASSERT(ia[0].v4.s_addr == 0);
+ OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
+
+ /* specified V4, unspecified V6 */
+ memcpy(eua.v, v4_spec_v6_unspec, sizeof(v4_spec_v6_unspec));
+ eua.l = sizeof(v4_spec_v6_unspec);
+ OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+ OSMO_ASSERT(ia[0].len == 4);
+ OSMO_ASSERT(ia[1].len == 16);
+ OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
+ OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
+
+ /* unspecified V4, specified V6 */
+ memcpy(eua.v, v4_unspec_v6_spec, sizeof(v4_unspec_v6_spec));
+ eua.l = sizeof(v4_unspec_v6_spec);
+ OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+ OSMO_ASSERT(ia[0].len == 4);
+ OSMO_ASSERT(ia[1].len == 16);
+ OSMO_ASSERT(ia[0].v4.s_addr == 0);
+ OSMO_ASSERT(!memcmp(&ia[1].v6, v4_unspec_v6_spec+2, ia[1].len));
+
+ /* specified V4, specified V6 */
+ memcpy(eua.v, v4_spec_v6_spec, sizeof(v4_spec_v6_spec));
+ eua.l = sizeof(v4_spec_v6_spec);
+ OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+ OSMO_ASSERT(ia[0].len == 4);
+ OSMO_ASSERT(ia[1].len == 16);
+ OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
+ OSMO_ASSERT(!memcmp(&ia[1].v6, v4_spec_v6_spec+6, ia[1].len));
+}
+
+static void test_in46a_netmasklen_v6(void)
+{
+ unsigned int len;
printf("Testing in46a_netmasklen() with IPv6 addresses\n");
const struct in46_addr netmaskA = {
.len = 16,
@@ -306,19 +427,34 @@ static void test_in46a_netmasklen(void)
int main(int argc, char **argv)
{
- osmo_init_logging(&log_info);
+ void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
+ msgb_talloc_ctx_init(tall_ctx, 0);
+ osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));
- test_in46a_to_af();
- test_in46a_to_sas();
- test_in46a_ntop();
- test_in46p_ntoa();
- test_in46a_equal();
- test_in46a_within_mask();
- test_in46a_to_eua();
- test_in46a_from_eua();
- test_in46a_netmasklen();
+ if (argc < 2 || strcmp(argv[1], "-v6")) {
+ test_in46a_to_af();
+ test_in46a_to_sas();
+ test_in46a_ntop();
+ test_in46p_ntoa();
+ test_in46a_equal();
+ test_in46a_within_mask();
+ test_in46a_to_eua();
+ test_in46a_from_eua();
+ test_in46a_netmasklen();
+ } else {
+ test_in46a_to_af_v6();
+ test_in46a_to_sas_v6();
+ test_in46a_ntop_v6();
+ test_in46a_equal_v6();
+ test_in46a_to_eua_v6();
+ test_in46a_from_eua_v6();
+ test_in46a_to_eua_v4v6();
+ test_in46a_from_eua_v4v6();
+ test_in46a_netmasklen_v6();
+ }
+ return 0;
}
diff --git a/tests/lib/in46a_test.ok b/tests/lib/in46a_test.ok
index 9a0ff7a..b497ad1 100644
--- a/tests/lib/in46a_test.ok
+++ b/tests/lib/in46a_test.ok
@@ -1,19 +1,17 @@
-Testing in46a_to_af()
-Testing in46a_to_sas()
-Testing in46a_ntop()
+Testing in46a_to_af() with IPv4 addresses
+Testing in46a_to_sas() with IPv4 addresses
+Testing in46a_ntop() with IPv4 addresses
res = UNDEFINED
res = UNDEFINED
res = 1.2.3.4
-res = 102:304:506:708:90a:b0c:d0e:f10
in46p_ntoa() returns 16.32.48.0/24
-Testing in46a_equal()
-Testing in46a_within_mask()
+Testing in46a_equal() with IPv4 addresses
+Testing in46a_within_mask() with IPv4 addresses
in46a_within_mask(10.11.12.13, 10.11.12.13, 32) = 1
in46a_within_mask(10.11.12.13, 10.11.12.12, 30) = 1
in46a_within_mask(10.11.12.13, 10.8.0.0, 13) = 1
in46a_within_mask(10.11.12.14, 10.11.12.13, 32) = 0
in46a_within_mask(10.11.12.14, 10.11.12.12, 30) = 1
-testing in46a_to_eua()
-Testing in46a_from_eua()
+testing in46a_to_eua() with IPv4 addresses
+Testing in46a_from_eua() with IPv4 addresses
Testing in46a_netmasklen() with IPv4 addresses
-Testing in46a_netmasklen() with IPv6 addresses
diff --git a/tests/lib/in46a_v6_test.ok b/tests/lib/in46a_v6_test.ok
new file mode 100644
index 0000000..10dc7f4
--- /dev/null
+++ b/tests/lib/in46a_v6_test.ok
@@ -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
diff --git a/tests/lib/ippool_test.c b/tests/lib/ippool_test.c
index ea56edd..6155ab8 100644
--- a/tests/lib/ippool_test.c
+++ b/tests/lib/ippool_test.c
@@ -7,6 +7,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
#include "../../lib/in46_addr.h"
#include "../../lib/ippool.h"
@@ -113,21 +114,31 @@ static void test_pool_sizes(void)
/* 65534 addresses [0.1..255.254] */
test_pool_size("192.168.0.0/16", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 65534);
- /* 256 prefixes of /64 each */
- test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
-
/* 253 addresses [1..254] & exclude 192.168.23.1/24 */
char *blacklist[] = {"176.16.222.10/24", "192.168.23.1/24", "192.168.38.2/24"};
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, blacklist, 3, 253);
}
+static void test_pool_sizes_v6(void)
+{
+ /* 256 prefixes of /64 each */
+ test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
+}
+
int main(int argc, char **argv)
{
- osmo_init_logging(&log_info);
+ void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
+ msgb_talloc_ctx_init(tall_ctx, 0);
+ osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));
- test_pool_sizes();
+ if (argc < 2 || strcmp(argv[1], "-v6")) {
+ test_pool_sizes();
+ } else {
+ test_pool_sizes_v6();
+ }
+ return 0;
}
diff --git a/tests/lib/ippool_test.err b/tests/lib/ippool_test.err
index b11c12a..7dc5371 100644
--- a/tests/lib/ippool_test.err
+++ b/tests/lib/ippool_test.err
@@ -8,5 +8,3 @@ No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
-No more IP addresses available
-No more IP addresses available
diff --git a/tests/lib/ippool_test.ok b/tests/lib/ippool_test.ok
index 263494e..d247c74 100644
--- a/tests/lib/ippool_test.ok
+++ b/tests/lib/ippool_test.ok
@@ -66301,263 +66301,6 @@ allocated address 192.168.255.251
allocated address 192.168.255.252
allocated address 192.168.255.253
allocated address 192.168.255.254
-testing pool for prefix 2001:DB8::/56, flags=0x0, blacklist_size=0, expected_size=256
-allocated address 2001:db8::
-allocated address 2001:db8:0:1::
-allocated address 2001:db8:0:2::
-allocated address 2001:db8:0:3::
-allocated address 2001:db8:0:4::
-allocated address 2001:db8:0:5::
-allocated address 2001:db8:0:6::
-allocated address 2001:db8:0:7::
-allocated address 2001:db8:0:8::
-allocated address 2001:db8:0:9::
-allocated address 2001:db8:0:a::
-allocated address 2001:db8:0:b::
-allocated address 2001:db8:0:c::
-allocated address 2001:db8:0:d::
-allocated address 2001:db8:0:e::
-allocated address 2001:db8:0:f::
-allocated address 2001:db8:0:10::
-allocated address 2001:db8:0:11::
-allocated address 2001:db8:0:12::
-allocated address 2001:db8:0:13::
-allocated address 2001:db8:0:14::
-allocated address 2001:db8:0:15::
-allocated address 2001:db8:0:16::
-allocated address 2001:db8:0:17::
-allocated address 2001:db8:0:18::
-allocated address 2001:db8:0:19::
-allocated address 2001:db8:0:1a::
-allocated address 2001:db8:0:1b::
-allocated address 2001:db8:0:1c::
-allocated address 2001:db8:0:1d::
-allocated address 2001:db8:0:1e::
-allocated address 2001:db8:0:1f::
-allocated address 2001:db8:0:20::
-allocated address 2001:db8:0:21::
-allocated address 2001:db8:0:22::
-allocated address 2001:db8:0:23::
-allocated address 2001:db8:0:24::
-allocated address 2001:db8:0:25::
-allocated address 2001:db8:0:26::
-allocated address 2001:db8:0:27::
-allocated address 2001:db8:0:28::
-allocated address 2001:db8:0:29::
-allocated address 2001:db8:0:2a::
-allocated address 2001:db8:0:2b::
-allocated address 2001:db8:0:2c::
-allocated address 2001:db8:0:2d::
-allocated address 2001:db8:0:2e::
-allocated address 2001:db8:0:2f::
-allocated address 2001:db8:0:30::
-allocated address 2001:db8:0:31::
-allocated address 2001:db8:0:32::
-allocated address 2001:db8:0:33::
-allocated address 2001:db8:0:34::
-allocated address 2001:db8:0:35::
-allocated address 2001:db8:0:36::
-allocated address 2001:db8:0:37::
-allocated address 2001:db8:0:38::
-allocated address 2001:db8:0:39::
-allocated address 2001:db8:0:3a::
-allocated address 2001:db8:0:3b::
-allocated address 2001:db8:0:3c::
-allocated address 2001:db8:0:3d::
-allocated address 2001:db8:0:3e::
-allocated address 2001:db8:0:3f::
-allocated address 2001:db8:0:40::
-allocated address 2001:db8:0:41::
-allocated address 2001:db8:0:42::
-allocated address 2001:db8:0:43::
-allocated address 2001:db8:0:44::
-allocated address 2001:db8:0:45::
-allocated address 2001:db8:0:46::
-allocated address 2001:db8:0:47::
-allocated address 2001:db8:0:48::
-allocated address 2001:db8:0:49::
-allocated address 2001:db8:0:4a::
-allocated address 2001:db8:0:4b::
-allocated address 2001:db8:0:4c::
-allocated address 2001:db8:0:4d::
-allocated address 2001:db8:0:4e::
-allocated address 2001:db8:0:4f::
-allocated address 2001:db8:0:50::
-allocated address 2001:db8:0:51::
-allocated address 2001:db8:0:52::
-allocated address 2001:db8:0:53::
-allocated address 2001:db8:0:54::
-allocated address 2001:db8:0:55::
-allocated address 2001:db8:0:56::
-allocated address 2001:db8:0:57::
-allocated address 2001:db8:0:58::
-allocated address 2001:db8:0:59::
-allocated address 2001:db8:0:5a::
-allocated address 2001:db8:0:5b::
-allocated address 2001:db8:0:5c::
-allocated address 2001:db8:0:5d::
-allocated address 2001:db8:0:5e::
-allocated address 2001:db8:0:5f::
-allocated address 2001:db8:0:60::
-allocated address 2001:db8:0:61::
-allocated address 2001:db8:0:62::
-allocated address 2001:db8:0:63::
-allocated address 2001:db8:0:64::
-allocated address 2001:db8:0:65::
-allocated address 2001:db8:0:66::
-allocated address 2001:db8:0:67::
-allocated address 2001:db8:0:68::
-allocated address 2001:db8:0:69::
-allocated address 2001:db8:0:6a::
-allocated address 2001:db8:0:6b::
-allocated address 2001:db8:0:6c::
-allocated address 2001:db8:0:6d::
-allocated address 2001:db8:0:6e::
-allocated address 2001:db8:0:6f::
-allocated address 2001:db8:0:70::
-allocated address 2001:db8:0:71::
-allocated address 2001:db8:0:72::
-allocated address 2001:db8:0:73::
-allocated address 2001:db8:0:74::
-allocated address 2001:db8:0:75::
-allocated address 2001:db8:0:76::
-allocated address 2001:db8:0:77::
-allocated address 2001:db8:0:78::
-allocated address 2001:db8:0:79::
-allocated address 2001:db8:0:7a::
-allocated address 2001:db8:0:7b::
-allocated address 2001:db8:0:7c::
-allocated address 2001:db8:0:7d::
-allocated address 2001:db8:0:7e::
-allocated address 2001:db8:0:7f::
-allocated address 2001:db8:0:80::
-allocated address 2001:db8:0:81::
-allocated address 2001:db8:0:82::
-allocated address 2001:db8:0:83::
-allocated address 2001:db8:0:84::
-allocated address 2001:db8:0:85::
-allocated address 2001:db8:0:86::
-allocated address 2001:db8:0:87::
-allocated address 2001:db8:0:88::
-allocated address 2001:db8:0:89::
-allocated address 2001:db8:0:8a::
-allocated address 2001:db8:0:8b::
-allocated address 2001:db8:0:8c::
-allocated address 2001:db8:0:8d::
-allocated address 2001:db8:0:8e::
-allocated address 2001:db8:0:8f::
-allocated address 2001:db8:0:90::
-allocated address 2001:db8:0:91::
-allocated address 2001:db8:0:92::
-allocated address 2001:db8:0:93::
-allocated address 2001:db8:0:94::
-allocated address 2001:db8:0:95::
-allocated address 2001:db8:0:96::
-allocated address 2001:db8:0:97::
-allocated address 2001:db8:0:98::
-allocated address 2001:db8:0:99::
-allocated address 2001:db8:0:9a::
-allocated address 2001:db8:0:9b::
-allocated address 2001:db8:0:9c::
-allocated address 2001:db8:0:9d::
-allocated address 2001:db8:0:9e::
-allocated address 2001:db8:0:9f::
-allocated address 2001:db8:0:a0::
-allocated address 2001:db8:0:a1::
-allocated address 2001:db8:0:a2::
-allocated address 2001:db8:0:a3::
-allocated address 2001:db8:0:a4::
-allocated address 2001:db8:0:a5::
-allocated address 2001:db8:0:a6::
-allocated address 2001:db8:0:a7::
-allocated address 2001:db8:0:a8::
-allocated address 2001:db8:0:a9::
-allocated address 2001:db8:0:aa::
-allocated address 2001:db8:0:ab::
-allocated address 2001:db8:0:ac::
-allocated address 2001:db8:0:ad::
-allocated address 2001:db8:0:ae::
-allocated address 2001:db8:0:af::
-allocated address 2001:db8:0:b0::
-allocated address 2001:db8:0:b1::
-allocated address 2001:db8:0:b2::
-allocated address 2001:db8:0:b3::
-allocated address 2001:db8:0:b4::
-allocated address 2001:db8:0:b5::
-allocated address 2001:db8:0:b6::
-allocated address 2001:db8:0:b7::
-allocated address 2001:db8:0:b8::
-allocated address 2001:db8:0:b9::
-allocated address 2001:db8:0:ba::
-allocated address 2001:db8:0:bb::
-allocated address 2001:db8:0:bc::
-allocated address 2001:db8:0:bd::
-allocated address 2001:db8:0:be::
-allocated address 2001:db8:0:bf::
-allocated address 2001:db8:0:c0::
-allocated address 2001:db8:0:c1::
-allocated address 2001:db8:0:c2::
-allocated address 2001:db8:0:c3::
-allocated address 2001:db8:0:c4::
-allocated address 2001:db8:0:c5::
-allocated address 2001:db8:0:c6::
-allocated address 2001:db8:0:c7::
-allocated address 2001:db8:0:c8::
-allocated address 2001:db8:0:c9::
-allocated address 2001:db8:0:ca::
-allocated address 2001:db8:0:cb::
-allocated address 2001:db8:0:cc::
-allocated address 2001:db8:0:cd::
-allocated address 2001:db8:0:ce::
-allocated address 2001:db8:0:cf::
-allocated address 2001:db8:0:d0::
-allocated address 2001:db8:0:d1::
-allocated address 2001:db8:0:d2::
-allocated address 2001:db8:0:d3::
-allocated address 2001:db8:0:d4::
-allocated address 2001:db8:0:d5::
-allocated address 2001:db8:0:d6::
-allocated address 2001:db8:0:d7::
-allocated address 2001:db8:0:d8::
-allocated address 2001:db8:0:d9::
-allocated address 2001:db8:0:da::
-allocated address 2001:db8:0:db::
-allocated address 2001:db8:0:dc::
-allocated address 2001:db8:0:dd::
-allocated address 2001:db8:0:de::
-allocated address 2001:db8:0:df::
-allocated address 2001:db8:0:e0::
-allocated address 2001:db8:0:e1::
-allocated address 2001:db8:0:e2::
-allocated address 2001:db8:0:e3::
-allocated address 2001:db8:0:e4::
-allocated address 2001:db8:0:e5::
-allocated address 2001:db8:0:e6::
-allocated address 2001:db8:0:e7::
-allocated address 2001:db8:0:e8::
-allocated address 2001:db8:0:e9::
-allocated address 2001:db8:0:ea::
-allocated address 2001:db8:0:eb::
-allocated address 2001:db8:0:ec::
-allocated address 2001:db8:0:ed::
-allocated address 2001:db8:0:ee::
-allocated address 2001:db8:0:ef::
-allocated address 2001:db8:0:f0::
-allocated address 2001:db8:0:f1::
-allocated address 2001:db8:0:f2::
-allocated address 2001:db8:0:f3::
-allocated address 2001:db8:0:f4::
-allocated address 2001:db8:0:f5::
-allocated address 2001:db8:0:f6::
-allocated address 2001:db8:0:f7::
-allocated address 2001:db8:0:f8::
-allocated address 2001:db8:0:f9::
-allocated address 2001:db8:0:fa::
-allocated address 2001:db8:0:fb::
-allocated address 2001:db8:0:fc::
-allocated address 2001:db8:0:fd::
-allocated address 2001:db8:0:fe::
-allocated address 2001:db8:0:ff::
testing pool for prefix 192.168.23.0/24, flags=0x3, blacklist_size=3, expected_size=253
allocated address 192.168.23.2
allocated address 192.168.23.3
diff --git a/tests/lib/ippool_v6_test.err b/tests/lib/ippool_v6_test.err
new file mode 100644
index 0000000..f0edefe
--- /dev/null
+++ b/tests/lib/ippool_v6_test.err
@@ -0,0 +1,2 @@
+No more IP addresses available
+No more IP addresses available
diff --git a/tests/lib/ippool_v6_test.ok b/tests/lib/ippool_v6_test.ok
new file mode 100644
index 0000000..19ea4d1
--- /dev/null
+++ b/tests/lib/ippool_v6_test.ok
@@ -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::
diff --git a/tests/testsuite.at b/tests/testsuite.at
index f365f95..fa94db3 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -8,12 +8,25 @@ cat $abs_srcdir/lib/ippool_test.err > experr
AT_CHECK([$abs_top_builddir/tests/lib/ippool_test], [], [expout], [experr])
AT_CLEANUP
+AT_SETUP([ippool_v6])
+AT_KEYWORDS([ippool_v6])
+cat $abs_srcdir/lib/ippool_v6_test.ok > expout
+cat $abs_srcdir/lib/ippool_v6_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/lib/ippool_test -v6], [], [expout], [experr])
+AT_CLEANUP
+
AT_SETUP([in46a])
AT_KEYWORDS([in46a])
cat $abs_srcdir/lib/in46a_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/lib/in46a_test], [], [expout], [])
AT_CLEANUP
+AT_SETUP([in46a_v6])
+AT_KEYWORDS([in46a_v6])
+cat $abs_srcdir/lib/in46a_v6_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/lib/in46a_test -v6], [], [expout], [])
+AT_CLEANUP
+
AT_SETUP([gtpie])
AT_KEYWORDS([gtpie])
cat $abs_srcdir/gtp/gtpie_test.ok > expout