Compare commits

...

49 Commits

Author SHA1 Message Date
Oliver Smith a2dda133e4 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: Idca6e77cb94529d4a5d85b004989db5b8993a82c
2024-05-08 14:40:52 +02:00
Harald Welte 39e085f03e README.md: Improve markdown formatting; more links
Change-Id: I16511491fdf00a01b9b6deecf8d19a1f2f5e6c3c
2024-03-23 19:30:11 +01:00
Harald Welte 9f46e04a23 Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: I2b5d1c422370ce8a2703c6f68403d27451559322
2024-03-23 19:20:00 +01:00
Oliver Smith 1f8b868649 tests/qemu: add 05_ms_ip46_sgsn_ip4.sh
Change-Id: I1e306bc4534f14138249cd4034f54f77d940b261
2024-02-21 13:46:50 +01:00
Oliver Smith c95db93512 tools/gtp-tunnel: pass rc of gtp_del_tunnel
Don't exit with 0 if gtp_del_tunnel fails.

Change-Id: I8046a57a5831e6bdee2ac37415380ce391c79c22
2024-02-21 10:28:31 +01:00
Oliver Smith 170337196b tests/qemu: pass MS_PROTO to gtp-tunnel delete
The MS protocol needs to be passed to gtp-tunnel delete, instead of the
SGSN_GGSN_PROTO.

Fix for:
  + gtp-tunnel add gtp_sgsn v1 200 100 172.99.0.1 fd00::2
  …
  + gtp-tunnel delete gtp_sgsn v1 200 ip6
  genl_socket_talk: No such file or directory

Change-Id: Ib6b011520fe41855b87ed0e2f6f8a6af3d0b2400
2024-02-21 10:28:31 +01:00
Oliver Smith 7928152581 gtp-link: close sockets on error
Avoid resource leaks.

Fixes: CID#347578, CID#347579
Change-Id: I9c437de9712ebe568528b4c9ee1e89a4ba5cd5d1
2024-02-19 12:18:28 +01:00
Pablo Neira Ayuso d489488a70 gtp-link: allow to specify listener address
gtp-link add <device> <family> [--sgsn] [<address>]

Allow to specify listener address, if not specified use ANY_ADDRESS.

Change-Id: I7a9b64393fd68d58c2c158c0e85c0470be007f63
2024-02-16 16:08:19 +01:00
Oliver Smith 72ddb01219 Add QEMU tests
Add tests to ensure libgtpnl + kernel driver work as expected.

Right now a kernel needs to be built from source, using Pablo's tree:
https://git.kernel.org/pub/scm/linux/kernel/git/pablo/gtp.git/

Make sure to enable:
  CONFIG_GTP=y
  CONFIG_NET_NS=y
  CONFIG_VETH=y

$ cp bzImage tests/qemu/_linux
$ ./configure --enable-qemu-tests
$ make
$ make check

Once patches are upstreamed, it will be possible to use a pre-built
kernel from jenkins with: make -C tests qemu-download-kernel

Related: OS#1952
Change-Id: Ibf75514b866fffb11e90529e4705f126b23d7415
2024-02-16 15:15:24 +01:00
Pablo Neira Ayuso 791a620e4d gtp-tunnel: display i_tei in help
display <tid|i_tei> in help message that is printed.

Change-Id: I0aa78e654f23a2b6588ac8e1e5508e798caad1a7
2024-02-15 15:42:02 +01:00
Pablo Neira Ayuso 64b5757043 gtp-genl: display gtp device in listing
Display the device that the tunnel belongs to, users need this
information to delete tunnels.

Change-Id: I888723a8318c04772f7d22a042e69fa6e9ef2239
2024-02-15 15:42:02 +01:00
Pablo Neira Ayuso 43b88cadbf gtp: add flags to gtp_tunnel object
Flags tells us what fields have been set in this object.

This is required by

	./gtp-tunnel del

otherwise, build helper function adds the MS/UE address and kernel reports
ENOENT, and tunnel identifier is ignored.

Update gtp-tunnel tool to set flowid to zero in the GTP version 0 case,
otherwise kernel reports EINVAL since now this flag is not ever set.

Change-Id: I66677ab2d4de2c459ed9987c465fce6f059d6d93
2024-02-15 15:42:02 +01:00
Pablo Neira Ayuso 67e1fef97d gtp: provide interface to set family
Kernel now supports for TID/I_TEI to be used in both IPv4 and IPv6 GTP
tunnels. This improves dualstack support for MS/UE so GTP traffic can
be identified with the same tunnel identifier.

Update gtp-tunnel to specify the family, since a tunnel is now
identified by the following tuple [ version, identifier, family ].

Change-Id: I584d3997ffb89cd430dfda9615a4ce0ce517ab2a
2024-02-15 15:42:02 +01:00
Pablo Neira Ayuso 9911f6bfeb gtp-link: set IPv6 socket only
GTP driver bails out for IPv4-mapped-IPv6 socket with EADDRNOAVAIL,
to prevent issues with setsockopt IPV6_ADDRFORM.

GTP control plane checks that tunnel family matches the socket family
for this GTP device, ie. there is a 1:1 mapping between the socket
listener and the device which determines the supported IP tunnel header.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Change-Id: I887a107657059adeb14ae425576ae7ea9018f762
2024-02-15 15:41:42 +01:00
Pablo Neira Ayuso 38b5e239d6 gtp-link: add IPv6 support
Update tool to allow to specify family that is used for the outer network
header in GTP tunnels.

./gtp-link add gtp0 ip 			# IPv4 GGSN
./gtp-link add gtp0 ip --sgsn		# IPv4 SGSN
./gtp-link add gtp0 ip6 		# IPv6 GGSN
./gtp-link add gtp0 ip6 --sgsn		# IPv6 SGSN

Change-Id: I201c32a1bf9a2ab7a228287590bc7ec19c4997b9
2024-02-15 15:14:08 +01:00
Oliver Smith f3253354e3 tools/gtp-tunnel: pass rc of gtp_add_tunnel
Don't exit with 0 if gtp_add_tunnel fails.

Change-Id: If11b1fb4487a96dcb7f24d95794fea4e36202203
2024-02-14 16:18:52 +01:00
Oliver Smith 4374964187 Makefile.am: remove DIST_SUBDIRS
It is the same as SUBDIRS, so it doesn't need to be defined. We don't do
this is in other Osmocom projects either.

Related: https://www.gnu.org/software/automake/manual/html_node/SUBDIRS-vs-DIST_005fSUBDIRS.html
Change-Id: I35345ba1c6ab6ecc827177dfd6f14a5be6606432
2024-02-14 16:18:52 +01:00
Oliver Smith 9c14b5060c configure.ac: move AC_OUTPUT -> AC_CONFIG_FILES
Instead of having one file in AC_OUTPUT, and all others in
AC_CONFIG_FILES, move all of them to AC_CONFIG_FILES. Sort the files
while at it and have one per line.

Fix this warning:
  configure.ac:86: warning: AC_OUTPUT should be used without arguments.

Change-Id: Iea782b03c9ede154eedbd0c0e9f0d7359bb49967
2024-02-14 16:18:52 +01:00
Oliver Smith a295615229 Add IPv6 support
Implement IPv6 in libgtpnl and the gtp-tunnel testing tool. Allow to
combine:

- GTPA_MS_ADDRESS and GTPA_PEER_ADDR6
- GTPA_MS_ADDR6 and GTPA_PEER_ADDRESS

to specify IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP in the tunnel
declaration from control plane.

This patch is based on multiple patches from Pablo in OS#6123. I decided
to squash them to directly implement v4-in-v6 and vice versa, instead of
implementing another variant first and then changing it again.

Co-developed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Related: OS#6096
Change-Id: If864c9170f74af52a95cbc4cdb1b866e0309306b
2023-10-24 13:54:09 +02:00
Oliver Smith 717db098c7 Use struct gtp_addr for ms_addr and sgsn_addr
Prepare for IPv6 support by using a new struct for MS and SGSN
addresses, in which either an IPv4 or IPv6 can be stored.

Related: OS#6096
Change-Id: Ifc7e3b03a723fb544d1c7b789101102b2c27b60e
2023-10-19 16:09:15 +02:00
Oliver Smith 2db5d2baf7 include/linux/gtp.h: sync with kernel header
Make the file exactly the same as include/uapi/linux/gtp.h in
linux.git. GTPA_SGSN_ADDRESS is not used, but we decided to rather add
it and not have a diff.

Related: https://gerrit.osmocom.org/c/libgtpnl/+/34735/comment/909c69a6_2fe7781a/
Change-Id: Icf7b78e6655b6573c09c2eaa71d22ef6742b2594
2023-10-19 16:09:15 +02:00
Oliver Smith 2121b82a61 include/linux/gtp.h: fix GTPA_MAX
Apply same fix as done by Pablo in linux.git:
> Subtract one to __GTPA_MAX, otherwise GTPA_MAX is off by 2

Change-Id: I42a358fc795d0e593e2c5509dcfa6ffdbad3cbfa
2023-10-19 16:09:15 +02:00
Oliver Smith 0bd1e0d6eb configure: add --enable-werror
Change-Id: Ife3716c667d2e9b3fcd94d3cdc044d594e7dab5a
2023-10-19 16:09:15 +02:00
Oliver Smith dd335ef8fc configure: regular_C(PP)FLAGS -> C(PP)FLAGS
Make it consistent with other Osmocom projects by removing special
naming for CFLAGS and CPPFLAGS. Otherwise the arguments we typically
add, such as --enable-sanitize which is already there, or
--enable-werror which I'll add in the next patch, do not work without
further changes.

Change-Id: I11e9657fb0c038169bd414a6455044ff4a4709b7
2023-10-19 16:09:15 +02:00
Oliver Smith 6db529f2da README, debian/control: update URLs
Change-Id: I0995f14657e3f58a46e9e357aa3e6baecc132fe0
2023-10-19 16:09:15 +02:00
Oliver Smith b4e23a4fed gitignore: add gtp-link, gtp-tunnel
Change-Id: I9f9c9f64d96e25d99c5631ec40c1439f76678905
2023-10-19 15:54:51 +02:00
Pablo Neira Ayuso 99a5c32546 gtp-genl: allocate room for maximum IPv6 address
Otherwise, inet_ntop() might return ENOSPC because of the buffer
being too small to accomodate an IPv6 address.

Change-Id: I2283e0c3112bec8e6e7e7b5c96657facc09d0a7a
2023-10-19 11:55:50 +00:00
Oliver Smith 0b272f256d gitreview: new file
Change-Id: Iadc35e1a14563516c81d4c845d0d6c5c7fda668a
2023-10-18 14:21:19 +02:00
Pau Espin 6af24bb93c Bump version: 1.2.4.3-b59b → 1.2.5
Change-Id: I7a752f8b3652be3c87808fa8c4f46ea98841969f
2023-09-12 14:18:53 +02:00
Oliver Smith b59bc14f17 debian: set compat level to 10
Related: OS#5958
Change-Id: I393d7e1211e6f9ace420a874a0582208c939bbc2
2023-04-25 16:48:16 +02:00
Neels Hofmeyr b8503c6537 Merge "fix memleak on del_tunnel() failure" 2023-02-09 16:50:42 +00:00
Pau Espin dd3696f173 Bump version: 1.2.3.3-6954 → 1.2.4
Change-Id: I905039b2fb8d1602df16a235c976ffbc48b5da44
2023-02-07 14:07:11 +01:00
Neels Hofmeyr 50ae7eafd2 fix memleak on del_tunnel() failure
Related: CID#307542
Change-Id: Ie29840a7518435447acf48f12e81fc520308a265
2023-02-03 01:05:01 +01:00
Oliver Smith 6954da78fb tools/gtp-link: add --sgsn to usage
Change-Id: I995754812b8418757a8df9ab2a3c208d61f16a8c
2022-09-07 15:48:16 +02:00
Oliver Smith 25d92af06a tools/gtp-tunnel: fix del usage
Change-Id: Iba5afd0cdb1588def3d865900ae02773dfd654e2
2022-09-07 15:43:27 +02:00
Oliver Smith 0535f30a37 README: fix link to homepage
Old link is 404, so adjust it.

Change-Id: I9ae4e9cb53618f4119170eb00e771c9033c52229
2022-09-01 14:29:21 +02:00
Pau Espin 6e7f2f2aa3 Bump version: 1.2.2.1-9fa7-dirty → 1.2.3
Change-Id: I9530d28a2deb4c31be0d1929a37e956c56f55b36
2022-06-28 16:13:45 +02:00
Neels Hofmeyr 9fa76ec4d6 fix some cases of rc == 0 on error
When genl_socket_talk() fails, return rc != 0.

While testing the new osmo-upf program, I noticed that I failed to get a
"Operation not permitted" error when forgetting to set cap_net_admin on
the osmo-upf binary. It looked like everything should work, but doesn't.

It is possible to catch these error cases without this patch, by
monitoring errno. That may well be the intention of the API? I'm still
submitting this patch because it seems better to return rc != 0.

Here is a code example that also catches all error cases without this
patch:

        errno = 0;
        rc = gtp_add_tunnel(genl_id, nl, t);
        if (errno) {
                rc = -errno;
        } else if (rc) {
                rc = -EINVAL;
        } else {
                tun->active = true;
        }
	return rc;

Related: SYS#5599
Change-Id: I22fd69709e023572c6c616a4184340a554456faf
2022-04-28 08:03:58 +02:00
Pau Espin Pedrol 314cc32fce Bump version: 1.2.1.6-465b → 1.2.2
Change-Id: Ica00c6684de20df06f69678cd321abaeca1996fb
2021-02-23 12:52:08 +01:00
Gabriel Ganne 465b76dfca install gtp-tunnel and gtp-link tools
This eases quick setup for demonstration purposes.

Change-Id: I674c463989bc1529bfe132b7ec0df7a0052169df
2021-02-12 09:36:52 +01:00
Pau Espin 118dd3ab17 .gitignore: Ignore new autofoo tmp files
Change-Id: Iaf853783679815e74b6b8e5f3e8631c53ed20613
2021-02-04 12:44:56 +01:00
Oliver Smith 5314ea43ac configure.ac: set -std=gnu11
Change-Id: Iea60698b4cb0f9c04a6f75cc4ca2ea5fbb84bae8
2021-01-27 17:31:54 +01:00
Pau Espin 102c81cf1b Enable parallel make in make distcheck
Related: OS#4421
Change-Id: I4fd59946c771906b5ec36e605fa8e6cbc648beec
2020-10-12 17:44:21 +02:00
Oliver Smith 01b2d940a0 contrib: integrate RPM spec
Remove OpenSUSE bug report link, set version to @VERSION@, make it build with
CentOS 8 etc.

Related: OS#4550
Change-Id: I5376fde96c7442eb70f49a2ce38f57a817a94f81
2020-05-19 15:19:35 +02:00
Oliver Smith 2f276eb3c6 contrib: import RPM spec
Copy the RPM spec file from:
https://build.opensuse.org/project/show/home:mnhauke:osmocom:nightly

Related: OS#4550
Change-Id: I3c9c989972cd223a46d28955edbfe4c69b7a7afb
2020-05-14 11:18:39 +02:00
Pau Espin fd3e98d334 Bump version: 1.2.0.2-d160-dirty → 1.2.1
Change-Id: Icc0dca37a88ce213f9b80598ab89570bd81cf0e7
2019-08-07 12:29:05 +02:00
Pau Espin 8639be5549 debian: Adapt package name to lib's current major version
Current LIBVERSION is 1:1:1 (current:revision:age), so major=current-age
makes it be major=0: libgtpnl0.

It can bee seen in current osmocom debian repos that libgtpnl1 package
contains libgtpnl.so.0.

Let's set package name accordingly.

Change-Id: I7d2fa50b6a17a598467f555558660ef2396c1744
2019-08-07 12:26:19 +02:00
Oliver Smith d160a73a41 contrib/jenkins.sh: run "make maintainer-clean"
Related: OS#3047
Change-Id: I20997b986b24ced9bb8d6d66a66d5ec2bafd00f2
2019-07-10 11:57:27 +02:00
Pau Espin bc71674c67 debian/rules: Don't overwrite .tarball-version
The .tarball-version file should contain the *source version* uniquely
identifying the git commit, and not the Debian package name.

With https://gerrit.osmocom.org/#/c/osmo-ci/+/10343/ there is a correct
.tarball-version file in the .tar.xz of the nightly source packages.

Related: OS#3449
Change-Id: I22dac8524ce8acb033dc9c72eab107999f49f975
2018-09-17 10:40:04 +02:00
36 changed files with 1038 additions and 134 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
open_collective: osmocom

8
.gitignore vendored
View File

@ -6,6 +6,7 @@
.deps
.libs
.dirstamp
*~
# Generated by autoconf/configure
Makefile
@ -31,3 +32,10 @@ debian/tmp
debian/libgtpnl-dev
debian/libgtpnl-dbg
debian/libgtpnl0
contrib/libgtpnl.spec
tools/gtp-link
tools/gtp-tunnel
tests/qemu/_*

3
.gitreview Normal file
View File

@ -0,0 +1,3 @@
[gerrit]
host=gerrit.osmocom.org
project=libgtpnl

View File

@ -1,2 +1,2 @@
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include ${LIBMNL_CFLAGS}
AM_CFLAGS = ${regular_CFLAGS} ${GCC_FVISIBILITY_HIDDEN}
AM_CPPFLAGS = ${CPPFLAGS} -I${top_srcdir}/include ${LIBMNL_CFLAGS}
AM_CFLAGS = ${CFLAGS} ${GCC_FVISIBILITY_HIDDEN}

View File

@ -4,8 +4,12 @@ include $(top_srcdir)/Make_global.am
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src include tools
DIST_SUBDIRS = src include tools
SUBDIRS = \
include \
src \
tests \
tools \
$(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgtpnl.pc

View File

@ -1,11 +1,11 @@
# libgtpnl - netlink library for Linux kernel GTP
In order to control the kernel-side GTP-U plane, a netlink based control
interface between GTP-C in userspace and GTP-U in kernelspace was
invented.
In order to control the kernel-side GTP-U plane (`gtp` driver), a
netlink based control interface between GTP-C in userspace and GTP-U in
kernelspace was invented.
The encoding and decoding of these control messages is implemented in
the libgtpnl (library for GTP netlink).
this *libgtpnl* (library for GTP netlink).
libgtpnl is part of the [Osmocom](https://osmocom.org/) Open Source
Mobile Communications project.
@ -13,16 +13,17 @@ Mobile Communications project.
## Homepage
The official homepage of the project is
<https://osmocom.org/projects/libgtpnl/wiki/>
<https://osmocom.org/projects/linux-kernel-gtp-u/wiki/Libgtpnl>
GIT Repository
--------------
You can clone from the official libgtpnl.git repository using
git clone git://git.osmocom.org/libgtpnl.git
git clone https://gitea.osmocom.org/cellular-infrastructure/libgtpnl
There is a cgit interface at <http://git.osmocom.org/libgtpnl/>
Visiting the URL in a browser shows a
[web interface](https://gitea.osmocom.org/cellular-infrastructure/libgtpnl).
Mailing List
------------

View File

@ -9,10 +9,17 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign tar-pax no-dist-gzip dist-bzip2 1.6 subdir-objects])
CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64 -D_REENTRANT"
CFLAGS="$CFLAGS -Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wshadow -Wstrict-prototypes \
-Wformat=2 -pipe"
dnl include release helper
RELMAKE='-include osmo-release.mk'
AC_SUBST([RELMAKE])
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@ -46,11 +53,48 @@ then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
regular_CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_REENTRANT"
regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wshadow -Wstrict-prototypes \
-Wformat=2 -pipe"
AC_SUBST([regular_CPPFLAGS])
AC_SUBST([regular_CFLAGS])
AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libgtpnl/Makefile include/linux/Makefile tools/Makefile libgtpnl.pc])
AC_OUTPUT
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+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
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_ARG_ENABLE(qemu_tests,
[AS_HELP_STRING(
[--enable-qemu-tests],
[Run automated tests in QEMU]
)],
[qemu_tests=$enableval], [qemu_tests="no"])
AC_MSG_CHECKING([whether to enable QEMU tests])
AC_MSG_RESULT([$qemu_tests])
AM_CONDITIONAL(ENABLE_QEMU_TESTS, test x"$qemu_tests" = x"yes")
if test x"$qemu_tests" = x"yes" && ! $srcdir/tests/qemu/check-depends.sh; then
AC_MSG_ERROR([missing programs for --enable-qemu-tests])
fi
AC_SUBST([CPPFLAGS])
AC_SUBST([CFLAGS])
AC_CONFIG_FILES([
Makefile
include/Makefile
include/libgtpnl/Makefile
include/linux/Makefile
libgtpnl.pc
src/Makefile
tests/Makefile
tools/Makefile
])
AC_OUTPUT()

View File

@ -7,6 +7,7 @@ osmo-clean-workspace.sh
autoreconf --install --force
./configure --enable-sanitize CFLAGS="-Werror" CPPFLAGS="-Werror"
$MAKE $PARALLEL_MAKE
$MAKE distcheck
$MAKE $PARALLEL_MAKE distcheck
$MAKE $PARALLEL_MAKE maintainer-clean
osmo-clean-workspace.sh

53
debian/changelog vendored
View File

@ -1,3 +1,56 @@
libgtpnl (1.2.5) unstable; urgency=medium
[ Neels Janosch Hofmeyr ]
* fix memleak on del_tunnel() failure
[ Oliver Smith ]
* debian: set compat level to 10
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:18:53 +0200
libgtpnl (1.2.4) unstable; urgency=medium
[ Oliver Smith ]
* README: fix link to homepage
* tools/gtp-tunnel: fix del usage
* tools/gtp-link: add --sgsn to usage
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 14:07:11 +0100
libgtpnl (1.2.3) unstable; urgency=medium
[ Neels Hofmeyr ]
* fix some cases of rc == 0 on error
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 16:13:45 +0200
libgtpnl (1.2.2) unstable; urgency=medium
[ Oliver Smith ]
* contrib: import RPM spec
* contrib: integrate RPM spec
* configure.ac: set -std=gnu11
[ Pau Espin Pedrol ]
* Enable parallel make in make distcheck
* .gitignore: Ignore new autofoo tmp files
[ Gabriel Ganne ]
* install gtp-tunnel and gtp-link tools
-- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 12:52:08 +0100
libgtpnl (1.2.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
* debian/rules: Don't overwrite .tarball-version
* debian: Adapt package name to lib's current major version
[ Oliver Smith ]
* contrib/jenkins.sh: run "make maintainer-clean"
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 12:29:04 +0200
libgtpnl (1.2.0) unstable; urgency=medium
* Fix CTRL_ATTR_FAMILY_ID attribute size

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

25
debian/control vendored
View File

@ -1,8 +1,8 @@
Source: libgtpnl
Maintainer: Harald Welte <laforge@gnumonks.org>
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Section: libs
Priority: optional
Build-Depends: debhelper (>= 9),
Build-Depends: debhelper (>= 10),
autotools-dev,
autoconf,
automake,
@ -12,11 +12,11 @@ Build-Depends: debhelper (>= 9),
pkg-config,
libmnl-dev
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/libgtpnl.git
Vcs-Browser: http://git.osmocom.org/gitweb?p=libgtpnl.git;a=summary
Homepage: https://projects.osmocom.org/projects/openggsn
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/libgtpnl
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/libgtpnl
Homepage: https://osmocom.org/projects/linux-kernel-gtp-u/wiki/Libgtpnl
Package: libgtpnl1
Package: libgtpnl0
Section: libs
Architecture: any
Multi-Arch: same
@ -29,7 +29,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: libmnl-dev,
libgtpnl1 (= ${binary:Version}),
libgtpnl0 (= ${binary:Version}),
${misc:Depends}
Description: Development headers for Linux kernel GTP-U netlink library
The header files provided by this package may be used to develop
@ -40,6 +40,15 @@ Architecture: any
Multi-Arch: same
Section: debug
Priority: extra
Depends: libgtpnl1 (= ${binary:Version}),
Depends: libgtpnl0 (= ${binary:Version}),
${misc:Depends}
Description: Debug symbols for Linux kernel GTP-U netlink library
Package: libgtpnl-tools
Architecture: any
Multi-Arch: same
Section: net
Priority: extra
Depends: libgtpnl0 (= ${binary:Version}),
${misc:Depends}
Description: Tools to manage gtp interfaces and tunnels.

2
debian/libgtpnl-tools.install vendored Normal file
View File

@ -0,0 +1,2 @@
/usr/bin/gtp-link
/usr/bin/gtp-tunnel

4
debian/rules vendored
View File

@ -24,7 +24,3 @@ override_dh_install:
# Print test results in case of a failure
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf

View File

@ -11,8 +11,11 @@ void gtp_tunnel_free(struct gtp_tunnel *t);
void gtp_tunnel_set_ifns(struct gtp_tunnel *t, int ifns);
void gtp_tunnel_set_ifidx(struct gtp_tunnel *t, uint32_t ifidx);
void gtp_tunnel_set_family(struct gtp_tunnel *t, uint16_t family);
void gtp_tunnel_set_ms_ip4(struct gtp_tunnel *t, struct in_addr *ms_addr);
void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr);
void gtp_tunnel_set_ms_ip6(struct gtp_tunnel *t, const struct in6_addr *ms_addr);
void gtp_tunnel_set_sgsn_ip6(struct gtp_tunnel *t, const struct in6_addr *sgsn_addr);
void gtp_tunnel_set_version(struct gtp_tunnel *t, uint32_t version);
void gtp_tunnel_set_tid(struct gtp_tunnel *t, uint64_t tid);
void gtp_tunnel_set_i_tei(struct gtp_tunnel *t, uint32_t i_tei);

View File

@ -1,10 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _UAPI_LINUX_GTP_H_
#define _UAPI_LINUX_GTP_H__
#define _UAPI_LINUX_GTP_H_
#define GTP_GENL_MCGRP_NAME "gtp"
enum gtp_genl_cmds {
GTP_CMD_NEWPDP,
GTP_CMD_DELPDP,
GTP_CMD_GETPDP,
GTP_CMD_ECHOREQ,
GTP_CMD_MAX,
};
@ -19,15 +23,19 @@ enum gtp_attrs {
GTPA_LINK,
GTPA_VERSION,
GTPA_TID, /* for GTPv0 only */
GTPA_PEER_ADDRESS,
GTPA_PEER_ADDRESS, /* Remote GSN peer, either SGSN or GGSN */
#define GTPA_SGSN_ADDRESS GTPA_PEER_ADDRESS /* maintain legacy attr name */
GTPA_MS_ADDRESS,
GTPA_FLOW,
GTPA_NET_NS_FD,
GTPA_I_TEI, /* for GTPv1 only */
GTPA_O_TEI, /* for GTPv1 only */
GTPA_PAD,
GTPA_PEER_ADDR6,
GTPA_MS_ADDR6,
GTPA_FAMILY,
__GTPA_MAX,
};
#define GTPA_MAX (__GTPA_MAX + 1)
#define GTPA_MAX (__GTPA_MAX - 1)
#endif /* _UAPI_LINUX_GTP_H_ */

View File

@ -2,7 +2,7 @@ include $(top_srcdir)/Make_global.am
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
LIBVERSION=1:1:1
LIBVERSION=1:2:1
lib_LTLIBRARIES = libgtpnl.la

View File

@ -44,20 +44,55 @@
static void gtp_build_payload(struct nlmsghdr *nlh, struct gtp_tunnel *t)
{
mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version);
if (t->ifns >= 0)
if (t->flags & GTP_TUN_FAMILY)
mnl_attr_put_u8(nlh, GTPA_FAMILY, t->ms_addr.family);
if (t->flags & GTP_TUN_VERSION)
mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version);
if (t->flags & GTP_TUN_IFNS)
mnl_attr_put_u32(nlh, GTPA_NET_NS_FD, t->ifns);
mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx);
if (t->sgsn_addr.s_addr)
mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.s_addr);
if (t->ms_addr.s_addr)
mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.s_addr);
if (t->gtp_version == GTP_V0) {
mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid);
mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid);
} else if (t->gtp_version == GTP_V1) {
mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei);
mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei);
if (t->flags & GTP_TUN_IFIDX)
mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx);
if (t->flags & GTP_TUN_MS_ADDR) {
switch (t->ms_addr.family) {
case AF_INET:
mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.ip4.s_addr);
break;
case AF_INET6:
mnl_attr_put(nlh, GTPA_MS_ADDR6, sizeof(t->ms_addr.ip6), &t->ms_addr.ip6);
break;
default:
/* No addr is set when deleting a tunnel */
break;
}
}
if (t->flags & GTP_TUN_SGSN_ADDR) {
switch (t->sgsn_addr.family) {
case AF_INET:
mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.ip4.s_addr);
break;
case AF_INET6:
mnl_attr_put(nlh, GTPA_PEER_ADDR6, sizeof(t->sgsn_addr.ip6), &t->sgsn_addr.ip6);
break;
default:
/* No addr is set when deleting a tunnel */
break;
}
}
if (t->flags & GTP_TUN_VERSION) {
if (t->gtp_version == GTP_V0) {
if (t->flags & GTP_TUN_V0_TID)
mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid);
if (t->flags & GTP_TUN_V0_FLOWID)
mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid);
} else if (t->gtp_version == GTP_V1) {
if (t->flags & GTP_TUN_V1_I_TEI)
mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei);
if (t->flags & GTP_TUN_V1_O_TEI)
mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei);
}
}
}
@ -77,8 +112,10 @@ int gtp_add_tunnel(int genl_id, struct mnl_socket *nl, struct gtp_tunnel *t)
GTP_CMD_NEWPDP);
gtp_build_payload(nlh, t);
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) {
perror("genl_socket_talk");
return -1;
}
return 0;
}
@ -94,14 +131,17 @@ int gtp_del_tunnel(int genl_id, struct mnl_socket *nl, struct gtp_tunnel *t)
GTP_CMD_DELPDP);
gtp_build_payload(nlh, t);
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) {
perror("genl_socket_talk");
return -1;
}
return 0;
}
EXPORT_SYMBOL(gtp_del_tunnel);
struct gtp_pdp {
uint32_t ifidx;
uint32_t version;
union {
struct {
@ -112,8 +152,8 @@ struct gtp_pdp {
uint32_t o_tei;
} v1;
} u;
struct in_addr sgsn_addr;
struct in_addr ms_addr;
struct gtp_addr sgsn_addr;
struct gtp_addr ms_addr;
};
static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
@ -125,6 +165,12 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
switch(type) {
case GTPA_FAMILY:
if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case GTPA_TID:
if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
perror("mnl_attr_validate");
@ -136,11 +182,20 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
case GTPA_PEER_ADDRESS:
case GTPA_MS_ADDRESS:
case GTPA_VERSION:
case GTPA_LINK:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case GTPA_PEER_ADDR6:
case GTPA_MS_ADDR6:
if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
sizeof(struct in6_addr)) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
default:
break;
}
@ -151,39 +206,65 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
static int genl_gtp_attr_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[GTPA_MAX + 1] = {};
char buf[INET_ADDRSTRLEN];
char buf[INET6_ADDRSTRLEN];
struct gtp_pdp pdp = {};
struct genlmsghdr *genl;
mnl_attr_parse(nlh, sizeof(*genl), genl_gtp_validate_cb, tb);
if (tb[GTPA_LINK])
pdp.ifidx = mnl_attr_get_u32(tb[GTPA_LINK]);
if (tb[GTPA_TID])
pdp.u.v0.tid = mnl_attr_get_u64(tb[GTPA_TID]);
if (tb[GTPA_I_TEI])
pdp.u.v1.i_tei = mnl_attr_get_u32(tb[GTPA_I_TEI]);
if (tb[GTPA_O_TEI])
pdp.u.v1.o_tei = mnl_attr_get_u32(tb[GTPA_O_TEI]);
if (tb[GTPA_PEER_ADDRESS]) {
pdp.sgsn_addr.s_addr =
mnl_attr_get_u32(tb[GTPA_PEER_ADDRESS]);
}
if (tb[GTPA_MS_ADDRESS]) {
pdp.ms_addr.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]);
}
if (tb[GTPA_VERSION]) {
pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
pdp.sgsn_addr.family = AF_INET;
pdp.sgsn_addr.ip4.s_addr = mnl_attr_get_u32(tb[GTPA_PEER_ADDRESS]);
} else if (tb[GTPA_PEER_ADDR6]) {
pdp.sgsn_addr.family = AF_INET6;
memcpy(&pdp.sgsn_addr.ip6, mnl_attr_get_payload(tb[GTPA_PEER_ADDR6]),
sizeof(struct in6_addr));
} else {
fprintf(stderr, "sgsn_addr: no IPv4 nor IPv6 set\n");
return MNL_CB_ERROR;
}
printf("version %u ", pdp.version);
if (pdp.version == GTP_V0) {
inet_ntop(AF_INET, &pdp.ms_addr, buf, sizeof(buf));
printf("tid %"PRIu64" ms_addr %s ",
pdp.u.v0.tid, buf);
} else if (pdp.version == GTP_V1) {
inet_ntop(AF_INET, &pdp.ms_addr, buf, sizeof(buf));
printf("tei %u/%u ms_addr %s ", pdp.u.v1.i_tei,
pdp.u.v1.o_tei, buf);
if (tb[GTPA_MS_ADDRESS]) {
pdp.ms_addr.family = AF_INET;
pdp.ms_addr.ip4.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]);
} else if (tb[GTPA_MS_ADDR6]) {
pdp.ms_addr.family = AF_INET6;
memcpy(&pdp.ms_addr.ip6, mnl_attr_get_payload(tb[GTPA_MS_ADDR6]), sizeof(struct in6_addr));
} else {
fprintf(stderr, "ms_addr: no IPv4 nor IPv6 set\n");
return MNL_CB_ERROR;
}
inet_ntop(AF_INET, &pdp.sgsn_addr, buf, sizeof(buf));
if (tb[GTPA_FAMILY] && mnl_attr_get_u32(tb[GTPA_FAMILY]) != pdp.ms_addr.family) {
fprintf(stderr, "ms_addr family does not match GTPA_FAMILY\n");
return MNL_CB_ERROR;
}
printf("%s ", if_indextoname(pdp.ifidx, buf));
if (tb[GTPA_VERSION])
pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
printf("version %u ", pdp.version);
if (pdp.version == GTP_V0)
printf("tid %"PRIu64" ", pdp.u.v0.tid);
else if (pdp.version == GTP_V1)
printf("tei %u/%u ", pdp.u.v1.i_tei, pdp.u.v1.o_tei);
inet_ntop(pdp.ms_addr.family, &pdp.ms_addr.ip4, buf, sizeof(buf));
printf("ms_addr %s ", buf);
inet_ntop(pdp.sgsn_addr.family, &pdp.sgsn_addr.ip4, buf, sizeof(buf));
printf("sgsn_addr %s\n", buf);
return MNL_CB_OK;
@ -200,7 +281,7 @@ int gtp_list_tunnel(int genl_id, struct mnl_socket *nl)
if (genl_socket_talk(nl, nlh, seq, genl_gtp_attr_cb, NULL) < 0) {
perror("genl_socket_talk");
return 0;
return -1;
}
return 0;

View File

@ -49,54 +49,88 @@ EXPORT_SYMBOL(gtp_tunnel_free);
void gtp_tunnel_set_ifns(struct gtp_tunnel *t, int ifns)
{
t->ifns = ifns;
t->flags |= GTP_TUN_IFNS;
}
EXPORT_SYMBOL(gtp_tunnel_set_ifns);
void gtp_tunnel_set_ifidx(struct gtp_tunnel *t, uint32_t ifidx)
{
t->ifidx = ifidx;
t->flags |= GTP_TUN_IFIDX;
}
EXPORT_SYMBOL(gtp_tunnel_set_ifidx);
void gtp_tunnel_set_family(struct gtp_tunnel *t, uint16_t family)
{
t->ms_addr.family = family;
t->flags |= GTP_TUN_FAMILY;
}
EXPORT_SYMBOL(gtp_tunnel_set_family);
void gtp_tunnel_set_ms_ip4(struct gtp_tunnel *t, struct in_addr *ms_addr)
{
t->ms_addr = *ms_addr;
t->ms_addr.family = AF_INET;
t->ms_addr.ip4 = *ms_addr;
t->flags |= GTP_TUN_FAMILY | GTP_TUN_MS_ADDR;
}
EXPORT_SYMBOL(gtp_tunnel_set_ms_ip4);
void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr)
{
t->sgsn_addr = *sgsn_addr;
t->sgsn_addr.family = AF_INET;
t->sgsn_addr.ip4 = *sgsn_addr;
t->flags |= GTP_TUN_SGSN_ADDR;
}
EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip4);
void gtp_tunnel_set_ms_ip6(struct gtp_tunnel *t, const struct in6_addr *ms_addr)
{
t->ms_addr.family = AF_INET6;
t->ms_addr.ip6 = *ms_addr;
t->flags |= GTP_TUN_FAMILY | GTP_TUN_MS_ADDR;
}
EXPORT_SYMBOL(gtp_tunnel_set_ms_ip6);
void gtp_tunnel_set_sgsn_ip6(struct gtp_tunnel *t, const struct in6_addr *sgsn_addr)
{
t->sgsn_addr.family = AF_INET6;
t->sgsn_addr.ip6 = *sgsn_addr;
t->flags |= GTP_TUN_SGSN_ADDR;
}
EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip6);
void gtp_tunnel_set_version(struct gtp_tunnel *t, uint32_t version)
{
t->gtp_version = version;
t->flags |= GTP_TUN_VERSION;
}
EXPORT_SYMBOL(gtp_tunnel_set_version);
void gtp_tunnel_set_tid(struct gtp_tunnel *t, uint64_t tid)
{
t->u.v0.tid = tid;
t->flags |= GTP_TUN_V0_TID;
}
EXPORT_SYMBOL(gtp_tunnel_set_tid);
void gtp_tunnel_set_flowid(struct gtp_tunnel *t, uint16_t flowid)
{
t->u.v0.flowid = flowid;
t->flags |= GTP_TUN_V0_FLOWID;
}
EXPORT_SYMBOL(gtp_tunnel_set_flowid);
void gtp_tunnel_set_i_tei(struct gtp_tunnel *t, uint32_t i_tei)
{
t->u.v1.i_tei = i_tei;
t->flags |= GTP_TUN_V1_I_TEI;
}
EXPORT_SYMBOL(gtp_tunnel_set_i_tei);
void gtp_tunnel_set_o_tei(struct gtp_tunnel *t, uint32_t o_tei)
{
t->u.v1.o_tei = o_tei;
t->flags |= GTP_TUN_V1_O_TEI;
}
EXPORT_SYMBOL(gtp_tunnel_set_o_tei);
@ -114,13 +148,13 @@ EXPORT_SYMBOL(gtp_tunnel_get_ifidx);
const struct in_addr *gtp_tunnel_get_ms_ip4(struct gtp_tunnel *t)
{
return &t->ms_addr;
return &t->ms_addr.ip4;
}
EXPORT_SYMBOL(gtp_tunnel_get_ms_ip4);
const struct in_addr *gtp_tunnel_get_sgsn_ip4(struct gtp_tunnel *t)
{
return &t->sgsn_addr;
return &t->sgsn_addr.ip4;
}
EXPORT_SYMBOL(gtp_tunnel_get_sgsn_ip4);

View File

@ -12,11 +12,32 @@
#include <stdint.h>
#include <netinet/in.h>
struct gtp_addr {
sa_family_t family;
union {
struct in_addr ip4;
struct in6_addr ip6;
};
};
enum {
GTP_TUN_IFNS = (1 << 0),
GTP_TUN_IFIDX = (1 << 1),
GTP_TUN_FAMILY = (1 << 2),
GTP_TUN_MS_ADDR = (1 << 3),
GTP_TUN_SGSN_ADDR = (1 << 4),
GTP_TUN_VERSION = (1 << 5),
GTP_TUN_V0_TID = (1 << 6),
GTP_TUN_V0_FLOWID = (1 << 7),
GTP_TUN_V1_I_TEI = (1 << 8),
GTP_TUN_V1_O_TEI = (1 << 9),
};
struct gtp_tunnel {
int ifns;
uint32_t ifidx;
struct in_addr ms_addr;
struct in_addr sgsn_addr;
struct gtp_addr ms_addr;
struct gtp_addr sgsn_addr;
int gtp_version;
union {
struct {
@ -28,6 +49,7 @@ struct gtp_tunnel {
uint32_t o_tei;
} v1;
} u;
uint32_t flags;
};
#endif

View File

@ -38,3 +38,12 @@ global:
local: *;
};
LIBGTPNL_1.1 {
gtp_tunnel_set_ms_ip6;
gtp_tunnel_set_sgsn_ip6;
} LIBGTPNL_1.0;
LIBGTPNL_1.2 {
gtp_tunnel_set_family;
} LIBGTPNL_1.1;

15
tests/Makefile.am Normal file
View File

@ -0,0 +1,15 @@
check-local:
$(MAKE) qemu-tests
if ENABLE_QEMU_TESTS
qemu-download-kernel:
rm -f qemu/_linux
wget -O qemu/_linux \
https://jenkins.osmocom.org/jenkins/job/ttcn3-ggsn-test-kernel-latest-net-next/ws/_cache/kernel-test/linux
qemu-tests:
qemu/initrd-build.sh
qemu/run-qemu.sh
else
qemu-tests:
@echo "Not running QEMU tests (determined at configure-time)"
endif

View File

@ -0,0 +1,123 @@
#!/bin/sh -ex
# Use ip from iproute2 instead of busybox ip, because iproute2's version has
# "ip netns" implemented. Calling /bin/ip explicitly is needed here, otherwise
# busybox sh will use busybox ip, regardless of PATH.
alias ip="/bin/ip"
alias ggsn_side="ip netns exec ggsn_side"
# MS - SGSN -gtp- GGSN - WEBSERVER
tunnel_start() {
test -n "$MS_PROTO"
test -n "$MS"
test -n "$MS_PREFLEN"
test -n "$SGSN_GGSN_PROTO"
test -n "$SGSN"
test -n "$SGSN_PREFLEN"
test -n "$GGSN"
test -n "$WEBSERVER"
ip netns add ggsn_side
# SGSN side: prepare veth_sgsn (SGSN), lo (MS)
ip link add veth_sgsn type veth peer name veth_ggsn
ip addr add "$SGSN"/"$SGSN_PREFLEN" dev veth_sgsn
ip link set veth_sgsn up
ip addr add "$MS"/"$MS_PREFLEN" dev lo
ip link set lo up
# SGSN side: prepare gtp-tunnel
gtp-link add gtp_sgsn "$SGSN_GGSN_PROTO" --sgsn &
sleep 1
gtp-tunnel add gtp_sgsn v1 200 100 "$MS" "$GGSN"
ip route add "$WEBSERVER"/"$MS_PREFLEN" dev gtp_sgsn
# GGSN side: prepare veth_ggsn (GGSN), lo (WEBSERVER)
ip link set veth_ggsn netns ggsn_side
ggsn_side ip addr add "$GGSN"/"$SGSN_PREFLEN" dev veth_ggsn
ggsn_side ip link set veth_ggsn up
ggsn_side ip addr add "$WEBSERVER"/"$MS_PREFLEN" dev lo
ggsn_side ip link set lo up
# GGSN side: prepare gtp-tunnel
ggsn_side gtp-link add gtp_ggsn "$SGSN_GGSN_PROTO" &
sleep 1
ggsn_side gtp-tunnel add gtp_ggsn v1 100 200 "$MS" "$SGSN"
ggsn_side ip route add "$MS"/"$MS_PREFLEN" dev gtp_ggsn
# List tunnel from both sides
gtp-tunnel list
ggsn_side gtp-tunnel list
}
# Add a second tunnel to test MS with IPv4v6
tunnel_start_2() {
test -n "$MS2_PROTO"
test -n "$MS2"
test -n "$MS2_PREFLEN"
test -n "$WEBSERVER2"
# SGSN side: add second IP to lo (MS)
ip addr add "$MS2"/"$MS2_PREFLEN" dev lo
# SGSN side: prepare second gtp-tunnel
gtp-tunnel add gtp_sgsn v1 200 100 "$MS2" "$GGSN"
ip route add "$WEBSERVER2"/"$MS2_PREFLEN" dev gtp_sgsn
# GGSN side: add second IP to lo (WEBSERVER)
ggsn_side ip addr add "$WEBSERVER2"/"$MS2_PREFLEN" dev lo
# GGSN side: prepare second gtp-tunnel
ggsn_side gtp-tunnel add gtp_ggsn v1 100 200 "$MS2" "$SGSN"
ggsn_side ip route add "$MS2"/"$MS2_PREFLEN" dev gtp_ggsn
# List tunnels from both sides
gtp-tunnel list
ggsn_side gtp-tunnel list
}
tunnel_ping() {
ip addr show
ping -c 1 "$WEBSERVER"
ggsn_side ping -c 1 "$MS"
if [ -n "$MS2" ]; then
ping -c 1 "$WEBSERVER2"
ggsn_side ping -c 1 "$MS2"
fi
}
tunnel_stop() {
killall gtp-link
ip addr del "$MS"/"$MS_PREFLEN" dev lo
if [ -n "$MS2" ]; then
ip addr del "$MS2"/"$MS2_PREFLEN" dev lo
fi
ip link set veth_sgsn down
if [ "$SGSN_GGSN_PROTO" == "ip" ]; then # FIXME: doesn't work with ip6
ip addr del "$SGSN"/"$SGSN_PREFLEN" dev veth_sgsn
fi
ip link del veth_sgsn
ip route del "$WEBSERVER"/"$MS_PREFLEN" dev gtp_sgsn
gtp-tunnel delete gtp_sgsn v1 200 "$MS_PROTO"
if [ -n "$MS2" ]; then
ip route del "$WEBSERVER2"/"$MS2_PREFLEN" dev gtp_sgsn
gtp-tunnel delete gtp_sgsn v1 200 "$MS2_PROTO"
fi
gtp-link del gtp_sgsn
ggsn_side gtp-tunnel delete gtp_ggsn v1 100 "$MS_PROTO"
if [ -n "$MS2" ]; then
ggsn_side gtp-tunnel delete gtp_ggsn v1 100 "$MS2_PROTO"
fi
ggsn_side gtp-link del gtp_ggsn
ip netns del ggsn_side
}

View File

@ -0,0 +1,15 @@
#!/bin/sh -ex
. /tests/00_test_functions.sh
MS_PROTO="ip"
MS="172.99.0.1"
MS_PREFLEN="32"
SGSN_GGSN_PROTO="ip"
SGSN="172.0.0.1"
SGSN_PREFLEN="24"
GGSN="172.0.0.2"
WEBSERVER="172.99.0.2"
tunnel_start
tunnel_ping
tunnel_stop

View File

@ -0,0 +1,15 @@
#!/bin/sh -ex
. /tests/00_test_functions.sh
MS_PROTO="ip"
MS="172.99.0.1"
MS_PREFLEN="32"
SGSN_GGSN_PROTO="ip6"
SGSN="fd00::1"
SGSN_PREFLEN="7"
GGSN="fd00::2"
WEBSERVER="172.99.0.2"
tunnel_start
tunnel_ping
tunnel_stop

View File

@ -0,0 +1,15 @@
#!/bin/sh -ex
. /tests/00_test_functions.sh
MS_PROTO="ip6"
MS="fd00::"
MS_PREFLEN="64"
SGSN_GGSN_PROTO="ip"
SGSN="172.0.0.1"
SGSN_PREFLEN="24"
GGSN="172.0.0.2"
WEBSERVER="fe00::2"
tunnel_start
tunnel_ping
tunnel_stop

View File

@ -0,0 +1,15 @@
#!/bin/sh -ex
. /tests/00_test_functions.sh
MS_PROTO="ip6"
MS="fc00::"
MS_PREFLEN="64"
SGSN_GGSN_PROTO="ip6"
SGSN="fd00::1"
SGSN_PREFLEN="7"
GGSN="fd00::2"
WEBSERVER="fe00::2"
tunnel_start
tunnel_ping
tunnel_stop

View File

@ -0,0 +1,22 @@
#!/bin/sh -ex
. /tests/00_test_functions.sh
MS_PROTO="ip"
MS="172.99.0.1"
MS_PREFLEN="32"
SGSN_GGSN_PROTO="ip"
SGSN="172.0.0.1"
SGSN_PREFLEN="24"
GGSN="172.0.0.2"
WEBSERVER="172.99.0.2"
tunnel_start
MS2_PROTO="ip6"
MS2="fd00::"
MS2_PREFLEN="64"
WEBSERVER2="fe00::2"
tunnel_start_2
tunnel_ping
tunnel_stop

36
tests/qemu/README.md Normal file
View File

@ -0,0 +1,36 @@
# QEMU tests for libgtpnl
The tests simulate how a GGSN would use libgtpnl, to set up a GTP tunnel
between SGSN and GGSN, so a MS on the SGSN side can talk to a webserver on the
GGSN side.
## Running the tests
```
$ autoreconf -fi
$ ./configure --enable-qemu-tests
$ make
$ make -C tests qemu-download-kernel # or build your own, see below
$ make check
```
## Building your own kernel
Clone a kernel tree, then:
```
$ make defconfig
$ make menuconfig
```
Set the following options:
```
CONFIG_GTP=y
CONFIG_NET_NS=y
CONFIG_VETH=y
```
Build the kernel and copy it to the tests dir:
```
$ make -j$(nproc)
$ cp arch/x86/boot/bzImage /path/to/libgtpnl/tests/qemu/_linux
```

19
tests/qemu/check-depends.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
RET=0
require_program() {
if [ -z "$(command -v "$1")" ]; then
RET=1
echo "ERROR: missing program: $1"
fi
}
require_program busybox
require_program cpio
require_program find
require_program gzip
require_program ip
require_program lddtree
require_program qemu-system-x86_64
exit "$RET"

110
tests/qemu/initrd-build.sh Executable file
View File

@ -0,0 +1,110 @@
#!/bin/sh -e
DIR="$(cd "$(dirname "$0")" && pwd)"
DIR_INITRD="$DIR/_initrd"
SRC_LIBS="$(realpath "$DIR/../../src/.libs/")"
TOOLS_LIBS="$(realpath "$DIR/../../tools/.libs/")"
# Add one or more files to the initramfs, with parent directories.
# usr-merge: resolve symlinks for /lib -> /usr/lib etc. so "cp --parents" does
# not fail with "cp: cannot make directory '/tmp/initrd/lib': File exists"
# $@: path to files
initrd_add_file() {
local i
for i in "$@"; do
case "$i" in
/bin/*|/sbin/*|/lib/*|/lib64/*)
cp -a --parents "$i" "$DIR_INITRD"/usr
;;
*)
cp -a --parents "$i" "$DIR_INITRD"
;;
esac
done
}
# Add binaries with depending libraries
# $@: paths to binaries
initrd_add_bin() {
local bin
local bin_path
local file
for bin in "$@"; do
local bin_path="$(which "$bin")"
if [ -z "$bin_path" ]; then
echo "ERROR: file not found: $bin"
exit 1
fi
lddtree_out="$(lddtree -l "$bin_path")"
if [ -z "$lddtree_out" ]; then
echo "ERROR: lddtree failed on '$bin_path'"
exit 1
fi
for file in $lddtree_out; do
initrd_add_file "$file"
# Copy resolved symlink
if [ -L "$file" ]; then
initrd_add_file "$(realpath "$file")"
fi
done
done
}
# Add command to run inside the initramfs
# $@: commands
initrd_add_cmd() {
local i
if ! [ -e "$DIR_INITRD"/cmd.sh ]; then
echo "#!/bin/sh -ex" > "$DIR_INITRD"/cmd.sh
chmod +x "$DIR_INITRD"/cmd.sh
fi
for i in "$@"; do
echo "$i" >> "$DIR_INITRD"/cmd.sh
done
}
rm -rf "$DIR_INITRD"
mkdir -p "$DIR_INITRD"
cd "$DIR_INITRD"
for dir in bin sbin lib lib64; do
ln -s usr/"$dir" "$dir"
done
mkdir -p \
dev/net \
proc \
run \
sys \
tmp \
usr/bin \
usr/sbin
initrd_add_bin \
busybox \
ip
initrd_add_cmd \
"export LD_LIBRARY_PATH=$SRC_LIBS:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="$SRC_LIBS:$LD_LIBRARY_PATH"
for i in gtp-link gtp-tunnel; do
initrd_add_bin "$TOOLS_LIBS"/"$i"
ln -s "$TOOLS_LIBS"/"$i" usr/bin/"$i"
done
mkdir tests
cp "$DIR"/*.sh tests
cp "$DIR"/initrd-init.sh init
find . -print0 \
| cpio --quiet -o -0 -H newc \
| gzip -1 > "$DIR"/_initrd.gz

36
tests/qemu/initrd-init.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/busybox sh
echo "Running initrd-init.sh"
set -x
run_test() {
echo
echo "QEMU test: $1"
echo
if ! sh -ex "/tests/$1"; then
poweroff -f
fi
}
export HOME=/root
export LD_LIBRARY_PATH=/usr/local/lib
export PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/local/sbin:/usr/sbin
export TERM=screen
/bin/busybox --install -s
hostname qemu
mount -t proc proc /proc
mount -t sysfs sys /sys
mknod /dev/null c 1 3
. /cmd.sh
set +x
# Run all tests
run_test 01_ms_ip4_sgsn_ip4.sh
run_test 02_ms_ip4_sgsn_ip6.sh
run_test 03_ms_ip6_sgsn_ip4.sh
run_test 04_ms_ip6_sgsn_ip6.sh
run_test 05_ms_ip46_sgsn_ip4.sh
# Success (run-qemu.sh checks for this line)
echo "QEMU_TEST_SUCCESSFUL"
poweroff -f

51
tests/qemu/run-qemu.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/sh -e
DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -e /dev/kvm ]; then
MACHINE_ARG="-machine pc,accel=kvm"
else
echo "WARNING: /dev/kvm not found, emulation will be slower"
MACHINE_ARG="-machine pc"
fi
if ! [ -e "$DIR"/_linux ]; then
echo "ERROR: linux kernel not found: $DIR/_linux"
echo "Put a kernel there, either download it from the Osmocom jenkins:"
echo "$ make -C tests qemu-download-kernel"
echo
echo "Or build your own kernel. Make sure to set:"
echo " CONFIG_GTP=y"
echo " CONFIG_NET_NS=y"
echo " CONFIG_VETH=y"
exit 1
fi
KERNEL_CMDLINE="root=/dev/ram0 console=ttyS0 panic=-1 init=/init"
set -x
qemu-system-x86_64 \
$MACHINE_ARG \
-smp 1 \
-m 512M \
-no-user-config -nodefaults -display none \
-gdb unix:"$DIR"/_gdb.pipe,server=on,wait=off \
-no-reboot \
-kernel "$DIR"/_linux \
-initrd "$DIR"/_initrd.gz \
-append "${KERNEL_CMDLINE}" \
-serial stdio \
-chardev socket,id=charserial1,path="$DIR"/_gdb-serial.pipe,server=on,wait=off \
-device isa-serial,chardev=charserial1,id=serial1 \
2>&1 | tee "$DIR/_output"
set +x
if grep -q "QEMU_TEST_SUCCESSFUL" "$DIR/_output"; then
echo
echo "QEMU tests: successful"
echo
else
echo
echo "QEMU tests: failed"
echo
exit 1
fi

View File

@ -1,6 +1,6 @@
include $(top_srcdir)/Make_global.am
noinst_PROGRAMS = gtp-link \
bin_PROGRAMS = gtp-link \
gtp-tunnel
gtp_link_SOURCES = gtp-link.c

View File

@ -39,14 +39,106 @@
#include <linux/if_link.h>
#include <libgtpnl/gtpnl.h>
#include <errno.h>
struct gtp_server_sock {
int family;
int fd1;
int fd2;
socklen_t len;
struct {
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} fd1;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} fd2;
} sockaddr;
union {
struct in_addr v4;
struct in6_addr v6;
} addr;
};
static void setup_sockaddr_in(struct sockaddr_in *sockaddr, struct in_addr *in,
uint16_t port)
{
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons(port);
sockaddr->sin_addr = *in;
}
static void setup_sockaddr_in6(struct sockaddr_in6 *sockaddr, struct in6_addr *in6,
uint16_t port)
{
sockaddr->sin6_family = AF_INET6;
sockaddr->sin6_port = htons(port);
sockaddr->sin6_addr = *in6;
}
static int setup_socket(struct gtp_server_sock *gtp_sock, int family)
{
int one = 1;
gtp_sock->fd1 = socket(family, SOCK_DGRAM, 0);
gtp_sock->fd2 = socket(family, SOCK_DGRAM, 0);
if (gtp_sock->fd1 < 0 || gtp_sock->fd2 < 0)
return -1;
gtp_sock->family = family;
switch (family) {
case AF_INET:
gtp_sock->len = sizeof(struct sockaddr_in);
setup_sockaddr_in(&gtp_sock->sockaddr.fd1.in,
&gtp_sock->addr.v4, 3386);
setup_sockaddr_in(&gtp_sock->sockaddr.fd2.in,
&gtp_sock->addr.v4, 2152);
break;
case AF_INET6:
gtp_sock->len = sizeof(struct sockaddr_in6);
setup_sockaddr_in6(&gtp_sock->sockaddr.fd1.in6,
&gtp_sock->addr.v6, 3386);
setup_sockaddr_in6(&gtp_sock->sockaddr.fd2.in6,
&gtp_sock->addr.v6, 2152);
if (setsockopt(gtp_sock->fd1, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0)
perror("setsockopt IPV6_V6ONLY: ");
if (setsockopt(gtp_sock->fd2, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0)
perror("setsockopt IPV6_V6ONLY: ");
break;
}
return 0;
}
static void close_socket(struct gtp_server_sock *gtp_sock)
{
if (gtp_sock->fd1 != -1) {
close(gtp_sock->fd1);
gtp_sock->fd1 = -1;
}
if (gtp_sock->fd2 != -1) {
close(gtp_sock->fd2);
gtp_sock->fd2 = -1;
}
}
int main(int argc, char *argv[])
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int ret, sgsn_mode = 0;
struct gtp_server_sock gtp_sock;
int ret, sgsn_mode = 0, family;
const char *addr = NULL;
memset(&gtp_sock, 0, sizeof(gtp_sock));
if (argc < 3) {
printf("Usage: %s <add|del> <device>\n", argv[0]);
printf("Usage: %s add <device> <family> [--sgsn] [<address>]\n", argv[0]);
printf(" %s del <device>\n", argv[0]);
exit(EXIT_FAILURE);
}
@ -56,45 +148,78 @@ int main(int argc, char *argv[])
perror("gtp_dev_destroy");
return 0;
} else if (!strcmp(argv[1], "add")) {
if (argc < 4) {
printf("Usage: %s add <device> <family> [--sgsn] [<address>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 5) {
if (!strcmp(argv[4], "--sgsn"))
sgsn_mode = 1;
else
addr = argv[4];
}
if (argc == 6) {
if (!strcmp(argv[4], "--sgsn"))
sgsn_mode = 1;
addr = argv[5];
}
}
if (argc > 3 && !strcmp(argv[3], "--sgsn"))
sgsn_mode = 1;
int fd1 = socket(AF_INET, SOCK_DGRAM, 0);
int fd2 = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in sockaddr_fd1 = {
.sin_family = AF_INET,
.sin_port = htons(3386),
.sin_addr = {
.s_addr = INADDR_ANY,
},
};
struct sockaddr_in sockaddr_fd2 = {
.sin_family = AF_INET,
.sin_port = htons(2152),
.sin_addr = {
.s_addr = INADDR_ANY,
},
};
if (bind(fd1, (struct sockaddr *) &sockaddr_fd1,
sizeof(sockaddr_fd1)) < 0) {
perror("bind");
if (!strcmp(argv[3], "ip"))
family = AF_INET;
else if (!strcmp(argv[3], "ip6"))
family = AF_INET6;
else {
fprintf(stderr, "unsupport family `%s', expecting `ip' or `ip6'\n",
argv[3]);
exit(EXIT_FAILURE);
}
if (bind(fd2, (struct sockaddr *) &sockaddr_fd2,
sizeof(sockaddr_fd2)) < 0) {
if (addr) {
if (!inet_pton(family, addr, &gtp_sock.addr)) {
fprintf(stderr, "invalid listener address: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
} else {
switch (family) {
case AF_INET:
gtp_sock.addr.v4.s_addr = INADDR_ANY;
break;
case AF_INET6:
gtp_sock.addr.v6 = in6addr_any;
break;
}
}
if (setup_socket(&gtp_sock, family) < 0) {
perror("socket");
close_socket(&gtp_sock);
exit(EXIT_FAILURE);
}
if (bind(gtp_sock.fd1, (struct sockaddr *) &gtp_sock.sockaddr.fd1, gtp_sock.len) < 0) {
perror("bind");
close_socket(&gtp_sock);
exit(EXIT_FAILURE);
}
if (bind(gtp_sock.fd2, (struct sockaddr *) &gtp_sock.sockaddr.fd2, gtp_sock.len) < 0) {
perror("bind");
close_socket(&gtp_sock);
exit(EXIT_FAILURE);
}
if (sgsn_mode)
ret = gtp_dev_create_sgsn(-1, argv[2], fd1, fd2);
ret = gtp_dev_create_sgsn(-1, argv[2], gtp_sock.fd1, gtp_sock.fd2);
else
ret = gtp_dev_create(-1, argv[2], fd1, fd2);
ret = gtp_dev_create(-1, argv[2], gtp_sock.fd1, gtp_sock.fd2);
if (ret < 0) {
perror("cannot create GTP device\n");
close_socket(&gtp_sock);
exit(EXIT_FAILURE);
}
@ -102,11 +227,13 @@ int main(int argc, char *argv[])
"this process running for testing purposes.\n");
while (1) {
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
union {
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
} sock;
ret = recvfrom(fd1, buf, sizeof(buf), 0,
(struct sockaddr *)&addr, &len);
ret = recvfrom(gtp_sock.fd1, buf, sizeof(buf), 0,
(struct sockaddr *)&sock, &gtp_sock.len);
printf("received %d bytes via UDP socket\n", ret);
}

View File

@ -49,14 +49,35 @@ static void add_usage(const char *name)
name);
}
static void set_addr(const char *addr, bool is_ms, struct gtp_tunnel *t)
{
struct in_addr ip4;
struct in6_addr ip6;
if (inet_pton(AF_INET, addr, &ip4) == 1) {
if (is_ms)
return gtp_tunnel_set_ms_ip4(t, &ip4);
return gtp_tunnel_set_sgsn_ip4(t, &ip4);
}
if (inet_pton(AF_INET6, addr, &ip6) == 1) {
if (is_ms)
return gtp_tunnel_set_ms_ip6(t, &ip6);
return gtp_tunnel_set_sgsn_ip6(t, &ip6);
}
fprintf(stderr, "bad address: %s\n", addr);
exit(EXIT_FAILURE);
}
static int
add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
{
struct gtp_tunnel *t;
uint32_t gtp_ifidx;
struct in_addr ms, sgsn;
uint32_t gtp_version;
int optidx;
int ret;
if (argc < 7 || argc > 8) {
add_usage(argv[0]);
@ -88,29 +109,21 @@ add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
optidx++;
if (gtp_version == GTP_V0)
if (gtp_version == GTP_V0) {
gtp_tunnel_set_tid(t, atoi(argv[optidx++]));
else if (gtp_version == GTP_V1) {
gtp_tunnel_set_flowid(t, 0);
} else if (gtp_version == GTP_V1) {
gtp_tunnel_set_i_tei(t, atoi(argv[optidx++]));
gtp_tunnel_set_o_tei(t, atoi(argv[optidx++]));
}
if (inet_aton(argv[optidx++], &ms) < 0) {
perror("bad address for ms");
exit(EXIT_FAILURE);
}
gtp_tunnel_set_ms_ip4(t, &ms);
set_addr(argv[optidx++], true, t);
set_addr(argv[optidx++], false, t);
if (inet_aton(argv[optidx++], &sgsn) < 0) {
perror("bad address for sgsn");
exit(EXIT_FAILURE);
}
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
gtp_add_tunnel(genl_id, nl, t);
ret = gtp_add_tunnel(genl_id, nl, t);
gtp_tunnel_free(t);
return 0;
return ret;
}
static int
@ -118,9 +131,10 @@ del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
{
struct gtp_tunnel *t;
uint32_t gtp_ifidx;
int ret;
if (argc != 5) {
printf("%s add <gtp device> <version> <tid>\n",
if (argc != 6) {
printf("%s del <gtp device> <version> <tid|i_tei> <family>\n",
argv[0]);
return EXIT_FAILURE;
}
@ -130,6 +144,7 @@ del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
gtp_ifidx = if_nametoindex(argv[2]);
if (gtp_ifidx == 0) {
fprintf(stderr, "wrong GTP interface %s\n", argv[2]);
gtp_tunnel_free(t);
return EXIT_FAILURE;
}
gtp_tunnel_set_ifidx(t, gtp_ifidx);
@ -143,13 +158,24 @@ del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
} else {
fprintf(stderr, "wrong GTP version %s, use v0 or v1\n",
argv[3]);
gtp_tunnel_free(t);
return EXIT_FAILURE;
}
gtp_del_tunnel(genl_id, nl, t);
if (strcmp(argv[5], "ip") == 0) {
gtp_tunnel_set_family(t, AF_INET);
} else if (strcmp(argv[5], "ip6") == 0) {
gtp_tunnel_set_family(t, AF_INET6);
} else {
fprintf(stderr, "wrong family %s, use ip or ip6\n", argv[5]);
gtp_tunnel_free(t);
return EXIT_FAILURE;
}
ret = gtp_del_tunnel(genl_id, nl, t);
gtp_tunnel_free(t);
return 0;
return ret;
}
struct gtp_pdp {