mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn.git
synced 2025-11-06 15:13:23 +00:00
Compare commits
181 Commits
keith/defa
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88d65840aa | ||
|
|
3aa81c594d | ||
|
|
5a97e87eed | ||
|
|
d46d0cc368 | ||
|
|
a564344d54 | ||
|
|
2d4cc5ad4c | ||
|
|
4407fc32ae | ||
|
|
1e1a66a8f7 | ||
|
|
90309f958a | ||
|
|
34cb772bb4 | ||
|
|
5f64814986 | ||
|
|
6cd61eed5c | ||
|
|
4bcb2f72e4 | ||
|
|
ba3d8905f9 | ||
|
|
cf65292b0d | ||
|
|
71fefdad09 | ||
|
|
4b8ebcd784 | ||
|
|
446dd65de1 | ||
|
|
09142e1c27 | ||
|
|
04f5ea4ffa | ||
|
|
839c2b9c84 | ||
|
|
65e133b42c | ||
|
|
95746b9588 | ||
|
|
2b161df6af | ||
|
|
13d23077d2 | ||
|
|
a4cb3eb011 | ||
|
|
9f1fd42148 | ||
|
|
ad03073219 | ||
|
|
234cd12ea5 | ||
|
|
d73801ebea | ||
|
|
1d7e86ae48 | ||
|
|
56a6e025d3 | ||
|
|
c075d032b9 | ||
|
|
d44bf6fd73 | ||
|
|
77d4ae0470 | ||
|
|
e6feda5185 | ||
|
|
f92d875119 | ||
|
|
454110a007 | ||
|
|
38b607ece3 | ||
|
|
41bec9529f | ||
|
|
6041554cef | ||
|
|
6d1d181403 | ||
|
|
7d80216717 | ||
|
|
de385e0253 | ||
|
|
ce691d8e64 | ||
|
|
8c015bd89a | ||
|
|
5f8960b206 | ||
|
|
0828b2afc3 | ||
|
|
70a1d64e55 | ||
|
|
ea3cf1b8ae | ||
|
|
7ce14f5e93 | ||
|
|
561a9bc77c | ||
|
|
7ae1177e04 | ||
|
|
57585767dc | ||
|
|
488972b442 | ||
|
|
08239ccac3 | ||
|
|
fbc56063c5 | ||
|
|
c1598e0eb4 | ||
|
|
519a2e401d | ||
|
|
fbef527222 | ||
|
|
68f5b086ad | ||
|
|
3cb3423a59 | ||
|
|
bad5eeba0f | ||
|
|
1dd16fa12f | ||
|
|
3372625ad9 | ||
|
|
1f9cc2674f | ||
|
|
4abe361f33 | ||
|
|
bb0655d5aa | ||
|
|
4e6fe42731 | ||
|
|
19a506b705 | ||
|
|
ea6c02ac1f | ||
|
|
ec357c5377 | ||
|
|
768d6d5be9 | ||
|
|
fa91a10498 | ||
|
|
6929391ecf | ||
|
|
9baac03927 | ||
|
|
9bd2711f39 | ||
|
|
b17fe7bfe9 | ||
|
|
0917ce4e22 | ||
|
|
2a0d37cb1d | ||
|
|
f3d541e353 | ||
|
|
8d976444b8 | ||
|
|
77734ac81b | ||
|
|
848ec697e2 | ||
|
|
6a2e82542d | ||
|
|
a625bdd136 | ||
|
|
08bb5182a4 | ||
|
|
4963d1c2ea | ||
|
|
37daa5d003 | ||
|
|
c4c4d90b85 | ||
|
|
59f1539ece | ||
|
|
eff88c08e7 | ||
|
|
92ac7249f9 | ||
|
|
5cf6b75dc9 | ||
|
|
4aa2e417c9 | ||
|
|
f14c056310 | ||
|
|
bf69ddbfef | ||
|
|
70a4e2e6f8 | ||
|
|
99afe979ef | ||
|
|
35066fb0b0 | ||
|
|
55fe62f634 | ||
|
|
4fac842826 | ||
|
|
97f60e3dca | ||
|
|
a727e6ed38 | ||
|
|
3a55b89777 | ||
|
|
9f1f747d8e | ||
|
|
b9036af7ca | ||
|
|
724ecc6680 | ||
|
|
0d3bd3435f | ||
|
|
3ed252b58e | ||
|
|
ac802e63d7 | ||
|
|
bc583d9763 | ||
|
|
ade4dc191b | ||
|
|
cd05da79e7 | ||
|
|
5545bcea5d | ||
|
|
c97286f839 | ||
|
|
f471800168 | ||
|
|
bdf0697a5a | ||
|
|
674a912fb5 | ||
|
|
1bf3b3d0f9 | ||
|
|
fb9303c610 | ||
|
|
0585769741 | ||
|
|
9b288b788e | ||
|
|
134ac7e7c8 | ||
|
|
46f04343a5 | ||
|
|
a3ca2d185b | ||
|
|
8cbdd21867 | ||
|
|
ae81195418 | ||
|
|
6ee5fa939a | ||
|
|
b6a0e3fd2e | ||
|
|
bd2b55679e | ||
|
|
f32c6a9095 | ||
|
|
2eed6ec5ec | ||
|
|
641206ad5e | ||
|
|
bfd3119ae4 | ||
|
|
4b9b19e998 | ||
|
|
00e0559e17 | ||
|
|
0b1d9dbc40 | ||
|
|
5379273ea3 | ||
|
|
ecef920b8f | ||
|
|
eb9267b15e | ||
|
|
1efb2bcd90 | ||
|
|
878593f205 | ||
|
|
1596463985 | ||
|
|
9d82492e49 | ||
|
|
303aeea8a8 | ||
|
|
18898b4a9f | ||
|
|
17cee2056c | ||
|
|
67a3c833af | ||
|
|
b1f641b5b7 | ||
|
|
f01ce65f5b | ||
|
|
be1cf99e9a | ||
|
|
7710080ffd | ||
|
|
798a81d48d | ||
|
|
51930f7b63 | ||
|
|
00ef1b0d6e | ||
|
|
02a82c3c9b | ||
|
|
349cbfcf50 | ||
|
|
51f99ae250 | ||
|
|
12304c0e5a | ||
|
|
1719abb409 | ||
|
|
3ddf4c6933 | ||
|
|
fb2a7298e0 | ||
|
|
568ac5ee8e | ||
|
|
23c832bb4b | ||
|
|
4831851ca3 | ||
|
|
080dcfaabe | ||
|
|
cbc07bdd82 | ||
|
|
aedae4c971 | ||
|
|
b36eb9d12f | ||
|
|
8df01fad14 | ||
|
|
c8020b959d | ||
|
|
2154607fb0 | ||
|
|
d08a15b343 | ||
|
|
4e37fb356a | ||
|
|
6a8a389c47 | ||
|
|
569e46cbf9 | ||
|
|
91d9410157 | ||
|
|
065ddb6416 | ||
|
|
53244a2132 | ||
|
|
db98f309a9 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
open_collective: osmocom
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -16,7 +16,6 @@ install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
osmo-ggsn.spec
|
||||
stamp-h1
|
||||
INSTALL
|
||||
m4/
|
||||
@@ -28,7 +27,7 @@ osmo-ggsn-*.tar*
|
||||
# debian
|
||||
debian/osmo-ggsn/
|
||||
debian/*.debhelper
|
||||
debian/libgtp1
|
||||
debian/libgtp*/
|
||||
debian/osmo-ggsn-dbg
|
||||
debian/*.log
|
||||
debian/autoreconf.*
|
||||
@@ -80,3 +79,12 @@ doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
doc/manuals/vty/ggsn_vty_reference.xml
|
||||
|
||||
contrib/osmo-ggsn.spec
|
||||
/debian/gtp-echo-responder-dbg/
|
||||
/debian/gtp-echo-responder/
|
||||
/debian/osmo-ggsn-doc/
|
||||
/utils/gtp-echo-responder
|
||||
|
||||
include/osmocom/gtp/version.h
|
||||
|
||||
21
Makefile.am
21
Makefile.am
@@ -1,5 +1,15 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
SUBDIRS = lib gtp ggsn sgsnemu doc contrib tests
|
||||
SUBDIRS = \
|
||||
include \
|
||||
lib \
|
||||
gtp \
|
||||
ggsn \
|
||||
sgsnemu \
|
||||
doc \
|
||||
contrib \
|
||||
utils \
|
||||
tests \
|
||||
$(NULL)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libgtp.pc
|
||||
@@ -10,7 +20,14 @@ $(top_srcdir)/.version:
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
EXTRA_DIST = git-version-gen .version README.md README.FreeBSD README.MacOSX
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
README.FreeBSD \
|
||||
README.MacOSX \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
167
README.md
167
README.md
@@ -5,57 +5,71 @@ This repository contains a C-language implementation of a GGSN (Gateway
|
||||
GPRS Support Node), a core network element of ETSI/3GPP cellular
|
||||
networks such as GPRS, EDGE, UMTS or HSPA.
|
||||
|
||||
OsmoGGSN is part of the [Osmocom](https://osmocom.org/) Open Source
|
||||
Mobile Communications projects and the successor to OpenGGSN.
|
||||
OpenGGSN was developed until 2004 by Mondru AB.
|
||||
**OsmoGGSN** is part of the [Osmocom](https://osmocom.org/) Open Source
|
||||
Mobile Communications projects and the successor to OpenGGSN (which was
|
||||
developed until 2004 by Mondru AB).
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
The official homepage of the project is
|
||||
https://osmocom.org/projects/openggsn/wiki
|
||||
The official homepage of the project is <https://osmocom.org/projects/openggsn/wiki>.
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official osmo-ggsn.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-ggsn.git
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
|
||||
There is a cgit interface at http://git.osmocom.org/osmo-ggsn/
|
||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
There currently is no other documentation other than the wiki on the
|
||||
homepage. It would be great if somebody would work towards a user
|
||||
manual that can become part of the osmo-gsm-manuals project.
|
||||
The user manual and VTY reference are optionally built in PDF form
|
||||
as part of the build process. Pre-rendered versions are available here:
|
||||
|
||||
* [osmo-ggsn user manual](https://ftp.osmocom.org/docs/osmo-ggsn/master/osmoggsn-usermanual.pdf)
|
||||
* [osmo-ggsn VTY reference](https://ftp.osmocom.org/docs/osmo-ggsn/master/osmoggsn-vty-reference.pdf)
|
||||
|
||||
Forum
|
||||
-----
|
||||
|
||||
We welcome any pySim related discussions in the
|
||||
[Cellular Network Infrastructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn/)
|
||||
section of the osmocom discourse (web based Forum).
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to OsmoGGSN are happening on the
|
||||
osmocom-net-gprs@lists.osmocom.org mailing list, please see
|
||||
https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs for
|
||||
<https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs> for
|
||||
subscription options and the list archive.
|
||||
|
||||
Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
Please observe the [Osmocom Mailing List Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
Issue Tracker
|
||||
-------------
|
||||
|
||||
We use the [issue tracker of the osmo-ggsn project on osmocom.org](https://osmocom.org/projects/openggsn/issues) for
|
||||
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
|
||||
us out by resolving existing issues.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We us a gerrit based patch submission/review process for managing
|
||||
We use a Gerrit based patch submission/review process for managing
|
||||
contributions. Please see
|
||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
|
||||
more details
|
||||
|
||||
The current patch queue for OsmoGGSN can be seen at
|
||||
https://gerrit.osmocom.org/#/q/project:osmo-ggsn+status:open
|
||||
<https://gerrit.osmocom.org/#/q/project:osmo-ggsn+status:open>
|
||||
|
||||
|
||||
QuickStart
|
||||
@@ -75,61 +89,33 @@ The tun driver is required for proper operation of openggsn. For Linux
|
||||
kernels later than 2.4.7 the driver is typically included, but might
|
||||
need to be configured for automatic loading:
|
||||
|
||||
1. Add the following line to /etc/modules.conf: alias char-major-10-200 tun
|
||||
2. depmod -a
|
||||
1. Add the following line to `/etc/modules.conf`: `alias char-major-10-200 tun`
|
||||
2. `depmod -a`
|
||||
|
||||
|
||||
Installation from binary
|
||||
------------------------
|
||||
|
||||
OsmoGGSN is built for common versions of Debian and Ubuntu as part of
|
||||
OsmoGGSN is built for common versions of Debian, Ubuntu and other distributions part of
|
||||
the [Osmocom Nightly Builds](https://osmocom.org/projects/cellular-infrastructure/wiki/Nightly_Builds)
|
||||
project. If you don't want to do development, it is suggested to simply
|
||||
use those binary packages, rather than building yourself from source.
|
||||
and [Osmocom Latest Builds](https://osmocom.org/projects/cellular-infrastructure/wiki/Latest_Builds).
|
||||
If you don't want to do development, it is suggested to simply use those binary packages, rather than building
|
||||
yourself from source.
|
||||
|
||||
|
||||
Installation from source
|
||||
------------------------
|
||||
|
||||
1. ./configure
|
||||
2. make
|
||||
3. make install
|
||||
```
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
You need to be root in order to install the package, but not in order
|
||||
to compile.
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
*sgsnemu*
|
||||
Start the emulator as root using the command:
|
||||
|
||||
sgsnemu -l 10.0.0.50 -r 10.0.0.40 --createif --defaultroute
|
||||
|
||||
This will cause the sgsn emulator to bind to local address 10.0.0.50
|
||||
and connect to the ggsn found at 10.0.0.40. It will first send off an
|
||||
ECHO_REQUEST message. After this it will attempt to establish a pdp
|
||||
context. If successful it will create a local interface and set up
|
||||
routing. Now you should be able to ping through the connection. Use a
|
||||
network analysator such as ethereal to monitor the traffic.
|
||||
|
||||
sgsnemu -h will show a list of available options.
|
||||
|
||||
sgsnemu -c sgsnemu.conf will use sgsnemu.conf as a configuration
|
||||
file. A sample file is provided in examples/sgsnemu.conf.
|
||||
|
||||
*ggsn*
|
||||
Edit the configuration file ggsn.conf found under openggsn/examples.
|
||||
Start the ggsn as root using the command:
|
||||
|
||||
ggsn --fg -c examples/ggsn.conf -l 10.0.0.40 --statedir ./
|
||||
|
||||
This will run the ggsn in foreground using the local interface
|
||||
10.0.0.40. If you don't have a GSM network available for testing you
|
||||
can use sgsnemu to test the GGSN.
|
||||
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
@@ -144,9 +130,10 @@ OsmoGGSN is an open source implementation of GPRS Support Nodes
|
||||
version 1.
|
||||
|
||||
OsmoGGSN provides 3 components:
|
||||
* gtplib
|
||||
* osmo-ggsn
|
||||
* sgsnemu
|
||||
|
||||
* *libgtp*, a shared library for the GTPv1C protocol
|
||||
* *osmo-ggsn*, the GGSN itself
|
||||
* *sgsnemu*, a SGSN emulator
|
||||
|
||||
*gtplib*
|
||||
This library contains all functionality relating to the GTP
|
||||
@@ -182,10 +169,10 @@ Both osmo-ggsn and sgsnemu uses the tun package. You need at least tun
|
||||
version 1.1. With Linux tun is normally included from kernel version
|
||||
2.4.7. To configure automatic loading:
|
||||
|
||||
1. Add the following line to /etc/modules.conf: alias char-major-10-200 tun
|
||||
2. depmod -a
|
||||
1. Add the following line to `/etc/modules.conf`: `alias char-major-10-200 tun`
|
||||
2. `depmod -a`
|
||||
|
||||
Alternatively you can execute "modprobe tun" on the commandline.
|
||||
Alternatively you can execute `modprobe tun` on the commandline.
|
||||
|
||||
Gengetopt
|
||||
---------
|
||||
@@ -195,11 +182,13 @@ cmdline.ggo source file. You need at least gengetopt version 2.8. If
|
||||
you are just going to compile the programs you don't need gengetopt.
|
||||
|
||||
To use gengetopt for the sgsnemu do the following:
|
||||
```
|
||||
cd sgsnemu
|
||||
gengetopt < cmdline.ggo --conf-parser
|
||||
```
|
||||
|
||||
For more information about gengetopt see
|
||||
http://www.gnu.org/software/gengetopt/gengetopt.html
|
||||
<http://www.gnu.org/software/gengetopt/gengetopt.html>
|
||||
|
||||
|
||||
Compilation and Installation
|
||||
@@ -213,27 +202,21 @@ Running osmo-ggsn
|
||||
|
||||
Use osmo-ggsn -h for a list of available options. All options available on
|
||||
the command line can also be given in a configuration file. See
|
||||
examples/osmo-ggsn.cfg for the format of this file.
|
||||
`doc/examples/osmo-ggsn.cfg` for the format of this file.
|
||||
|
||||
Start osmo-ggsn as root using the command:
|
||||
|
||||
osmo-ggsn -c examples/osmo-ggsn.cfg
|
||||
`osmo-ggsn -c doc/examples/osmo-ggsn.cfg`
|
||||
|
||||
First a tun network interface will be created. In the above example
|
||||
the network interface address is 192.168.0.0 and the mask is
|
||||
255.255.255.0. You can check that this interface is up by using
|
||||
ifconfig.
|
||||
First, a tun network interface will be created for each configured apn.
|
||||
|
||||
After tun has been successfully established the ggsn will wait for GTP
|
||||
create PDP context requests on the local interface
|
||||
10.0.0.40. Currently all requests are accepted, and no password,
|
||||
username or APN validation is performed.
|
||||
create PDP context requests on the configured `gtp bind-ip` address.
|
||||
Currently all requests are accepted, and no password, username validation is performed.
|
||||
|
||||
When receiving a create PDP context request a dynamic IP address will
|
||||
be allocated from the address pool determined by --dynip. In the above
|
||||
example the first allocated address will be 192.168.0.1, followed by
|
||||
192.168.0.2 and so on. The request is confirmed by sending a create
|
||||
PDP context response message to the peer (SGSN).
|
||||
When receiving a create PDP context request for a given APN, a dynamic IP address will
|
||||
be allocated from the address pool defined in the config file section for that apn.
|
||||
The request is confirmed by sending a create PDP context response message to the peer (SGSN).
|
||||
|
||||
Now IP packets will be forwarded between the tun network interface and
|
||||
the established GTP tunnel. In order to allow users to access the
|
||||
@@ -243,20 +226,20 @@ Networking HOWTO for details.
|
||||
|
||||
Remember to enable routing:
|
||||
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
`echo 1 > /proc/sys/net/ipv4/ip_forward`
|
||||
|
||||
If you installed using a binary RPM package it is possible to start
|
||||
osmo-ggsn by using the Sys 5 script:
|
||||
If you're using systemd and did `make install` or installed from a bianry package,
|
||||
you can start osmo-ggsn by using the included systemd service/unit file:
|
||||
|
||||
/etc/init.d/osmo-ggsn start
|
||||
`systemctl start osmo-ggsn`
|
||||
|
||||
|
||||
Running sgsnemu
|
||||
===============
|
||||
|
||||
Use sgsnemu -h for a list of available options. All options available
|
||||
Use `sgsnemu -h` for a list of available options. All options available
|
||||
on the command line can also be given in a configuration file. See
|
||||
examples/sgsnemu.conf for the format of this file.
|
||||
`doc/examples/sgsnemu.conf` for the format of this file.
|
||||
|
||||
If you want to test a GRX roaming connection you will need to do the
|
||||
following:
|
||||
@@ -269,11 +252,11 @@ subnet mask and default route. See the Linux Networking HOWTO for
|
||||
details.
|
||||
4. Launch sgsnemu with something like:
|
||||
|
||||
sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 0
|
||||
`sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 0`
|
||||
|
||||
sgsnemu will print something like the following on the screen:
|
||||
|
||||
|
||||
```
|
||||
Using DNS server: 10.20.38.51 (10.20.38.51)
|
||||
Local IP address is: 10.0.0.50 (10.0.0.50)
|
||||
Remote IP address is: 10.0.0.40 (10.0.0.40)
|
||||
@@ -289,6 +272,7 @@ sgsnemu will print something like the following on the screen:
|
||||
Waiting for response from ggsn........
|
||||
|
||||
Received echo response. Cause value: 0
|
||||
```
|
||||
|
||||
This is quite good. It means that you managed to send off an echo
|
||||
request to a remote GGSN, and it was friendly enough to answer you. If
|
||||
@@ -306,10 +290,11 @@ testing. Also note that you are establishing a connection to the Gi
|
||||
network, so please be carefull not to route internet traffic onto the
|
||||
GPRS core network! Assuming you know what you are doing:
|
||||
|
||||
sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 1 --apn internet --imsi 240011234567890 --msisdn 46702123456 --createif --defaultroute
|
||||
`sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 1 --apn internet --imsi 240011234567890 --msisdn 46702123456 --createif --defaultroute`
|
||||
|
||||
sgsnemu will print something like the following on the screen:
|
||||
|
||||
```
|
||||
Using DNS server: 10.20.38.51 (10.20.38.51)
|
||||
Local IP address is: 10.0.0.50 (10.0.0.50)
|
||||
Remote IP address is: 10.0.0.40 (10.0.0.40)
|
||||
@@ -330,7 +315,7 @@ sgsnemu will print something like the following on the screen:
|
||||
Setting up interface and routing
|
||||
/sbin/ifconfig tun0 192.168.0.1
|
||||
/sbin/route add -net 192.168.0.0 netmask 255.255.255.0 gw 192.168.0.1
|
||||
|
||||
```
|
||||
|
||||
Now a context is established to the remote GGSN. The IP address of the
|
||||
context is 192.168.0.1. You should be able to ping a known address on
|
||||
@@ -344,13 +329,13 @@ do this is to use policy routing. Also note that you are effectively
|
||||
connecting the same computer to both the Gn and Gi network, so please
|
||||
be carefull not to route internet traffic onto the GPRS core network
|
||||
and please protect yourself against hackers! For this reason it is
|
||||
advised to always use --contexts 0 when testing a live network.
|
||||
advised to always use `--contexts 0` when testing a live network.
|
||||
|
||||
After --timelimit seconds the PDP context is disconnected with the
|
||||
After `--timelimit seconds` the PDP context is disconnected with the
|
||||
following messages from sgsnemu:
|
||||
|
||||
|
||||
```
|
||||
Disconnecting PDP context #0
|
||||
Received delete PDP context response. Cause value: 128
|
||||
Deleting tun interface
|
||||
|
||||
```
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# In short:
|
||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
|
||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libgtp new API SGSN Ctx Req/Resp/Ack, gtp_encode_pdp_ctx, gtp_decode_pdp_ctx, gtp_set_cb_sgsn_context_request_ind, ...
|
||||
libgtp new dependency libosmogsm
|
||||
|
||||
18
configure.ac
18
configure.ac
@@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
@@ -69,7 +71,7 @@ AC_ARG_ENABLE([gtp-linux],
|
||||
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
|
||||
|
||||
AS_IF([test "x$enable_gtp_linux" = "xyes"], [
|
||||
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.2.0])
|
||||
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.3.0])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
|
||||
@@ -152,9 +154,10 @@ adl_FUNC_GETOPT_LONG
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -255,6 +258,7 @@ AC_CONFIG_FILES([Makefile
|
||||
lib/Makefile
|
||||
intl/Makefile
|
||||
po/Makefile
|
||||
utils/Makefile
|
||||
sgsnemu/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
@@ -262,8 +266,10 @@ AC_CONFIG_FILES([Makefile
|
||||
tests/Makefile
|
||||
tests/lib/Makefile
|
||||
tests/gtp/Makefile
|
||||
libgtp.pc
|
||||
osmo-ggsn.spec])
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/gtp/Makefile
|
||||
libgtp.pc])
|
||||
AC_OUTPUT
|
||||
|
||||
echo "
|
||||
|
||||
@@ -27,7 +27,7 @@ mkdir "$deps" || true
|
||||
if [ "x$GTP" == "x--enable-gtp-linux" ]; then
|
||||
osmo-build-dep.sh libgtpnl
|
||||
fi
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
|
||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
@@ -38,7 +38,6 @@ export PATH="$inst/bin:$PATH"
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
@@ -54,11 +53,11 @@ cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-werror $GTP $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE maintainer-clean
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# osmo-ggsn This shell script takes care of starting and stopping
|
||||
# osmo-ggsn.
|
||||
#
|
||||
# chkconfig: - 65 35
|
||||
# description: osmo-ggsn is a Gateway GPRS Support Node.
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
# Source networking configuration.
|
||||
. /etc/sysconfig/network
|
||||
|
||||
if [ -f /etc/sysconfig/osmo-ggsn ]; then
|
||||
. /etc/sysconfig/osmo-ggsn
|
||||
fi
|
||||
|
||||
# Check that networking is up.
|
||||
[ ${NETWORKING} = "no" ] && exit 0
|
||||
|
||||
[ -f /usr/bin/osmo-ggsn ] || exit 0
|
||||
[ -f /etc/osmo-ggsn.cfg ] || exit 0
|
||||
|
||||
RETVAL=0
|
||||
prog="osmo-ggsn"
|
||||
|
||||
start() {
|
||||
# Start daemons.
|
||||
echo -n $"Starting $prog: "
|
||||
|
||||
# Load tun module
|
||||
/sbin/modprobe tun >/dev/null 2>&1
|
||||
|
||||
# Enable routing of packets: WARNING!!!
|
||||
# Users should enable this explicitly
|
||||
# echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
# Check for runtime directory of nonvolatile data
|
||||
if [ ! -d /var/lib/osmo-ggsn ]; then
|
||||
mkdir /var/lib/osmo-ggsn
|
||||
fi
|
||||
|
||||
# Check for GTP restart counter
|
||||
if [ ! -d /var/lib/osmo-ggsn/gsn_restart ]; then
|
||||
echo 0 > /var/lib/osmo-ggsn/gsn_restart
|
||||
fi
|
||||
|
||||
|
||||
daemon /usr/bin/osmo-ggsn
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/osmo-ggsn
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
# Stop daemons.
|
||||
echo -n $"Shutting down $prog: "
|
||||
killproc osmo-ggsn
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/osmo-ggsn /var/run/osmo-ggsn.pid
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart|reload)
|
||||
stop
|
||||
start
|
||||
RETVAL=$?
|
||||
;;
|
||||
condrestart)
|
||||
if [ -f /var/lock/subsys/osmo-ggsn ] ; then
|
||||
stop
|
||||
start
|
||||
RETVAL=$?
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
status osmo-ggsn
|
||||
RETVAL=$?
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|condrestart|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $RETVAL
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
[Unit]
|
||||
Description=OsmoGGSN
|
||||
After=networking.service
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
# For setting up the gtp0/tun0 devices
|
||||
AmbientCapabilities=CAP_NET_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
301
debian/changelog
vendored
301
debian/changelog
vendored
@@ -1,3 +1,304 @@
|
||||
osmo-ggsn (1.13.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* gtp: Allow tx Direct Tunnel Flags in UpdatePDPCtx{Req,Resp}
|
||||
* gtp: Store rx Direct Tunnel Flags in UpdatePDPCtx{Req,Resp}
|
||||
* gtp: Allow UpdatePDPContext initiated by GGSN
|
||||
* gtp: Allow setting callback to receive update_context_ind
|
||||
* ggsn: Mark internal cb function static
|
||||
* ggsn: kernel gtpu: Support updating pdp ctx remote IP address and TEID
|
||||
* Move apn allocation code out of vty file
|
||||
* Move ggsn allocation code out of vty file
|
||||
* Move g_ggsn_list declaration to ggsn.c
|
||||
* vty: Fix missing newline in description of 'apn tun-device' cmd
|
||||
* ggsn: Support announcing APN MTU over PCO
|
||||
* ggsn: Support announcing APN MTU over ICMPv6 RA
|
||||
* ggsn: use libosmocore tundev API to create apn tun device
|
||||
* ggsn: Use osmo_netdev_addaddr() libosmocore API
|
||||
* ggsn: apply configured APN MTU to tun
|
||||
* doc: Fix typo in user manual
|
||||
* doc: Remove reference to non longer existing osmo-ggsn.init
|
||||
* doc: Fix typo: wrong interface named
|
||||
* doc: Reorder some chapters
|
||||
* tun: Fix null pointer derefence when in kernel gtp mode
|
||||
* ggsn: Avoid forwarding IPv6 solicited-node multicast addr to tun device
|
||||
* ggsn: Rename confusing functions
|
||||
* tun.h: Remove non-existent tun_decaps()
|
||||
* Rename tun_encaps -> tun_inject_pkt
|
||||
* Refactor tun_t fields and alloc APIs
|
||||
* tun: Use OSMO_STRLCPY_ARRAY
|
||||
* doc: Document MTU features in User Manual and example config files
|
||||
* doc: Update all iptables references with nftables
|
||||
* jenkins.sh: Use --disable-doxygen configure param
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* gtp_internal.h: add missing include to <stdint.h>
|
||||
* gtpie: fix comment
|
||||
* gtp.h: add more GTP cause code from 29.060 v15.3.30
|
||||
* gtp_new(): use talloc instead of calloc/free
|
||||
* gtpie: add gtp_encaps a modern encapsulation method
|
||||
* gtp: split gtp_req into 2 parts: transmit and fill header
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* gtp: Make peer addr const in gtp_req/gtp_resp
|
||||
* gtp: Rework gtp_resp() into gtp_resp_pdp()
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:09:58 +0100
|
||||
|
||||
osmo-ggsn (1.12.0) unstable; urgency=medium
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* libgtp: Remove defines for reserved causes in gtp.h
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* pco: Improve IPCP spec reference documentation
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Fix a typo
|
||||
* gtp_new: deduplicate create_and_bind_socket code
|
||||
* kernel-gtp: support IPv6 on outer layer
|
||||
* kernel-gtp: support IPv6 on inner layer
|
||||
* Revert "kernel-gtp: support IPv6 on outer layer"
|
||||
* Cosmetic: Makefile.am: make SUBDIRS diff friendly
|
||||
* Cosmetic: AM_CFLAGS: make diff friendly
|
||||
* Cosmetic: {lib,gtp}/Makefile.am: diff friendly
|
||||
* lib/gtp-kernel.c: initialize ret with 0
|
||||
* lib/gtp-kernel.c: check rc of in46a_from_eua
|
||||
* doc: fix typo ndoe -> node
|
||||
* Add clear error for kernel not supporting IPv6
|
||||
* contrib: remove rpm spec file
|
||||
* libgtp: move includes to osmocom/include/gtp
|
||||
* gtp/gsn_internal.h: new file
|
||||
* gtp/gtp.c: move gtp_create_context_resp down
|
||||
* gtp: remove unused conversion functions
|
||||
* gtp: move conversion functions up
|
||||
* gtp/gtp_internal.h: new file
|
||||
* ggsn/ggsn_vty: create state-dir
|
||||
* doc: set state-dir to /var/lib/osmocom/osmo-ggsn
|
||||
* contrib/systemd: run as osmocom user
|
||||
* Use uniform log format for default config files
|
||||
* {contrib,debian}/osmo-ggsn.init: remove
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add funding link to github mirror
|
||||
* README.md: Improve markdown formatting
|
||||
* README.md: Add Forum + Issue Tracker sections
|
||||
* README.md: Major overhaul
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* README.md: cosmetic: fix a typo
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:13:31 +0200
|
||||
|
||||
osmo-ggsn (1.11.0) unstable; urgency=medium
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* gtp: Add net GTP cause values and a function to check for success
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Tue, 28 Nov 2023 13:38:29 +0100
|
||||
|
||||
osmo-ggsn (1.10.2) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* Do not hard-code -g and -O2 in CFLAGS
|
||||
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
||||
|
||||
[ Oliver Smith ]
|
||||
* doc/manuals/chapters/configuration: fix typo
|
||||
* debian: set compat level to 10
|
||||
* systemd: depend on networking-online.target
|
||||
* README: update documentation section
|
||||
* doc: running: update kernel-gtp limitations
|
||||
* lib/in46_addr: add in46a_from_gsna
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:36:10 +0200
|
||||
|
||||
osmo-ggsn (1.10.1) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* debian/libgtp6.shlibs: new file
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* lib/icmpv6.h: fix struct icmpv6_{radv_hdr,opt_prefix}
|
||||
* gtp/gsn.c: fix 'No newline at end of file'
|
||||
* gtp: use OSMO_ASSERT() in gtp_new()
|
||||
|
||||
-- Vadim Yanitskiy <vyanitskiy@sysmocom.de> Mon, 27 Feb 2023 22:35:47 +0700
|
||||
|
||||
osmo-ggsn (1.10.0) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Set working directory in systemd service file
|
||||
* Ignore .deb build byproducts
|
||||
* ctrl: take both address and port from vty config
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: gtp: Fix typo in comment
|
||||
* Split gsn_t related APIs out of gtp.{c,h}
|
||||
* Use rate_ctr for gsn_t available_counters
|
||||
* ggsn: Introduce tdef and make it configurable over VTY
|
||||
* gtp: Introduce VTY configurable GTP timer X3
|
||||
* Fix typos in comments and VTY descriptions
|
||||
|
||||
[ arehbein ]
|
||||
* osmo-ggsn: Transition to use of 'telnet_init_default'
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 14:29:48 +0100
|
||||
|
||||
osmo-ggsn (1.9.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* tests: in46a_test: Make coverity happy when calling in46a_from_eua
|
||||
* vty: Fix cmd 'no echo-interval' doing nothing
|
||||
* libgtp: Fix ggsn crash if pdp alloc array is full (PDP_MAX)
|
||||
* libgtp: Define retransmit QUEUE_SIZE relative to PDP_MAX (increase)
|
||||
* gtp: Use switch statement in gtp_create_pdp_ind()
|
||||
* gtp: Log detection of rx duplicate
|
||||
* gtp: Small log improvements in gtp_create_pdp_ind()
|
||||
* gtp: Specify retrans queue name & seqnum in log lines
|
||||
* gtp: Log retrans queue register&free entries
|
||||
* gtp: Fix typo in comment
|
||||
* pco.h: Fix typo in reference to spec
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
|
||||
|
||||
[ Harald Welte ]
|
||||
* update git URLs (git -> https; gitea)
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 17:48:22 +0200
|
||||
|
||||
osmo-ggsn (1.8.0) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* doc/examples/Makefile.am: add sgsnemu.conf
|
||||
* doc/examples/osmo-ggsn-kernel-gtp.cfg: new file
|
||||
* doc/manuals: describe GTP-U kernel module
|
||||
* gitignore: add ggsn_vty_reference.xml
|
||||
|
||||
[ Harald Welte ]
|
||||
* Don't install osmo-ggsn-kernel-gtp.cfg to /etc/osmocom/
|
||||
* Don't install sgsnemu.conf to /etc/osmocom/
|
||||
* ggsn: Reject PDP CTX ACT for static IP addresses
|
||||
* vty: Inform user that static IP addresses are not supported
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* gtp: Update teic_confirmed only on resp success
|
||||
* gtp: Rework parsing logic of UpdatePdpCtxResponse
|
||||
* ggsn: Improve logging on incoming DL data packets
|
||||
* gtp: Improve logging of failing pdp ctx resolution from TEI/TID
|
||||
* cosmetic: gtpie.c: Fix trailing whitespace
|
||||
* gtp: constify pointer arg
|
||||
* gtp: Support tx/rx RAN Information Relay message
|
||||
* ggsn: Log tun fd write errors
|
||||
* ggsn: Fix heap-use-after-free during Recovery without associated PDP
|
||||
* cosmetic: configure.ac: Fix tabulation in line
|
||||
* Introduce program gtp-echo-responder
|
||||
* gtp_echo_responder: report invalid chars present in node-feautres cmdline arg as error
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 13:49:16 +0100
|
||||
|
||||
osmo-ggsn (1.7.1) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* manuals: generate vty reference xml at build time
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 17:31:24 +0100
|
||||
|
||||
osmo-ggsn (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* configure.ac: Fix trailing whitespace
|
||||
* doc: Update VTY reference xml file
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* ggsn: generate coredump and exit upon SIGABRT received
|
||||
* tests: Explicitly drop category from log
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
|
||||
[ Keith ]
|
||||
* Fix vty PDP lookups by IMSI
|
||||
* Prevent Crash in show pdp-context from vty
|
||||
* Minor: remove code duplication
|
||||
* Use imsi_str2gtp() in sgsnemu
|
||||
* sgsnemu: relax check on length of IMSI cmdline arg.
|
||||
* GTP: Replace recently introduced imsi_str2gtp()
|
||||
|
||||
[ Harald Welte ]
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* gtp-kernel: Remove duplicate #include section
|
||||
* gtp-kernel: don't #include libmnl headers
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
* apn_start: avoid segfault if missing tun-device
|
||||
* .gitignore: ignore debian/libgtp*
|
||||
* deb/rpm: build with --enable-gtp-linux
|
||||
|
||||
-- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 13:34:39 +0100
|
||||
|
||||
osmo-ggsn (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: Fix comment typo
|
||||
* netns: Improve error checking
|
||||
* sgsnemu: cmdline: Drop unused function cmdline_parser_params_create()
|
||||
* sgsnemu: Pass array of in64_addr to in46a_from_eua()
|
||||
* sgsnemu: Rename sgsnemu's libgtp cb_conf
|
||||
* sgsnemu: Set its default loglevel category to INFO
|
||||
* Move icmpv6 and checksum files from ggsn/ dir to lib/
|
||||
* netdev_addaddr6: Use prefixlen arg
|
||||
* sgsnemu: Avoid adding extra autogenerated local link ipv6 addr to tun iface
|
||||
* sgsnemu: Fix ping transmitted statistics output
|
||||
* cosmetic: icmpv6.c: fix typo in comment
|
||||
* icmpv6.c: Mark internal function as static
|
||||
* sgsnemu: Get rid of duplicated options.destaddr
|
||||
* sgsnemu: Get rid of duplicated options.net
|
||||
* sgsnemu: tun_addaddr: Don't set local addr as dstaddr
|
||||
* icmpv6.c: Move code generating ipv6 hdr to its own function
|
||||
* Rename netdev_*route to end in route4
|
||||
* sgsnemu: Fix build/run against linux < 4.11 (no sysctl addr_gen_mode support)
|
||||
* sgsnemu: Handle IPv6 SLAAC in tun iface manually
|
||||
* sgsnemu: Implement ping on IPv6 APNs
|
||||
* sgsnemu: Fix assumption ipv6 Interface-Identifier of public addr == announced Prefix
|
||||
* gtp: queue_test: Fix printf gcc warn under ARM
|
||||
|
||||
[ Andreas Schultz ]
|
||||
* add Linux network namespace support for TUN device
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* lib/netns: fix open_ns(): return fd from open()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* doc: do not use random ip address for dns in default conf
|
||||
* doc: use 127.0.0.2 instead of 127.0.0.6 as bind ip.
|
||||
* debug: use LOGL_NOTICE instead of LOGL_DEBUG
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
|
||||
[ Harald Welte ]
|
||||
* lib/netns.c: Add comments to the code, including doxygen API docs
|
||||
* lib/netns: OSMO_ASSERT() if user doesn't call init_netns()
|
||||
* lib/netns: Fix up error paths
|
||||
* example config: use RFC1918 addresses for GGSN pools
|
||||
|
||||
[ Dmitri Kalashnik ]
|
||||
* sgsnemu: use real tun device name after the device is up.
|
||||
|
||||
[ Oliver Smith ]
|
||||
* osmo-ggsn.spec.in: remove
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Thu, 13 Aug 2020 12:26:20 +0200
|
||||
|
||||
osmo-ggsn (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Jan Engelhardt ]
|
||||
|
||||
2
debian/compat
vendored
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
9
|
||||
10
|
||||
|
||||
36
debian/control
vendored
36
debian/control
vendored
@@ -1,17 +1,18 @@
|
||||
Source: osmo-ggsn
|
||||
Maintainer: Harald Welte <laforge@gnumonks.org>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Section: net
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 9),
|
||||
Build-Depends: debhelper (>= 10),
|
||||
autotools-dev,
|
||||
pkg-config,
|
||||
libdpkg-perl, git,
|
||||
dh-autoreconf,
|
||||
libosmocore-dev (>= 1.1.0),
|
||||
osmo-gsm-manuals-dev
|
||||
libosmocore-dev (>= 1.11.0),
|
||||
osmo-gsm-manuals-dev (>= 1.6.0),
|
||||
libgtpnl-dev (>= 1.3.0)
|
||||
Standards-Version: 3.9.6
|
||||
Vcs-Browser: http://git.osmocom.org/osmo-ggsn/
|
||||
Vcs-Git: git://git.osmocom.org/osmo-ggsn
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
Homepage: https://projects.osmocom.org/projects/openggsn
|
||||
|
||||
Package: osmo-ggsn
|
||||
@@ -23,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: libgtp6
|
||||
Package: libgtp11
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
@@ -37,12 +38,18 @@ Description: library implementing the GTP protocol between SGSN and GGSN
|
||||
This library is part of OsmoGGSN and implements the GTP protocol between
|
||||
SGSN (Serving GPRS support node) and GGSN.
|
||||
|
||||
Package: gtp-echo-responder
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description: Small program answering GTP ECHO Request with GTP ECHO Response
|
||||
|
||||
Package: libgtp-dev
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libdevel
|
||||
Depends: ${misc:Depends},
|
||||
libgtp6 (= ${binary:Version})
|
||||
libgtp11 (= ${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
|
||||
@@ -55,18 +62,27 @@ Package: osmo-ggsn-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp11 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for OsmoGGSN
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
operators as the interface between the Internet and the rest of the
|
||||
mobile network infrastructure.
|
||||
|
||||
Package: gtp-echo-responder-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, gtp-echo-responder (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for gtp-echo-responder
|
||||
Small program answering GTP ECHO Request with GTP ECHO Response.
|
||||
|
||||
Package: libgtp-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp11 (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for OsmoGGSN
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
|
||||
5
debian/copyright
vendored
5
debian/copyright
vendored
@@ -16,6 +16,11 @@ Files: lib/getopt.c
|
||||
Copyright: 1987-2001 Free Software Foundation, Inc.
|
||||
License: LGPL-2.1+
|
||||
|
||||
Files: utils/gtp_echo_responder.c
|
||||
utils/gtp_echo_responder_test.py
|
||||
Copyright: 2021 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
License: MIT
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2010-2017 Harald Welte <laforge@gnumonks.org>
|
||||
2016 Ruben Undheim <ruben.undheim@gmail.com>
|
||||
|
||||
1
debian/gtp-echo-responder.install
vendored
Normal file
1
debian/gtp-echo-responder.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/usr/bin/gtp-echo-responder
|
||||
2
debian/libgtp11.shlibs
vendored
Normal file
2
debian/libgtp11.shlibs
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Most recent version of the package that added new symbols (OS#5318)
|
||||
libgtp 11 libgtp11 (>= 1.13.0)
|
||||
1
debian/osmo-ggsn.examples
vendored
1
debian/osmo-ggsn.examples
vendored
@@ -1,2 +1,3 @@
|
||||
doc/examples/osmo-ggsn.cfg
|
||||
doc/examples/osmo-ggsn-kernel-gtp.cfg
|
||||
doc/examples/sgsnemu.conf
|
||||
|
||||
163
debian/osmo-ggsn.init
vendored
163
debian/osmo-ggsn.init
vendored
@@ -1,163 +0,0 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: osmo-ggsn
|
||||
# Required-Start: $network $local_fs $remote_fs
|
||||
# Required-Stop: $network $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Gateway GPRS Support Node
|
||||
# Description: Gateway GPRS Support Node
|
||||
### END INIT INFO
|
||||
|
||||
# Author: Harald Welte <laforge@gnumonks.org>
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
DESC="OsmoGGSN Gateway GPRS Support Node"
|
||||
NAME=ggsn
|
||||
DAEMON=/usr/bin/osmo-ggsn
|
||||
DAEMON_ARGS="" # Arguments to run the daemon with
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/osmo-ggsn
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x $DAEMON ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/osmo-ggsn ] && . /etc/default/osmo-ggsn
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
. /lib/init/vars.sh
|
||||
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
DAEMON_ARGS="$DAEMON_ARGS"
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||
|| return 1
|
||||
|
||||
# Check for runtime directory of nonvolatile data
|
||||
if [ ! -d /var/lib/osmo-ggsn ]; then
|
||||
mkdir /var/lib/osmo-ggsn
|
||||
fi
|
||||
|
||||
# Check for GTP restart counter
|
||||
if [ ! -f /var/lib/osmo-ggsn/gsn_restart ]; then
|
||||
echo 0 > /var/lib/osmo-ggsn/gsn_restart
|
||||
fi
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
||||
$DAEMON_ARGS \
|
||||
|| return 2
|
||||
# Add code here, if necessary, that waits for the process to be ready
|
||||
# to handle requests from services started subsequently which depend
|
||||
# on this one. As a last resort, sleep for some time.
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
|
||||
RETVAL="$?"
|
||||
[ "$RETVAL" = 2 ] && return 2
|
||||
# Wait for children to finish too if this is a daemon that forks
|
||||
# and if the daemon is only ever run from this initscript.
|
||||
# If the above conditions are not satisfied then add some other code
|
||||
# that waits for the process to drop all resources that could be
|
||||
# needed by services started subsequently. A last resort is to
|
||||
# sleep for some time.
|
||||
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||
[ "$?" = 2 ] && return 2
|
||||
# Many daemons don't delete their pidfiles when they exit.
|
||||
rm -f $PIDFILE
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#log_end_msg $?
|
||||
#;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
# 'force-reload' alias
|
||||
#
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
39
debian/postinst
vendored
Executable file
39
debian/postinst
vendored
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.13.0"; then
|
||||
if [ -e /etc/osmocom/osmo-ggsn.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-ggsn.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-ggsn.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
||||
|
||||
7
debian/rules
vendored
7
debian/rules
vendored
@@ -16,10 +16,13 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
|
||||
dh_strip -plibgtp6 --dbg-package=libgtp-dbg
|
||||
dh_strip -plibgtp11 --dbg-package=libgtp-dbg
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
dh_auto_configure -- \
|
||||
--enable-gtp-linux \
|
||||
--with-systemdsystemunitdir=/lib/systemd/system \
|
||||
--enable-manuals
|
||||
|
||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||
override_dh_compress:
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
OSMOCONF_FILES = \
|
||||
osmo-ggsn.cfg \
|
||||
$(NULL)
|
||||
|
||||
osmoconfdir = $(sysconfdir)/osmocom
|
||||
osmoconf_DATA = osmo-ggsn.cfg
|
||||
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||
|
||||
EXTRA_DIST = osmo-ggsn.cfg
|
||||
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||
|
||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||
CFG_FILES = find $(srcdir) -name '*.cfg' -o -name '*.conf' | sed -e 's,^$(srcdir),,'
|
||||
|
||||
dist-hook:
|
||||
for f in $$($(CFG_FILES)); do \
|
||||
|
||||
@@ -13,36 +13,36 @@
|
||||
# to and from the Gn interface.
|
||||
# * Masquerede on Gi interface.
|
||||
|
||||
IPTABLES="/sbin/iptables"
|
||||
NFT="nft"
|
||||
IFGN="eth0"
|
||||
IFGI="eth1"
|
||||
|
||||
$IPTABLES -P INPUT DROP
|
||||
$IPTABLES -P FORWARD ACCEPT
|
||||
$IPTABLES -P OUTPUT ACCEPT
|
||||
$NFT add chain ip filter input '{ policy drop; }'
|
||||
$NFT add chain ip filter forward '{ policy accept; }'
|
||||
$NFT add chain ip filter output '{ policy accept; }'
|
||||
|
||||
#Allow related and established on all interfaces (input)
|
||||
$IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
$NFT add rule ip filter input ct state related,established counter accept
|
||||
|
||||
#Allow releated, established, GTP and ssh on $IFGN. Reject everything else.
|
||||
$IPTABLES -A INPUT -i $IFGN -p tcp -m tcp --dport 22 --syn -j ACCEPT
|
||||
$IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 2123 -j ACCEPT
|
||||
$IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 2152 -j ACCEPT
|
||||
$IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 3386 -j ACCEPT
|
||||
$IPTABLES -A INPUT -i $IFGN -j REJECT
|
||||
$NFT add rule ip filter input iifname $IFGN tcp dport 22 tcp flags syn / fin,syn,rst,ack counter accept
|
||||
$NFT add rule ip filter input iifname $IFGN udp dport 2123 counter accept
|
||||
$NFT add rule ip filter input iifname $IFGN udp dport 2152 counter accept
|
||||
$NFT add rule ip filter input iifname $IFGN udp dport 3386 counter accept
|
||||
$NFT add rule ip filter input iifname $IFGN counter reject
|
||||
|
||||
#Allow related, established and ssh. Drop everything else.
|
||||
$IPTABLES -A INPUT -i $IFGI -p tcp -m tcp --dport 22 --syn -j ACCEPT
|
||||
$IPTABLES -A INPUT -i $IFGI -j DROP
|
||||
$NFT add rule ip filter input iifname $IFGI tcp dport 22 tcp flags syn / fin,syn,rst,ack counter accept
|
||||
$NFT add rule ip filter input iifname $IFGI counter drop
|
||||
|
||||
# Masquerade everything going out on $IFGI
|
||||
$IPTABLES -t nat -A POSTROUTING -o $IFGI -j MASQUERADE
|
||||
$NFT add rule ip nat POSTROUTING oifname $IFGI counter masquerade
|
||||
|
||||
#Allow everything on loopback interface.
|
||||
$IPTABLES -A INPUT -i lo -j ACCEPT
|
||||
$NFT add rule ip filter input iifname "lo" counter accept
|
||||
|
||||
# Drop everything to and from $IFGN (forward)
|
||||
$IPTABLES -A FORWARD -i $IFGN -j DROP
|
||||
$IPTABLES -A FORWARD -o $IFGN -j DROP
|
||||
$NFT add rule ip filter forward iifname $IFGN counter drop
|
||||
$NFT add rule ip filter forward oifname $IFGN counter drop
|
||||
|
||||
|
||||
|
||||
54
doc/examples/osmo-ggsn-kernel-gtp.cfg
Normal file
54
doc/examples/osmo-ggsn-kernel-gtp.cfg
Normal file
@@ -0,0 +1,54 @@
|
||||
!
|
||||
! OpenGGSN (0.94.1-adac) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level ip info
|
||||
logging level tun info
|
||||
logging level ggsn info
|
||||
logging level sgsn notice
|
||||
logging level icmp6 notice
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
logging level lctrl notice
|
||||
logging level lgtp info
|
||||
logging level lstats notice
|
||||
logging level lgsup notice
|
||||
logging level loap notice
|
||||
logging level lss7 notice
|
||||
logging level lsccp notice
|
||||
logging level lsua notice
|
||||
logging level lm3ua notice
|
||||
logging level lmgcp notice
|
||||
!
|
||||
stats interval 5
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
ggsn ggsn0
|
||||
gtp state-dir /var/lib/osmocom/osmo-ggsn
|
||||
gtp bind-ip 127.0.0.2
|
||||
apn internet
|
||||
gtpu-mode kernel-gtp
|
||||
tun-device tun4
|
||||
type-support v4
|
||||
mtu default apply
|
||||
ip prefix dynamic 172.16.222.0/24
|
||||
ip dns 0 8.8.8.8
|
||||
ip dns 1 8.8.4.4
|
||||
ip ifconfig 172.16.222.0/24
|
||||
no shutdown
|
||||
default-apn internet
|
||||
no shutdown ggsn
|
||||
@@ -3,10 +3,12 @@
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category 0
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level ip info
|
||||
logging level tun info
|
||||
logging level ggsn info
|
||||
@@ -36,21 +38,23 @@ line vty
|
||||
no login
|
||||
!
|
||||
ggsn ggsn0
|
||||
gtp state-dir /tmp
|
||||
gtp state-dir /var/lib/osmocom/osmo-ggsn
|
||||
gtp bind-ip 127.0.0.2
|
||||
apn internet
|
||||
gtpu-mode tun
|
||||
tun-device tun4
|
||||
type-support v4
|
||||
ip prefix dynamic 176.16.222.0/24
|
||||
mtu default apply
|
||||
ip prefix dynamic 172.16.222.0/24
|
||||
ip dns 0 8.8.8.8
|
||||
ip dns 1 8.8.4.4
|
||||
ip ifconfig 176.16.222.0/24
|
||||
ip ifconfig 172.16.222.0/24
|
||||
no shutdown
|
||||
apn inet6
|
||||
gtpu-mode tun
|
||||
tun-device tun6
|
||||
type-support v6
|
||||
mtu default apply
|
||||
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
|
||||
@@ -60,10 +64,11 @@ ggsn ggsn0
|
||||
gtpu-mode tun
|
||||
tun-device tun46
|
||||
type-support v4v6
|
||||
ip prefix dynamic 176.16.46.0/24
|
||||
mtu default apply
|
||||
ip prefix dynamic 172.16.46.0/24
|
||||
ip dns 0 8.8.8.8
|
||||
ip dns 1 8.8.4.4
|
||||
ip ifconfig 176.16.46.0/24
|
||||
ip ifconfig 172.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
|
||||
|
||||
@@ -11,6 +11,12 @@ if BUILD_MANUALS
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmoggsn-vty-reference.xml
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/ggsn_vty_reference.xml
|
||||
$(builddir)/vty/ggsn_vty_reference.xml: $(top_builddir)/ggsn/osmo-ggsn
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/ggsn/osmo-ggsn --vty-ref-xml > $@
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
OSMO_REPOSITORY=osmo-ggsn
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[[osmoggsn_configuring]]
|
||||
== Configuring OsmoGGSN
|
||||
|
||||
All configuration of OsmoGGSN is performed using the VTY. For more
|
||||
@@ -16,12 +17,13 @@ your configuration file, like in below example:
|
||||
.Example: Single GGSN configuration section
|
||||
----
|
||||
ggsn ggsn0
|
||||
gtp state-dir /tmp
|
||||
gtp state-dir /var/lib/osmocom/osmo-ggsn
|
||||
gtp bind-ip 127.0.0.6
|
||||
apn internet
|
||||
gtpu-mode tun
|
||||
tun-device tun4
|
||||
type-support v4
|
||||
mtu default apply
|
||||
ip prefix dynamic 176.16.222.0/24
|
||||
ip dns 0 192.168.100.1
|
||||
ip dns 1 8.8.8.8
|
||||
@@ -58,7 +60,7 @@ The following two mandatory configuration statements have to be given
|
||||
for every GGSN instance:
|
||||
|
||||
----
|
||||
OsmoGGSN(config-ggsn)# gtp state-dir /var/lib/ggsn/ggsn0 <1>
|
||||
OsmoGGSN(config-ggsn)# gtp state-dir /var/lib/osmocom/osmo-ggsn <1>
|
||||
OsmoGGSN(config-ggsn)# gtp bind-ip 127.0.0.6 <2>
|
||||
----
|
||||
<1> Store the GSN restart state in the specified directory
|
||||
@@ -101,7 +103,7 @@ OsmoGGSN(config-ggsn)# no shutdown ggsn <4>
|
||||
----
|
||||
<1> Change into privileged mode
|
||||
<2> Enter the interactive configuration mode
|
||||
<3> Enter the config ndoe of the GGSN instance `ggsn0`
|
||||
<3> Enter the config node of the GGSN instance `ggsn0`
|
||||
<4> Take the GGSN instance out of shutdown
|
||||
|
||||
|
||||
@@ -121,7 +123,7 @@ OsmoGGSN(config-ggsn)# shutdown ggsn <4>
|
||||
----
|
||||
<1> Change into privileged mode
|
||||
<2> Enter the interactive configuration mode
|
||||
<3> Enter the config ndoe of the GGSN instance `ggsn0`
|
||||
<3> Enter the config node of the GGSN instance `ggsn0`
|
||||
<4> Shut down the GGSN instance
|
||||
|
||||
|
||||
@@ -162,18 +164,20 @@ out of shutdown.
|
||||
----
|
||||
OsmoGGSN(config-ggsn-apn)# gtpu-mode tun <1>
|
||||
OsmoGGSN(config-ggsn-apn)# type-support v4 <2>
|
||||
OsmoGGSN(config-ggsn-apn)# ip prefix dynamic 176.16.222.0/24 <3>
|
||||
OsmoGGSN(config-ggsn-apn)# ip dns 0 192.168.100.1 <4>
|
||||
OsmoGGSN(config-ggsn-apn)# ip dns 1 8.8.8.8 <5>
|
||||
OsmoGGSN(config-ggsn-apn)# ip ifconfig 176.16.222.0/24 <6>
|
||||
OsmoGGSN(config-ggsn-apn)# mtu 1420 apply <3>
|
||||
OsmoGGSN(config-ggsn-apn)# ip prefix dynamic 176.16.222.0/24 <4>
|
||||
OsmoGGSN(config-ggsn-apn)# ip dns 0 192.168.100.1 <5>
|
||||
OsmoGGSN(config-ggsn-apn)# ip dns 1 8.8.8.8 <6>
|
||||
OsmoGGSN(config-ggsn-apn)# ip ifconfig 176.16.222.0/24 <7>
|
||||
----
|
||||
<1> Use the userspace GTP-U handling using a TUN device
|
||||
<2> Support (only) IPv4 Addresses
|
||||
<3> Specify the pool of dynamic IPv4 addresses to be allocated to PDP
|
||||
<3> Specify MTU to announce to MS. Apply the MTU on the tunnel interface.
|
||||
<4> Specify the pool of dynamic IPv4 addresses to be allocated to PDP
|
||||
contexts
|
||||
<4> Specify the primary DNS server to be provided using IPCP/PCO
|
||||
<5> Specify the secondary DNS server to be provided using IPCP/PCO
|
||||
<6> Request OsmoGGSN to configure the `tun4` device network/netmask
|
||||
<5> Specify the primary DNS server to be provided using IPCP/PCO
|
||||
<6> Specify the secondary DNS server to be provided using IPCP/PCO
|
||||
<7> Request OsmoGGSN to configure the `tun4` device network/netmask
|
||||
|
||||
NOTE:: If you use the optional `ip ifconfig` command to set the network
|
||||
device address/mask, OsmoGGSN must run with root or `CAP_NET_ADMIN`
|
||||
@@ -215,8 +219,8 @@ OsmoGGSN(config-ggsn-apn)# no shutdown <5>
|
||||
----
|
||||
<1> Change into privileged mode
|
||||
<2> Enter the interactive configuration mode
|
||||
<3> Enter the config ndoe of the GGSN instance `ggsn0`
|
||||
<4> Enter the config ndoe of the APN `internet`
|
||||
<3> Enter the config node of the GGSN instance `ggsn0`
|
||||
<4> Enter the config node of the APN `internet`
|
||||
<5> Take the APN out of shutdown
|
||||
|
||||
|
||||
@@ -237,8 +241,8 @@ OsmoGGSN(config-ggsn-apn)# shutdown <5>
|
||||
----
|
||||
<1> Change into privileged mode
|
||||
<2> Enter the interactive configuration mode
|
||||
<3> Enter the config ndoe of the GGSN instance `ggsn0`
|
||||
<4> Enter the config ndoe of the APN `internet`
|
||||
<3> Enter the config node of the GGSN instance `ggsn0`
|
||||
<4> Enter the config node of the APN `internet`
|
||||
<5> Shut down the APN
|
||||
|
||||
[[ggsn_no_root]]
|
||||
@@ -271,6 +275,7 @@ to match the `ip prefix dynamic` config item, and activate the link, for example
|
||||
|
||||
----
|
||||
# ip addr add 192.168.7.1/24 dev apn0
|
||||
# ip link set mtu 1420 dev apn0
|
||||
# ip link set apn0 up
|
||||
----
|
||||
|
||||
@@ -298,13 +303,18 @@ Group=username <3>
|
||||
[Match]
|
||||
Name=apn0 <1>
|
||||
|
||||
[Link]
|
||||
MTUBytes=1420 <2>
|
||||
|
||||
[Network]
|
||||
Address=192.168.7.1/24 <2>
|
||||
IPMasquerade=yes <3>
|
||||
Address=192.168.7.1/24 <3>
|
||||
IPMasquerade=yes <4>
|
||||
----
|
||||
<1> The netowrk device name, which must match the one in the apn0.netdev unit file above
|
||||
<2> The local IP address configured on the device
|
||||
<3> Requesting systemd to configure IP masquerading for this interface. Depending on your needs,
|
||||
<1> The network device name, which must match the one in the apn0.netdev unit file above
|
||||
<2> Requesting systemd to set the MTU for this interface. The MTU of the tun
|
||||
interface should be lower than regular, since it must accommodate the extra IP/UDP/GTPv1U headers.
|
||||
<3> The local IP address configured on the device
|
||||
<4> Requesting systemd to configure IP masquerading for this interface. Depending on your needs,
|
||||
You may not want this if you have proper end-to-end routing set up, and want to have transparent
|
||||
inbound IP access to your GPRS-attached devices.
|
||||
|
||||
@@ -326,6 +336,7 @@ ggsn ggsn0
|
||||
gtpu-mode tun
|
||||
tun-device apn0
|
||||
type-support v4
|
||||
mtu 1420
|
||||
ip prefix dynamic 192.168.7.0/24
|
||||
ip dns 0 192.168.100.1
|
||||
ip dns 1 8.8.8.8
|
||||
|
||||
171
doc/manuals/chapters/mtu.adoc
Normal file
171
doc/manuals/chapters/mtu.adoc
Normal file
@@ -0,0 +1,171 @@
|
||||
=== MTU considerations
|
||||
|
||||
When running OsmoGGSN, the user may want to take network Maximum Transmission
|
||||
Unit (MTU) into consideration, and configure it based on network specific setup.
|
||||
|
||||
Applying and announcing a proper MTU provides, for the MS employing it, reduced
|
||||
transmission overhead (ie. due to IP fragmentation) and avoids potential
|
||||
problems due to misconfigured nodes in the path (e.g. ICMP packet filtering).
|
||||
|
||||
In OsmoGGSN, the MTU can be configured per APN through the VTY, see
|
||||
<<osmoggsn_configuring>>. If told so by the config, osmo-ggsn will apply the MTU
|
||||
on the APN network interface.
|
||||
|
||||
==== MTU announced to MS
|
||||
|
||||
The configured MTU is also announced to the MS through:
|
||||
|
||||
* IPv4 APN: GTPv1C Create PDP Context Response, PCO IE "IPv4 Link MTU", 3GPP TS
|
||||
24.008 Table 10.5.154.
|
||||
* IPv6 APN: ICMPv6 Routing Advertisement during IPv6 SLAAC procedure, RFC 4861.
|
||||
|
||||
NOTE: It is up to the MS to request and use the link MTU size provided by the
|
||||
network. Hence, providing an MTU size does not guarantee that there will be no
|
||||
packets larger than the provided value.
|
||||
|
||||
==== GTP-U tunnel overhead
|
||||
|
||||
OsmoGGSN is encapsulating traffic over GTP-U, it means the packets being received,
|
||||
encapsulated and transmitted over the tunnel get their size increased by the sum of
|
||||
IP/UDP/GTPv1U headers being prepended:
|
||||
|
||||
* IP: IPv4 headers can take up to 60 bytes (due to IPv4 options). IPv6 headers
|
||||
can take up to 40 bytes (assuming no extension headers for IPv6 in general,
|
||||
since they are uncommon). Hence, the 60 bytes of IPv4 are picked since that's
|
||||
greater than the IPv4.
|
||||
* UDP: The UDP header takes 8 bytes.
|
||||
* GTPv1U: The GTPv1U header takes 12 bytes, assuming here no extensions headers
|
||||
are used (OsmoGGSN doesn't use them).
|
||||
|
||||
Hence, these headers add an overhead of up to `80`` bytes, as per the below formula:
|
||||
|
||||
----
|
||||
GTPv1U_OVERHEAD = 60 + 8 + 12 = 80 bytes
|
||||
----
|
||||
|
||||
==== Figuring out optimal MTU value
|
||||
|
||||
There is no golden MTU value, since it really depends on the local (and remote)
|
||||
networks where traffic is routed. The idea is finding out a value that:
|
||||
|
||||
* Is as big as possible, to avoid need to split big chunks of data into lots of
|
||||
small packets, hence affecting performance due to processing overhead: extra
|
||||
headers being trnasmitted, plus processing of extra packets.
|
||||
* Is small enough so that it can be transported over the lower layers of the
|
||||
links involving the communication, avoiding IP fragmentation, which again hurts
|
||||
performance.
|
||||
|
||||
OsmoGGSN, by default, assumes that traffic is transported over an Ethernet
|
||||
network, which has a standarized maximum MTU of 1500 bytes. Hence, by default it
|
||||
announces an MTU of of `1420` bytes as per the following formula:
|
||||
|
||||
----
|
||||
TUNNEL_MTU = ETH_MTU - GTPv1U_OVERHEAD = 1500 - 80 = 1420 bytes
|
||||
----
|
||||
|
||||
Under certain networks, the base MTU may already be smaller than Ethernet's MTU
|
||||
(1500 bytes), due to, for instance, existence of some sort of extra tunneling
|
||||
protocol in the path, such as a VPN, ipsec, extra VLAN levels, etc. Under this
|
||||
scenario, the user must take care of figuring out the new base MTU value to use
|
||||
for the calculations presented above. This can be accomplished by packet
|
||||
inspection (eg. `wireshark`) or with tools such as `ping`, running it with a
|
||||
certain packet size and the IPv4 DF bit set, and see up to which packet size the
|
||||
networks is able to forward the message.
|
||||
|
||||
.Example: Test if packets of 1420 bytes can reach peer host 176.16.222.4
|
||||
----
|
||||
$ ping -M probe 176.16.222.4 -s 1420
|
||||
----
|
||||
|
||||
=== Increasing outer MTU
|
||||
|
||||
Specifications at IEEE 802.3 establish that standard Ethernet has a maximum MTU
|
||||
of `1500` bytes.
|
||||
However, many Ethernet controllers can nowadays overcome this limit and allow
|
||||
the use of so called _jumbo frames_. The _jumbo frames_ maximum MTU varies
|
||||
depending on the implementation, with `9000` bytes being a commonly used limit.
|
||||
|
||||
Note that using MTUs over the standarized `1500` bytes by means of _jumbo frames_
|
||||
can create interoperability problems with networks not supporting such frames
|
||||
(eg. forcing of IP packet fragmentation), plus the fact that larger frames
|
||||
consume more Ethernet link transmission time, causing greater delays and
|
||||
increasing latency.
|
||||
|
||||
Nevertheless, if the operator:
|
||||
|
||||
* is in control of the whole GTP-U path between OsmoGGSN and the MS, and
|
||||
* has Ethernet NICs supporting MTUs bigger than 1500 or uses any other link
|
||||
layer supporting as well bigger MTUs.
|
||||
|
||||
Then, it may be wise for the operator to configure such links with an increased
|
||||
outer MTU so that they can end up transporting GTP-U inner payload of 1500 bytes
|
||||
without fragmentation ocurring.
|
||||
|
||||
Hence, following the examples presented on the above sections, one could
|
||||
configure *all the links* which are part of the GTP-U path to use an outer MTU
|
||||
of `1580` bytes, as per the following formula:
|
||||
|
||||
----
|
||||
TUNNEL_MTU = ETH_MTU + GTPv1U_OVERHEAD = 1500 + 80 = 1580 bytes
|
||||
----
|
||||
|
||||
.Example: Setting an MTU of `1580` to network interface `eth0` under Linux
|
||||
----
|
||||
ip link set mtu 1580 dev eth0
|
||||
----
|
||||
|
||||
==== TCP MSS Clamping
|
||||
|
||||
Usually endpoints use Path MTU Discovery (PMTUD) to determine the maximum MTU to
|
||||
reach the peer. However, this technique may sometimes not be optimal for all
|
||||
users of OsmoGGSN:
|
||||
|
||||
* MS may not support requesting and/or configuring the MTU OsmoGGSN announced.
|
||||
* MS may not support PMTUD on its network stack, or may not have it enabled or
|
||||
may be buggy.
|
||||
* Network may be misconfigured or some middlebox may be buggy (eg. not
|
||||
forwarding ICMP `Packet Too Big` packets).
|
||||
|
||||
Furthermore, PMTUD takes time to figure out the maximum MTU to use, since it
|
||||
relies on sending data and checking if it got lost, and adapting to the fact,
|
||||
reducing efficiency (throughput) of connections or even stalling them completely
|
||||
when big packets are generated.
|
||||
|
||||
Hence, it may become useful for the operator of OsmoGGSN to, on top of MTU
|
||||
configuration, also configure its network to tune TCP Maximum Segment Size (MSS)
|
||||
option of TCP connections being established over the GTPv1U tunnel. This will
|
||||
make sure at least TCP connections can use the full capacity of the path MTU
|
||||
without passing its imit.
|
||||
|
||||
The MSS TCP option is an optional parameter in the TCP header sent during TCP
|
||||
initial handshake (`SYN,SYN/ACK`) that specifies the maximum amount of bytes of
|
||||
TCP payload a TCP chunk may transport. The MSS value doesn't count the
|
||||
underlaying IP/TCP headers.
|
||||
|
||||
Hence, following up on MTU size calculations from previous section, with a
|
||||
sample GTPv1U MTU of 1420 bytes and IP header of 60 bytes, plus taking into
|
||||
account that TCP header can span up to 56 bytes, we'd get to an MSS value of:
|
||||
|
||||
----
|
||||
MSS = TUNNEL_MTU - IP_HDR - TCP_HDR = 1420 - 60 - 56 = 1304
|
||||
----
|
||||
|
||||
In linux, the MSS of TCP connections can be clamped using nftables:
|
||||
|
||||
----
|
||||
nft 'add rule ip nat prerouting iifname "apn0" tcp flags syn / syn,rst counter tcp option maxseg size set 1304'
|
||||
nft 'insert rule ip nat postrouting oifname "apn0" tcp flags syn / syn,rst counter tcp option maxseg size set 1304'
|
||||
nft 'add rule ip6 nat prerouting iifname "apn0" tcp flags syn / syn,rst counter tcp option maxseg size set 1304'
|
||||
nft 'insert rule ip6 nat postrouting oifname "apn0" tcp flags syn / syn,rst counter tcp option maxseg size set 1304'
|
||||
----
|
||||
|
||||
==== Further Reading
|
||||
|
||||
Check the following specs regarding MTU in 3GPP mobile networks:
|
||||
|
||||
* 3GPP TS 29.061 section 11.2.1.5
|
||||
* 3GPP TS 290.060 section 13.2 IP Fragmentation
|
||||
* 3GPP TS 25.414 section 6.1.3.3
|
||||
* 3GPP TS 23.060 section 9.3, Annex C
|
||||
* 3GPP TS 24.008 (PCO IPv4 MTU)
|
||||
* RFC 4861 (IPv6 Router Advertisement)
|
||||
@@ -43,11 +43,6 @@ configuration file, by default located in *./osmo-ggsn.cfg*
|
||||
In *contrib/osmo-ggsn.service* you can find a sample service file for
|
||||
OsmoGGSN which can be used with systemd.
|
||||
|
||||
==== init script
|
||||
|
||||
In *contrib/osmo-ggsn.init* you can find a sample init script to be used
|
||||
on systems with classic init process.
|
||||
|
||||
=== Limitations
|
||||
|
||||
OsmoGGSN supports both GTP0 (GSM 09.60) and GTP1 (3GPP 29.060). In the
|
||||
|
||||
@@ -43,20 +43,22 @@ To manually enable IPv4 forwarding and masquerading ad-hoc, you can do:
|
||||
|
||||
----
|
||||
sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
iptables -t nat -A POSTROUTING -o '*' -j MASQUERADE
|
||||
nft 'add rule ip nat postrouting oifname "\*" counter masquerade'
|
||||
----
|
||||
|
||||
(You may want to replace `*` with the network device name, like `-o eth0`)
|
||||
|
||||
There are various ways to enable these settings persistently, please refer to
|
||||
your distribution's documentation -- e.g. look for @net.ipv4.ip_forward=1@ in
|
||||
@/etc/sysctl.d/@, and https://wiki.debian.org/iptables for masquerading.
|
||||
@/etc/sysctl.d/@, and https://wiki.debian.org/nftables for masquerading.
|
||||
|
||||
include::{srcdir}/chapters/mtu.adoc[]
|
||||
|
||||
=== Multiple instances
|
||||
|
||||
Running multiple instances of `osmo-ggsn` is possible if all GGSN instances
|
||||
are binding to different local IP addresse and all other interfaces (VTY,
|
||||
OML) are separated using the appropriate configuration options. The IP based
|
||||
are binding to different local IP addresses and all other interfaces (VTY,
|
||||
CTRL) are separated using the appropriate configuration options. The IP based
|
||||
interfaces are binding to local host by default. In order to separate the
|
||||
processes, the user has to bind those services to specific but different
|
||||
IP addresses.
|
||||
@@ -80,3 +82,52 @@ possible to pick differing ports on the same IP address), like:
|
||||
ggsn ggsn0
|
||||
gtp bind-ip 127.0.0.2
|
||||
----
|
||||
|
||||
=== GTP-U kernel module
|
||||
|
||||
WARNING: As of writing, IPv6 support for the kernel module has not been
|
||||
upstreamed yet (OS#1952).
|
||||
|
||||
WARNING: As of writing, it is not possible to configure multiple APNs with
|
||||
gtpu-mode kernel-gpt. This is a limitation in OsmoGGSN, not in the
|
||||
kernel module (OS#6106).
|
||||
|
||||
OsmoGGSN has support to use the Linux kernel GTP-U tunnel driver to accelerate
|
||||
the data/user plane while still implementing the control plane (GTP-C) in
|
||||
userspace in OsmoGGSN. The kernel module is included in Linux 4.7.0 and higher.
|
||||
Notably the Debian GNU/Linux distribution has it enabled by default.
|
||||
|
||||
In order to use this feature, make sure that your Linux kernel was configured
|
||||
to support it (`CONFIG_GTP=m` or `=y`). Furthermore, `osmo-ggsn` must have been
|
||||
built with `./configure` argument `--enable-gtp-linux` (which requires libgtpnl
|
||||
to be installed).
|
||||
|
||||
Load the kernel module with:
|
||||
|
||||
----
|
||||
$ sudo modprobe gtp
|
||||
----
|
||||
|
||||
Then start OsmoGGSN with a configuration file that uses `gtpu-mode kernel-gtp`.
|
||||
|
||||
A full example configuration is in `osmo-ggsn-kernel-gtp.cfg`.
|
||||
|
||||
----
|
||||
$ sudo osmo-ggsn -c /usr/share/doc/osmo-ggsn/examples/osmo-ggsn-kernel-gtp.cfg
|
||||
----
|
||||
|
||||
.Example: APN with kernel-gtp
|
||||
----
|
||||
ggsn ggsn0
|
||||
gtp state-dir /tmp
|
||||
gtp bind-ip 127.0.0.2
|
||||
apn internet
|
||||
gtpu-mode kernel-gtp
|
||||
tun-device tun4
|
||||
type-support v4
|
||||
ip prefix dynamic 172.16.222.0/24
|
||||
ip dns 0 8.8.8.8
|
||||
ip dns 1 8.8.4.4
|
||||
ip ifconfig 172.16.222.0/24
|
||||
no shutdown
|
||||
----
|
||||
|
||||
@@ -11,15 +11,16 @@ include::{srcdir}/chapters/running.adoc[]
|
||||
|
||||
//include::{srcdir}/chapters/control.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::./common/chapters/vty.adoc[]
|
||||
|
||||
include::./common/chapters/logging.adoc[]
|
||||
|
||||
|
||||
include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::./common/chapters/control_if.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,16 @@ bin_PROGRAMS = osmo-ggsn
|
||||
|
||||
AM_LDFLAGS = @EXEC_LDFLAGS@
|
||||
|
||||
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_CFLAGS = \
|
||||
-D_GNU_SOURCE \
|
||||
-fno-builtin \
|
||||
-Wall \
|
||||
-DSBINDIR='"$(sbindir)"' \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
osmo_ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
|
||||
|
||||
252
ggsn/ggsn.c
252
ggsn/ggsn.c
@@ -46,19 +46,22 @@
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../lib/gtp-kernel.h"
|
||||
#include "../lib/util.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../lib/icmpv6.h"
|
||||
#include "pco.h"
|
||||
#include "ggsn.h"
|
||||
#include "../gtp/gtp_internal.h"
|
||||
|
||||
LLIST_HEAD(g_ggsn_list);
|
||||
|
||||
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
|
||||
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
|
||||
|
||||
void ggsn_close_one_pdp(struct pdp_t *pdp)
|
||||
@@ -92,6 +95,43 @@ static void pool_close_all_pdp(struct ippool_t *pool)
|
||||
}
|
||||
}
|
||||
|
||||
static struct apn_ctx *apn_alloc(struct ggsn_ctx *ggsn, const char *name)
|
||||
{
|
||||
struct apn_ctx *apn;
|
||||
|
||||
apn = talloc_zero(ggsn, struct apn_ctx);
|
||||
OSMO_ASSERT(apn);
|
||||
|
||||
apn->ggsn = ggsn;
|
||||
apn->cfg.name = talloc_strdup(apn, name);
|
||||
apn->cfg.shutdown = true;
|
||||
apn->cfg.tx_gpdu_seq = true;
|
||||
apn->cfg.mtu = MAX_DESIRED_APN_MTU;
|
||||
INIT_LLIST_HEAD(&apn->cfg.name_list);
|
||||
|
||||
llist_add_tail(&apn->list, &ggsn->apn_list);
|
||||
return apn;
|
||||
}
|
||||
|
||||
struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
|
||||
{
|
||||
struct apn_ctx *apn;
|
||||
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list) {
|
||||
if (!strcmp(apn->cfg.name, name))
|
||||
return apn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
|
||||
{
|
||||
struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
|
||||
if (!apn)
|
||||
apn = apn_alloc(ggsn, name);
|
||||
return apn;
|
||||
}
|
||||
|
||||
int apn_stop(struct apn_ctx *apn)
|
||||
{
|
||||
LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
|
||||
@@ -109,7 +149,6 @@ int apn_stop(struct apn_ctx *apn)
|
||||
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;
|
||||
@@ -187,26 +226,18 @@ int apn_start(struct apn_ctx *apn)
|
||||
switch (apn->cfg.gtpu_mode) {
|
||||
case APN_GTPU_MODE_TUN:
|
||||
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
|
||||
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
|
||||
apn->tun.tun = tun_alloc_tundev(apn->tun.cfg.dev_name);
|
||||
if (!apn->tun.tun) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
|
||||
return -1;
|
||||
}
|
||||
LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", apn->tun.tun->devname);
|
||||
|
||||
/* Register with libosmcoore */
|
||||
osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, BSC_FD_READ, ggsn_tun_fd_cb, apn, 0);
|
||||
osmo_fd_register(&apn->tun.fd);
|
||||
|
||||
/* Set TUN library callback */
|
||||
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
|
||||
break;
|
||||
case APN_GTPU_MODE_KERNEL_GTP:
|
||||
LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
|
||||
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Kernel GTP currently supports only IPv4\n");
|
||||
apn_stop(apn);
|
||||
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
|
||||
@@ -216,7 +247,8 @@ int apn_start(struct apn_ctx *apn)
|
||||
return 0;
|
||||
}
|
||||
/* use GTP kernel module for data packet encapsulation */
|
||||
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
|
||||
apn->tun.tun = tun_alloc_gtpdev(apn->tun.cfg.dev_name, gsn->fd0, gsn->fd1u);
|
||||
if (!apn->tun.tun) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -234,7 +266,7 @@ int apn_start(struct apn_ctx *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,
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr,
|
||||
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));
|
||||
@@ -246,7 +278,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
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,
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr,
|
||||
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",
|
||||
@@ -259,7 +291,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
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,
|
||||
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr,
|
||||
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",
|
||||
@@ -270,6 +302,23 @@ int apn_start(struct apn_ctx *apn)
|
||||
apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
|
||||
}
|
||||
|
||||
rc = osmo_netdev_ifupdown(apn->tun.tun->netdev, true);
|
||||
if (rc < 0) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun interface UP: %s\n", strerror(errno));
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (apn->tun.cfg.mtu_apply) {
|
||||
rc = osmo_netdev_set_mtu(apn->tun.tun->netdev, apn->cfg.mtu);
|
||||
if (rc < 0) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun interface MTU %u: %s\n",
|
||||
apn->cfg.mtu, strerror(errno));
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->tun.cfg.ipup_script) {
|
||||
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
|
||||
apn->tun.cfg.ipup_script);
|
||||
@@ -428,7 +477,7 @@ static struct sgsn_peer* ggsn_find_or_create_sgsn(struct ggsn_ctx *ggsn, struct
|
||||
return sgsn;
|
||||
}
|
||||
|
||||
int create_context_ind(struct pdp_t *pdp)
|
||||
static int create_context_ind(struct pdp_t *pdp)
|
||||
{
|
||||
static char name_buf[256];
|
||||
struct gsn_t *gsn = pdp->gsn;
|
||||
@@ -467,9 +516,13 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: we manually force all context requests to dynamic here! */
|
||||
if (pdp->eua.l > 2)
|
||||
pdp->eua.l = 2;
|
||||
/* FIXME: implement context request for static IP addresses! */
|
||||
if (pdp->eua.l > 2) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Static IP addresses not supported: %s\n",
|
||||
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NOT_SUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(pdp->qos_neg0, pdp->qos_req0, sizeof(pdp->qos_req0));
|
||||
|
||||
@@ -532,10 +585,11 @@ int create_context_ind(struct pdp_t *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 (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
|
||||
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));
|
||||
if (addrv6 && errno == EINVAL)
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Maybe your kernel does not support GTP-U with IPv6 yet?\n");
|
||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
|
||||
return 0;
|
||||
}
|
||||
@@ -580,7 +634,43 @@ err_wrong_af:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Internet-originated IP packet, needs to be sent via GTP towards MS */
|
||||
static int update_context_ind(struct pdp_t *pdp)
|
||||
{
|
||||
char apn_name[256];
|
||||
struct gsn_t *gsn = pdp->gsn;
|
||||
struct ggsn_ctx *ggsn = gsn->priv;
|
||||
struct apn_ctx *apn;
|
||||
bool apn_found = false;
|
||||
int rc;
|
||||
|
||||
if (!osmo_apn_to_str(apn_name, pdp->apn_use.v, pdp->apn_use.l)) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Unable to decode associated APN len=%d buf: %s\n",
|
||||
pdp->apn_use.l, osmo_hexdump(pdp->apn_use.v, pdp->apn_use.l));
|
||||
return gtp_update_context_resp(ggsn->gsn, pdp, GTPCAUSE_MISSING_APN);
|
||||
}
|
||||
|
||||
llist_for_each_entry (apn, &ggsn->apn_list, list) {
|
||||
if (strncmp(apn_name, apn->cfg.name, sizeof(apn_name)) != 0)
|
||||
continue;
|
||||
apn_found = true;
|
||||
break;
|
||||
}
|
||||
if (!apn_found) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Unable to find associated APN %s\n", apn_name);
|
||||
return gtp_update_context_resp(ggsn->gsn, pdp, GTPCAUSE_MISSING_APN);
|
||||
}
|
||||
|
||||
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
|
||||
/* Update the kernel with the potentially new remote data IP address + TEID */
|
||||
gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name);
|
||||
gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name);
|
||||
}
|
||||
rc = gtp_update_context_resp(ggsn->gsn, pdp, GTPCAUSE_ACC_REQ);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Rx Internet-originated IP packet from our tun iface, needs to be sent via
|
||||
* GTPU towards MS. */
|
||||
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
{
|
||||
struct apn_ctx *apn = tun->priv;
|
||||
@@ -589,7 +679,7 @@ 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];
|
||||
char straddr[2][INET6_ADDRSTRLEN];
|
||||
uint8_t pref_offset;
|
||||
|
||||
switch (iph->version) {
|
||||
@@ -622,22 +712,48 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
return 0;
|
||||
|
||||
if (ippool_getip(pool, &ipm, &dst)) {
|
||||
LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s) with no PDP contex! (%s)\n",
|
||||
LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for IP address outside "
|
||||
"pool of managed addresses: %s <- %s\n",
|
||||
apn->cfg.name,
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
|
||||
inet_ntop(AF_INET, &iph->daddr, straddr[0], sizeof(straddr[0])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], sizeof(straddr[0])),
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->saddr, straddr[1], sizeof(straddr[1])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], sizeof(straddr[1])));
|
||||
return 0;
|
||||
}
|
||||
LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s)\n", apn->cfg.name);
|
||||
|
||||
if (ipm->peer) /* Check if a peer protocol is defined */
|
||||
gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
|
||||
if (ipm->peer) { /* Check if a peer protocol is defined */
|
||||
struct pdp_t *pdp = (struct pdp_t *)ipm->peer;
|
||||
LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for PDP(%s:%u): %s <- %s\n",
|
||||
apn->cfg.name,
|
||||
imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi,
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->daddr, straddr[0], sizeof(straddr[0])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], sizeof(straddr[0])),
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->saddr, straddr[1], sizeof(straddr[1])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], sizeof(straddr[1])));
|
||||
gtp_data_req(apn->ggsn->gsn, pdp, pack, len);
|
||||
} else {
|
||||
LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for IP address with no "
|
||||
"associated PDP Ctx: %s <- %s\n",
|
||||
apn->cfg.name,
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->daddr, straddr[0], sizeof(straddr[0])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], sizeof(straddr[0])),
|
||||
iph->version == 4 ?
|
||||
inet_ntop(AF_INET, &iph->saddr, straddr[1], sizeof(straddr[1])) :
|
||||
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], sizeof(straddr[1])));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MS-originated GTP1-U packet, needs to be sent via TUN device */
|
||||
static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
/* Rx MS-originated GTP-U packet, needs to be sent via TUN device.
|
||||
* "pack" contains the GTP-U payload once decapsulated from GTP-U by libgtp.
|
||||
* Hence, "pack" should contain an IP packet. */
|
||||
static int cb_gtpu_data_ind(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)pack;
|
||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
@@ -671,10 +787,15 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst)) {
|
||||
/* daddr: all-routers multicast addr */
|
||||
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
|
||||
return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
|
||||
&apn->v6_lladdr, pack, len);
|
||||
&apn->v6_lladdr, apn->cfg.mtu, pack, len);
|
||||
/* daddr: solicited-node multicast addr */
|
||||
if (memcmp(&ip6h->ip6_dst.s6_addr, solicited_node_mcast_addr_prefix, sizeof(solicited_node_mcast_addr_prefix)) == 0)
|
||||
return handle_solicited_node_mcast(pack, len);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
peer = pdp_get_peer_ipv(pdp, false);
|
||||
@@ -696,17 +817,7 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
osmo_hexdump(pack, len));
|
||||
return -1;
|
||||
}
|
||||
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
|
||||
}
|
||||
|
||||
/* callback for tun device osmocom select loop integration */
|
||||
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct apn_ctx *apn = fd->data;
|
||||
|
||||
OSMO_ASSERT(what & BSC_FD_READ);
|
||||
|
||||
return tun_decaps(apn->tun.tun);
|
||||
return tun_inject_pkt((struct tun_t *)pdp->ipif, pack, len);
|
||||
}
|
||||
|
||||
/* callback for libgtp osmocom select loop integration */
|
||||
@@ -715,7 +826,7 @@ static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
struct ggsn_ctx *ggsn = fd->data;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(what & BSC_FD_READ);
|
||||
OSMO_ASSERT(what & OSMO_FD_READ);
|
||||
|
||||
switch (fd->priv_nr) {
|
||||
case 0:
|
||||
@@ -782,6 +893,42 @@ static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_
|
||||
return sgsn_peer_handle_recovery(sgsn, pdp, recovery);
|
||||
}
|
||||
|
||||
static struct ggsn_ctx *ggsn_alloc(void *ctx, const char *name)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
|
||||
ggsn = talloc_zero(ctx, struct ggsn_ctx);
|
||||
OSMO_ASSERT(ggsn);
|
||||
|
||||
ggsn->cfg.name = talloc_strdup(ggsn, name);
|
||||
ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
|
||||
ggsn->cfg.shutdown = true;
|
||||
INIT_LLIST_HEAD(&ggsn->apn_list);
|
||||
INIT_LLIST_HEAD(&ggsn->sgsn_list);
|
||||
|
||||
llist_add_tail(&ggsn->list, &g_ggsn_list);
|
||||
return ggsn;
|
||||
}
|
||||
|
||||
struct ggsn_ctx *ggsn_find(const char *name)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
|
||||
llist_for_each_entry(ggsn, &g_ggsn_list, list) {
|
||||
if (!strcmp(ggsn->cfg.name, name))
|
||||
return ggsn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = ggsn_find(name);
|
||||
if (!ggsn)
|
||||
ggsn = ggsn_alloc(ctx, name);
|
||||
return ggsn;
|
||||
}
|
||||
|
||||
/* Start a given GGSN */
|
||||
int ggsn_start(struct ggsn_ctx *ggsn)
|
||||
{
|
||||
@@ -809,21 +956,22 @@ int ggsn_start(struct ggsn_ctx *ggsn)
|
||||
ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4;
|
||||
|
||||
/* Register File Descriptors */
|
||||
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
||||
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd0);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
|
||||
osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd1c);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
|
||||
osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd1u);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
gtp_set_cb_data_ind(ggsn->gsn, encaps_tun);
|
||||
gtp_set_cb_delete_context(ggsn->gsn, delete_context);
|
||||
gtp_set_cb_data_ind(ggsn->gsn, cb_gtpu_data_ind);
|
||||
gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind);
|
||||
gtp_set_cb_update_context_ind(ggsn->gsn, update_context_ind);
|
||||
gtp_set_cb_delete_context(ggsn->gsn, delete_context);
|
||||
gtp_set_cb_conf(ggsn->gsn, cb_conf);
|
||||
gtp_set_cb_recovery3(ggsn->gsn, cb_recovery3);
|
||||
|
||||
|
||||
31
ggsn/ggsn.h
31
ggsn/ggsn.h
@@ -6,13 +6,14 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../gtp/gtp.h"
|
||||
|
||||
#include "sgsn.h"
|
||||
|
||||
@@ -20,6 +21,19 @@
|
||||
#define APN_TYPE_IPv6 0x02 /* v6-only */
|
||||
#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */
|
||||
|
||||
/* The maximum sane MTU over GTP-U somebody may wish to configure:
|
||||
* 9000 bytes aka jumbo frames. */
|
||||
#define MAX_POSSIBLE_APN_MTU 9000
|
||||
|
||||
/* See 3GPP TS 23.060 Annex C: */
|
||||
#define ETHERNET_MTU 1500
|
||||
#define IPV4_HDR_MAX_SIZE 60
|
||||
#define IPV6_HDR_MAX_SIZE 40 /* Assume no extension headers in general... */
|
||||
#define UDP_HDR_MAX_SIZE 8
|
||||
#define GTPU_HDR_MAX_SIZE 12 /* Assume no extension headers in general... */
|
||||
#define MAX_DESIRED_APN_MTU ((ETHERNET_MTU) - (GTPU_HDR_MAX_SIZE) - (UDP_HDR_MAX_SIZE) - (IPV4_HDR_MAX_SIZE))
|
||||
/* MAX_DESIRED_APN_MTU = 1500 - 60 - 8 - 12 = 1500 - 80 = 1420 */
|
||||
|
||||
struct ggsn_ctx;
|
||||
|
||||
struct apn_ctx_ip {
|
||||
@@ -65,10 +79,12 @@ struct apn_ctx {
|
||||
uint32_t apn_type_mask;
|
||||
/* GTP-U via TUN device or in Linux kernel */
|
||||
enum apn_gtpu_mode gtpu_mode;
|
||||
/* administratively shut-down (true) or not (false) */
|
||||
/* administratively shut down (true) or not (false) */
|
||||
bool shutdown;
|
||||
/* transmit G-PDU sequeence numbers (true) or not (false) */
|
||||
/* transmit G-PDU sequence numbers (true) or not (false) */
|
||||
bool tx_gpdu_seq;
|
||||
/* MTU announced to the UE */
|
||||
uint16_t mtu;
|
||||
} cfg;
|
||||
|
||||
/* corresponding tun device */
|
||||
@@ -79,9 +95,10 @@ struct apn_ctx {
|
||||
/* ip-up and ip-down script names/paths */
|
||||
char *ipup_script;
|
||||
char *ipdown_script;
|
||||
/* Whether to apply the MTU (apn->cfg.mtu) on the tun device: */
|
||||
bool mtu_apply;
|
||||
} cfg;
|
||||
struct tun_t *tun;
|
||||
struct osmo_fd fd;
|
||||
} tun;
|
||||
|
||||
/* ipv6 link-local address */
|
||||
@@ -127,7 +144,7 @@ struct ggsn_ctx {
|
||||
char *state_dir;
|
||||
/* Time between Echo requests on each SGSN */
|
||||
unsigned int echo_interval;
|
||||
/* administratively shut-down (true) or not (false) */
|
||||
/* administratively shut down (true) or not (false) */
|
||||
bool shutdown;
|
||||
} cfg;
|
||||
|
||||
@@ -152,6 +169,7 @@ struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
|
||||
/* ggsn_main.c */
|
||||
extern struct ctrl_handle *g_ctrlh;
|
||||
extern void *tall_ggsn_ctx;
|
||||
extern struct osmo_tdef_group ggsn_tdef_group[];
|
||||
|
||||
/* ggsn.c */
|
||||
extern int ggsn_start(struct ggsn_ctx *ggsn);
|
||||
@@ -167,6 +185,3 @@ void ggsn_close_one_pdp(struct pdp_t *pdp);
|
||||
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
|
||||
|
||||
#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
|
||||
|
||||
#define LOGTUN(level, tun, fmt, args...) \
|
||||
LOGP(DTUN, level, "TUN(%s): " fmt, (tun)->devname, ## args)
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
@@ -45,6 +46,7 @@
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include "ggsn.h"
|
||||
|
||||
@@ -59,6 +61,11 @@ struct ul255_t apn;
|
||||
|
||||
static char *config_file = "osmo-ggsn.cfg";
|
||||
|
||||
struct osmo_tdef_group ggsn_tdef_group[] = {
|
||||
{.name = "gtp", .tdefs = gtp_T_defs, .desc = "GTP (libgtp) timers" },
|
||||
{ }
|
||||
};
|
||||
|
||||
/* To exit gracefully. Used with GCC compilation flag -pg and gprof */
|
||||
static void signal_handler(int s)
|
||||
{
|
||||
@@ -70,6 +77,17 @@ static void signal_handler(int s)
|
||||
end = 1;
|
||||
break;
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_ggsn_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_ggsn_ctx, stderr);
|
||||
@@ -95,17 +113,49 @@ static void print_help()
|
||||
" -c --config-file filename The config file to use\n"
|
||||
" -V --version Print the version of OsmoGGSN\n"
|
||||
);
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
}
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
|
||||
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (vty_ref_mode < 0) {
|
||||
fprintf(stderr, "%s: Unknown VTY reference generation "
|
||||
"mode '%s'\n", prog_name, optarg);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
|
||||
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static int long_option = 0;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "vty-ref-mode", 1, &long_option, 1 },
|
||||
{ "vty-ref-xml", 0, &long_option, 2 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -114,6 +164,9 @@ static void handle_options(int argc, char **argv)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
@@ -158,6 +211,7 @@ int main(int argc, char **argv)
|
||||
osmo_stats_vty_add_cmds();
|
||||
ggsn_vty_init();
|
||||
ctrl_vty_init(tall_ggsn_ctx);
|
||||
osmo_cpu_sched_vty_init(tall_ggsn_ctx);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
@@ -169,12 +223,11 @@ int main(int argc, char **argv)
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(tall_ggsn_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GGSN);
|
||||
rc = telnet_init_default(tall_ggsn_ctx, NULL, OSMO_VTY_PORT_GGSN);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(),
|
||||
OSMO_CTRL_PORT_GGSN, NULL);
|
||||
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
|
||||
if (!g_ctrlh) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
|
||||
exit(1);
|
||||
|
||||
175
ggsn/ggsn_vty.c
175
ggsn/ggsn_vty.c
@@ -22,6 +22,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
@@ -33,89 +35,26 @@
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include "../lib/util.h"
|
||||
|
||||
#include "ggsn.h"
|
||||
#include "sgsn.h"
|
||||
#include "../gtp/gtp_internal.h"
|
||||
|
||||
#define PREFIX_STR "Prefix (Network/Netmask)\n"
|
||||
#define IFCONFIG_STR "GGSN-based interface configuration\n"
|
||||
#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
|
||||
|
||||
LLIST_HEAD(g_ggsn_list);
|
||||
|
||||
enum ggsn_vty_node {
|
||||
GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
APN_NODE,
|
||||
};
|
||||
|
||||
struct ggsn_ctx *ggsn_find(const char *name)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
|
||||
llist_for_each_entry(ggsn, &g_ggsn_list, list) {
|
||||
if (!strcmp(ggsn->cfg.name, name))
|
||||
return ggsn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
|
||||
ggsn = ggsn_find(name);
|
||||
if (ggsn)
|
||||
return ggsn;
|
||||
|
||||
ggsn = talloc_zero(ctx, struct ggsn_ctx);
|
||||
if (!ggsn)
|
||||
return NULL;
|
||||
|
||||
ggsn->cfg.name = talloc_strdup(ggsn, name);
|
||||
ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
|
||||
ggsn->cfg.shutdown = true;
|
||||
INIT_LLIST_HEAD(&ggsn->apn_list);
|
||||
INIT_LLIST_HEAD(&ggsn->sgsn_list);
|
||||
|
||||
llist_add_tail(&ggsn->list, &g_ggsn_list);
|
||||
return ggsn;
|
||||
}
|
||||
|
||||
struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
|
||||
{
|
||||
struct apn_ctx *apn;
|
||||
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list) {
|
||||
if (!strcmp(apn->cfg.name, name))
|
||||
return apn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
|
||||
{
|
||||
struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
|
||||
if (apn)
|
||||
return apn;
|
||||
|
||||
apn = talloc_zero(ggsn, struct apn_ctx);
|
||||
if (!apn)
|
||||
return NULL;
|
||||
apn->ggsn = ggsn;
|
||||
apn->cfg.name = talloc_strdup(apn, name);
|
||||
apn->cfg.shutdown = true;
|
||||
apn->cfg.tx_gpdu_seq = true;
|
||||
INIT_LLIST_HEAD(&apn->cfg.name_list);
|
||||
|
||||
llist_add_tail(&apn->list, &ggsn->apn_list);
|
||||
return apn;
|
||||
}
|
||||
|
||||
/* GGSN Node */
|
||||
|
||||
static struct cmd_node ggsn_node = {
|
||||
@@ -222,6 +161,11 @@ DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
|
||||
if (mkdir(argv[0], 0755) == -1 && errno != EEXIST) {
|
||||
vty_out(vty, "%% Failed to create state-dir: %s%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -298,7 +242,7 @@ DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
|
||||
|
||||
DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
|
||||
"shutdown ggsn",
|
||||
"Put the GGSN in administrative shut-down\n" GGSN_STR)
|
||||
"Put the GGSN in administrative shutdown\n" GGSN_STR)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
|
||||
@@ -315,7 +259,7 @@ DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
|
||||
|
||||
DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
|
||||
"no shutdown ggsn",
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
|
||||
@@ -343,7 +287,7 @@ static void show_one_sgsn(struct vty *vty, const struct sgsn_peer *sgsn, const c
|
||||
|
||||
DEFUN(cfg_ggsn_show_sgsn, cfg_ggsn_show_sgsn_cmd,
|
||||
"show sgsn",
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
struct sgsn_peer *sgsn;
|
||||
@@ -389,10 +333,9 @@ DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd,
|
||||
NO_STR "Send an echo request to this static GGSN every interval.\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
int prev_interval = ggsn->cfg.echo_interval;
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
if (prev_interval == ggsn->cfg.echo_interval)
|
||||
if (ggsn->cfg.echo_interval == 0)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
ggsn->cfg.echo_interval = 0;
|
||||
@@ -469,13 +412,46 @@ DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
|
||||
DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
|
||||
"tun-device NAME",
|
||||
"Configure tun device name\n"
|
||||
"TUN device name")
|
||||
"TUN device name\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* MAX_POSSIBLE_APN_MTU = 9000
|
||||
* MAX_DESIRED_APN_MTU = 1420 */
|
||||
DEFUN(cfg_apn_mtu, cfg_apn_mtu_cmd,
|
||||
"mtu (<0-" OSMO_STRINGIFY_VAL(MAX_POSSIBLE_APN_MTU) ">|default) [apply]",
|
||||
"Configure announced MTU\n"
|
||||
"MTU of the APN, announced to the UE\n"
|
||||
"Default value of the MTU of the APN (1420)\n"
|
||||
"Apply the MTU on the tun-device of the APN\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
int rc;
|
||||
|
||||
if (strcmp(argv[0], "default") == 0)
|
||||
apn->cfg.mtu = MAX_DESIRED_APN_MTU;
|
||||
else
|
||||
apn->cfg.mtu = atoi(argv[0]);
|
||||
apn->tun.cfg.mtu_apply = (argc > 1);
|
||||
|
||||
if (apn->tun.cfg.mtu_apply && apn->tun.tun) {
|
||||
rc = osmo_netdev_set_mtu(apn->tun.tun->netdev, apn->cfg.mtu);
|
||||
if (rc < 0) {
|
||||
char buf_err[128];
|
||||
strerror_r(errno, buf_err, sizeof(buf_err));
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun interface MTU %u: %s (%d)\n",
|
||||
apn->cfg.mtu, buf_err, rc);
|
||||
vty_out(vty, "%% Failed to set tun interface MTU %u: %s (%d)%s",
|
||||
apn->cfg.mtu, buf_err, rc, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
|
||||
"ipup-script PATH",
|
||||
"Configure name/path of ip-up script\n"
|
||||
@@ -533,9 +509,11 @@ DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
|
||||
struct in46_prefix *pfx;
|
||||
|
||||
/* first update our parsed prefix */
|
||||
if (!strcmp(argv[0], "static"))
|
||||
if (!strcmp(argv[0], "static")) {
|
||||
pfx = &apn->v4.cfg.static_prefix;
|
||||
else
|
||||
vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
} else
|
||||
pfx = &apn->v4.cfg.dynamic_prefix;
|
||||
str2prefix(pfx, argv[1]);
|
||||
|
||||
@@ -567,9 +545,11 @@ DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
struct in46_prefix *pfx;
|
||||
|
||||
if (!strcmp(argv[0], "static"))
|
||||
if (!strcmp(argv[0], "static")) {
|
||||
pfx = &apn->v6.cfg.static_prefix;
|
||||
else
|
||||
vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
} else
|
||||
pfx = &apn->v6.cfg.dynamic_prefix;
|
||||
str2prefix(pfx, argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
@@ -677,7 +657,7 @@ DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
|
||||
|
||||
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
||||
"shutdown",
|
||||
"Put the APN in administrative shut-down\n")
|
||||
"Put the APN in administrative shutdown\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
|
||||
@@ -694,11 +674,15 @@ DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
||||
|
||||
DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
|
||||
"no shutdown",
|
||||
NO_STR "Remove the APN from administrative shut-down\n")
|
||||
NO_STR "Remove the APN from administrative shutdown\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
|
||||
if (apn->cfg.shutdown) {
|
||||
if (!apn->tun.cfg.dev_name) {
|
||||
vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (apn_start(apn) < 0) {
|
||||
vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -738,6 +722,9 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
vty_out(vty, " mtu %" PRIu16 "%s%s", apn->cfg.mtu,
|
||||
apn->tun.cfg.mtu_apply ? " apply" : "", VTY_NEWLINE);
|
||||
|
||||
if (!apn->cfg.tx_gpdu_seq)
|
||||
vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
|
||||
|
||||
@@ -788,6 +775,7 @@ static int config_write_ggsn(struct vty *vty)
|
||||
vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
|
||||
if (ggsn->cfg.gtpu_addr.v4.s_addr)
|
||||
vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
|
||||
osmo_tdef_vty_groups_write(vty, " ");
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
config_write_apn(vty, apn);
|
||||
if (ggsn->cfg.default_apn)
|
||||
@@ -805,10 +793,7 @@ static const char *print_gsnaddr(const struct ul16_t *in)
|
||||
{
|
||||
struct in46_addr in46;
|
||||
|
||||
in46.len = in->l;
|
||||
OSMO_ASSERT(in->l <= sizeof(in46.v6));
|
||||
memcpy(&in46.v6, in->v, in->l);
|
||||
|
||||
in46a_from_gsna(in, &in46);
|
||||
return in46a_ntoa(&in46);
|
||||
}
|
||||
|
||||
@@ -894,11 +879,16 @@ DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
imsi = strtoull(argv[1], NULL, 10);
|
||||
if (strlen(argv[1]) < 6 || strlen(argv[1]) > 15) {
|
||||
vty_out(vty, "%% Invalid IMSI '%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
imsi = gtp_imsi_str2gtp(argv[1]);
|
||||
|
||||
if (argc > 2) {
|
||||
nsapi = atoi(argv[2]);
|
||||
if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
|
||||
if (!gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
|
||||
show_one_pdp(vty, pdp);
|
||||
num_found++;
|
||||
}
|
||||
@@ -1101,6 +1091,8 @@ int ggsn_vty_init(void)
|
||||
install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
|
||||
|
||||
osmo_tdef_vty_groups_init(GGSN_NODE, ggsn_tdef_group);
|
||||
|
||||
install_node(&apn_node, NULL);
|
||||
install_element(APN_NODE, &cfg_description_cmd);
|
||||
install_element(APN_NODE, &cfg_no_description_cmd);
|
||||
@@ -1110,6 +1102,7 @@ int ggsn_vty_init(void)
|
||||
install_element(APN_NODE, &cfg_apn_type_support_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_mtu_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
|
||||
install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
|
||||
@@ -1131,17 +1124,6 @@ int ggsn_vty_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ggsn_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case GGSN_NODE:
|
||||
case APN_NODE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ggsn_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
@@ -1181,5 +1163,4 @@ struct vty_app_info g_vty_info = {
|
||||
.version = PACKAGE_VERSION,
|
||||
.copyright = ggsn_copyright,
|
||||
.go_parent_cb = ggsn_vty_go_parent,
|
||||
.is_config_node = ggsn_vty_is_config_node,
|
||||
};
|
||||
|
||||
11
ggsn/pco.c
11
ggsn/pco.c
@@ -110,6 +110,7 @@ ret_broken:
|
||||
osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
|
||||
}
|
||||
|
||||
/* Handle IP Control Protocol, RFC 1332, extensions in RFC 1877 */
|
||||
static void process_pco_element_ipcp(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
@@ -197,6 +198,13 @@ static void process_pco_element_dns_ipv4(const struct pco_element *pco_elem, str
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv4 DNS, but APN has none configured\n");
|
||||
}
|
||||
|
||||
static void process_pco_element_link_mtu_ipv4(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
const uint16_t val_be = osmo_htons(apn->cfg.mtu);
|
||||
msgb_t16lv_put(resp, PCO_P_IPv4_LINK_MTU, 2, (const uint8_t *)&val_be);
|
||||
}
|
||||
|
||||
static void process_pco_element(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
@@ -216,6 +224,9 @@ static void process_pco_element(const struct pco_element *pco_elem, struct msgb
|
||||
case PCO_P_DNS_IPv4_ADDR:
|
||||
process_pco_element_dns_ipv4(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
case PCO_P_IPv4_LINK_MTU:
|
||||
process_pco_element_link_mtu_ipv4(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
default:
|
||||
LOGPPDP(LOGL_INFO, pdp, "Unknown/Unimplemented PCO Protocol 0x%04x: %s\n",
|
||||
protocol_id, osmo_hexdump_nospc(pco_elem->data, pco_elem->length));
|
||||
|
||||
13
ggsn/pco.h
13
ggsn/pco.h
@@ -2,9 +2,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
/* 3GPP TS 24.008 10.6.5.3 */
|
||||
/* 3GPP TS 24.008 10.5.6.3 */
|
||||
enum pco_protocols {
|
||||
PCO_P_LCP = 0xC021,
|
||||
PCO_P_PAP = 0xC023,
|
||||
@@ -42,12 +42,11 @@ struct pco_element {
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* RFC 1332 */
|
||||
/* RFC 1332 IP Control Protocol options, extensions in RFC 1877 */
|
||||
enum ipcp_options {
|
||||
IPCP_OPT_IPADDR = 3,
|
||||
IPCP_OPT_PRIMARY_DNS = 129,
|
||||
IPCP_OPT_SECONDARY_DNS = 131,
|
||||
IPCP_OPT_IPADDR = 3, /* RFC 1332 3.3 */
|
||||
IPCP_OPT_PRIMARY_DNS = 129, /* RFC 1877 1.1 */
|
||||
IPCP_OPT_SECONDARY_DNS = 131, /* RFC 1877 1.2 */
|
||||
};
|
||||
|
||||
struct ipcp_option_hdr {
|
||||
|
||||
14
ggsn/sgsn.c
14
ggsn/sgsn.c
@@ -1,6 +1,6 @@
|
||||
#include "sgsn.h"
|
||||
#include "ggsn.h"
|
||||
|
||||
#include "../gtp/gtp_internal.h"
|
||||
|
||||
static bool sgsn_peer_attempt_free(struct sgsn_peer *sgsn)
|
||||
{
|
||||
@@ -116,6 +116,7 @@ static unsigned int sgsn_peer_drop_all_pdp_except(struct sgsn_peer *sgsn, struct
|
||||
{
|
||||
unsigned int num = 0;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
unsigned int count = llist_count(&sgsn->pdp_list);
|
||||
|
||||
inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));
|
||||
|
||||
@@ -125,10 +126,17 @@ static unsigned int sgsn_peer_drop_all_pdp_except(struct sgsn_peer *sgsn, struct
|
||||
continue;
|
||||
ggsn_close_one_pdp(pdp->lib);
|
||||
num++;
|
||||
if (num == count) {
|
||||
/* Note: if except is NULL, all pdp contexts are freed and sgsn
|
||||
* is most probably already freed at this point.
|
||||
* As a result, last access to sgsn->pdp_list before exiting
|
||||
* loop would access already freed memory. Avoid it by exiting
|
||||
* the loop without the last check, and make sure sgsn is not
|
||||
* accessed after this loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: if except is NULL, all pdp contexts are freed and sgsn is
|
||||
already freed at this point */
|
||||
LOGP(DGGSN, LOGL_INFO, "SGSN(%s) Dropped %u PDP contexts\n", buf, num);
|
||||
|
||||
return num;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
struct ggsn_ctx;
|
||||
struct pdp_priv_t;
|
||||
|
||||
@@ -2,14 +2,33 @@
|
||||
# Please read chapter "Library interface versions" of the libtool documentation
|
||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
|
||||
# If major=current-age is increased, remember to update the dh_strip line in debian/rules!
|
||||
LIBVERSION=6:0:0
|
||||
LIBVERSION=11:0:0
|
||||
|
||||
lib_LTLIBRARIES = libgtp.la
|
||||
|
||||
include_HEADERS = gtp.h pdp.h gtpie.h
|
||||
AM_CFLAGS = \
|
||||
-fno-builtin \
|
||||
-Wall \
|
||||
-DSBINDIR='"$(sbindir)"' \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
libgtp_la_SOURCES = \
|
||||
gsn.c \
|
||||
gsn_internal.h \
|
||||
gtp.c \
|
||||
gtp_internal.h \
|
||||
gtp_sgsn_ctx.c \
|
||||
gtp_sgsn_ctx.h \
|
||||
gtpie.c \
|
||||
lookupa.c \
|
||||
lookupa.h \
|
||||
pdp.c \
|
||||
queue.c \
|
||||
queue.h \
|
||||
$(NULL)
|
||||
|
||||
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)
|
||||
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
|
||||
|
||||
612
gtp/gsn.c
Normal file
612
gtp/gsn.c
Normal file
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* TODO:
|
||||
* - Do we need to handle fragmentation?
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
|
||||
#include "../config.h"
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* #include <stdint.h> ISO C99 types */
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "gsn_internal.h"
|
||||
#include "gtp_internal.h"
|
||||
#include "gtp_sgsn_ctx.h"
|
||||
|
||||
/* Error reporting functions */
|
||||
|
||||
static const struct rate_ctr_desc gsn_ctr_description[] = {
|
||||
[GSN_CTR_ERR_SOCKET] = { "err:socket", "Socket error" },
|
||||
[GSN_CTR_ERR_READFROM] = { "err:readfrom", "readfrom() errors" },
|
||||
[GSN_CTR_ERR_SENDTO] = { "err:sendto", "sendto() errors" },
|
||||
[GSN_CTR_ERR_QUEUEFULL] = { "err:queuefull", "Failed to queue message because queue is full" },
|
||||
[GSN_CTR_ERR_SEQ] = { "err:seq", "Sequence number out of range" },
|
||||
[GSN_CTR_ERR_ADDRESS] = { "err:address", "GSN address conversion failed" },
|
||||
[GSN_CTR_ERR_UNKNOWN_PDP] = { "err:unknown_pdp", "Failed looking up PDP context" },
|
||||
[GSN_CTR_ERR_UNEXPECTED_CAUSE] = { "err:unexpected_cause", "Unexpected cause value received" },
|
||||
[GSN_CTR_ERR_OUT_OF_PDP] = { "err:out_of_pdp", "Out of storage for PDP contexts" },
|
||||
[GSN_CTR_PKT_EMPTY] = { "pkt:empty", "Empty packet received" },
|
||||
[GSN_CTR_PKT_UNSUP] = { "pkt:unsupported", "Unsupported GTP version received" },
|
||||
[GSN_CTR_PKT_TOOSHORT] = { "pkt:too_short", "Packet too short received" },
|
||||
[GSN_CTR_PKT_UNKNOWN] = { "pkt:unknown", "Unknown packet type received" },
|
||||
[GSN_CTR_PKT_UNEXPECT] = { "pkt:unexpected", "Unexpected packet type received" },
|
||||
[GSN_CTR_PKT_DUPLICATE] = { "pkt:duplicate", "Duplicate or unsolicited packet received" },
|
||||
[GSN_CTR_PKT_MISSING] = { "pkt:missing", "Missing IE in packet received" },
|
||||
[GSN_CTR_PKT_INCORRECT] = { "pkt:incorrect", "Incorrect IE in packet received" },
|
||||
[GSN_CTR_PKT_INVALID] = { "pkt:invalid", "Invalid format in packet received" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc gsn_ctrg_desc = {
|
||||
"gsn",
|
||||
"GSN Statistics",
|
||||
OSMO_STATS_CLASS_PEER,
|
||||
ARRAY_SIZE(gsn_ctr_description),
|
||||
gsn_ctr_description,
|
||||
};
|
||||
static unsigned int gsn_ctr_next_idx = 0;
|
||||
|
||||
/* Global timer definitions for GTP operation, provided for convenience. To make these user configurable, it is convenient to add
|
||||
* gtp_gsn_tdefs as one of your program's osmo_tdef_group entries and call osmo_tdef_vty_init(). */
|
||||
struct osmo_tdef gtp_T_defs[] = {
|
||||
{ .T = GTP_GSN_TIMER_T3_RESPONSE, .default_val = 5, .unit = OSMO_TDEF_S,
|
||||
.desc = "Timer T3-RESPONSE holds the maximum wait time for a response of a request message"
|
||||
},
|
||||
{ .T = GTP_GSN_TIMER_N3_REQUESTS, .default_val = 3, .unit = OSMO_TDEF_CUSTOM,
|
||||
.desc = "Counter N3-REQUESTS holds the maximum number of attempts made by GTP to send a request message"
|
||||
},
|
||||
{ .T = GTP_GSN_TIMER_T3_HOLD_RESPONSE, .default_val = 5 * 3 /* (GTP_GSN_TIMER_T3_RESPONSE * GTP_GSN_TIMER_N3_REQUESTS) */, .unit = OSMO_TDEF_S,
|
||||
.desc = "Time a GTP response message is kept cached to re-transmit it when a duplicate request is received. Value is generally equal to (T3-RESPONSE * N3-REQUESTS) set at the peer"
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/* API Functions */
|
||||
|
||||
/* Deprecated, use gtp_pdp_newpdp() instead */
|
||||
int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
int rc;
|
||||
rc = gtp_pdp_newpdp(gsn, pdp, imsi, nsapi, NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp)
|
||||
{
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(pdp);
|
||||
return pdp_freepdp(pdp);
|
||||
}
|
||||
|
||||
/* Free pdp and all its secondary PDP contexts. Must be called on the primary PDP context. */
|
||||
int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp)
|
||||
{
|
||||
int n;
|
||||
struct pdp_t *secondary_pdp;
|
||||
OSMO_ASSERT(!pdp->secondary);
|
||||
|
||||
for (n = 0; n < PDP_MAXNSAPI; n++) {
|
||||
if (pdp->secondary_tei[n]) {
|
||||
if (gtp_pdp_getgtp1(gsn, &secondary_pdp,
|
||||
pdp->secondary_tei[n])) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown secondary PDP context\n");
|
||||
continue;
|
||||
}
|
||||
if (pdp != secondary_pdp) {
|
||||
gtp_freepdp(gsn, secondary_pdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gtp_freepdp(gsn, pdp);
|
||||
}
|
||||
|
||||
/* gtp_gpdu */
|
||||
|
||||
extern int gtp_fd(struct gsn_t *gsn)
|
||||
{
|
||||
return gsn->fd0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer))
|
||||
{
|
||||
gsn->cb_unsup_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer))
|
||||
{
|
||||
gsn->cb_extheader_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer, union gtpie_member **ie))
|
||||
{
|
||||
gsn->cb_ran_info_relay_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Initialise delete context callback */
|
||||
/* Called whenever a pdp context is deleted for any reason */
|
||||
int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb)(struct pdp_t *pdp))
|
||||
{
|
||||
gsn->cb_delete_context = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb)(int type, int cause,
|
||||
struct pdp_t *pdp, void *cbp))
|
||||
{
|
||||
gsn->cb_conf = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cb_recovery()
|
||||
* pdp may be NULL if Recovery IE was received from a message independent
|
||||
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
|
||||
* local setup. In case pdp is known, caller may want to keep that pdp alive to
|
||||
* handle subsequent msg cb as this specific pdp ctx is still valid according to
|
||||
* specs.
|
||||
*/
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb_recovery2)(struct sockaddr_in *peer,
|
||||
struct pdp_t *pdp, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery2 = cb_recovery2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cb_recovery()
|
||||
* pdp may be NULL if Recovery IE was received from a message independent
|
||||
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
|
||||
* local setup. In case pdp is known, caller may want to keep that pdp alive to
|
||||
* handle subsequent msg cb as this specific pdp ctx is still valid according to
|
||||
* specs.
|
||||
*/
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb_recovery3)(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
struct pdp_t *pdp, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery3 = cb_recovery3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind)(struct pdp_t *pdp,
|
||||
void *pack, unsigned len))
|
||||
{
|
||||
gsn->cb_data_ind = cb_data_ind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_sgsn_context_request_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size))
|
||||
{
|
||||
gsn->cb_sgsn_context_request_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_sgsn_context_response_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size))
|
||||
{
|
||||
gsn->cb_sgsn_context_response_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_sgsn_context_ack_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size))
|
||||
{
|
||||
gsn->cb_sgsn_context_ack_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_timer_retrans(struct gsn_t *gsn)
|
||||
{
|
||||
/* Retransmit any outstanding packets */
|
||||
/* Remove from queue if maxretrans exceeded */
|
||||
time_t now;
|
||||
struct qmsg_t *qmsg;
|
||||
unsigned int t3_response, n3_requests;
|
||||
|
||||
now = time(NULL);
|
||||
t3_response = osmo_tdef_get(gsn->tdef, GTP_GSN_TIMER_T3_RESPONSE, OSMO_TDEF_S, -1);
|
||||
n3_requests = osmo_tdef_get(gsn->tdef, GTP_GSN_TIMER_N3_REQUESTS, OSMO_TDEF_CUSTOM, -1);
|
||||
|
||||
/* get first element in queue, as long as the timeout of that
|
||||
* element has expired */
|
||||
while ((!queue_getfirst(gsn->queue_req, &qmsg)) &&
|
||||
(qmsg->timeout <= now)) {
|
||||
if (qmsg->retrans > n3_requests) { /* Too many retrans */
|
||||
LOGP(DLGTP, LOGL_NOTICE, "Retransmit req queue timeout of seq %" PRIu16 "\n",
|
||||
qmsg->seq);
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
|
||||
queue_freemsg(gsn->queue_req, qmsg);
|
||||
} else {
|
||||
LOGP(DLGTP, LOGL_INFO, "Retransmit (%d) of seq %" PRIu16 "\n",
|
||||
qmsg->retrans, qmsg->seq);
|
||||
if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
|
||||
(struct sockaddr *)&qmsg->peer,
|
||||
sizeof(struct sockaddr_in)) < 0) {
|
||||
rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SENDTO);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s\n",
|
||||
gsn->fd0, (unsigned long)&qmsg->p,
|
||||
qmsg->l, strerror(errno));
|
||||
}
|
||||
queue_back(gsn->queue_req, qmsg);
|
||||
qmsg->timeout = now + t3_response;
|
||||
qmsg->retrans++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also clean up reply timeouts */
|
||||
while ((!queue_getfirst(gsn->queue_resp, &qmsg)) &&
|
||||
(qmsg->timeout < now)) {
|
||||
LOGP(DLGTP, LOGL_DEBUG, "Retransmit resp queue seq %"
|
||||
PRIu16 " expired, removing from queue\n", qmsg->seq);
|
||||
queue_freemsg(gsn->queue_resp, qmsg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_timer_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
||||
{
|
||||
time_t now, later, diff;
|
||||
struct qmsg_t *qmsg;
|
||||
timeout->tv_usec = 0;
|
||||
|
||||
if (queue_getfirst(gsn->queue_req, &qmsg)) {
|
||||
timeout->tv_sec = 10;
|
||||
} else {
|
||||
now = time(NULL);
|
||||
later = qmsg->timeout;
|
||||
timeout->tv_sec = later - now;
|
||||
if (timeout->tv_sec < 0)
|
||||
timeout->tv_sec = 0; /* No negative allowed */
|
||||
if (timeout->tv_sec > 10)
|
||||
timeout->tv_sec = 10; /* Max sleep for 10 sec */
|
||||
}
|
||||
|
||||
if (queue_getfirst(gsn->queue_resp, &qmsg)) {
|
||||
/* already set by queue_req, do nothing */
|
||||
} else { /* trigger faster if earlier timeout exists in queue_resp */
|
||||
now = time(NULL);
|
||||
later = qmsg->timeout;
|
||||
diff = later - now;
|
||||
if (diff < 0)
|
||||
diff = 0;
|
||||
if (diff < timeout->tv_sec)
|
||||
timeout->tv_sec = diff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gtp_queue_timer_start(struct gsn_t *gsn)
|
||||
{
|
||||
struct timeval next;
|
||||
|
||||
/* Retrieve next retransmission as timeval */
|
||||
queue_timer_retranstimeout(gsn, &next);
|
||||
|
||||
/* re-schedule the timer */
|
||||
osmo_timer_schedule(&gsn->queue_timer, next.tv_sec, next.tv_usec/1000);
|
||||
}
|
||||
|
||||
/* timer callback for libgtp retransmission and ping */
|
||||
static void queue_timer_cb(void *data)
|
||||
{
|
||||
struct gsn_t *gsn = data;
|
||||
|
||||
/* do all the retransmissions as needed */
|
||||
queue_timer_retrans(gsn);
|
||||
|
||||
gtp_queue_timer_start(gsn);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief clear the request and response queue. Useful for debugging to reset "some" state.
|
||||
* @param gsn The GGSN instance
|
||||
*/
|
||||
void gtp_clear_queues(struct gsn_t *gsn)
|
||||
{
|
||||
struct qmsg_t *qmsg;
|
||||
|
||||
LOGP(DLGTP, LOGL_INFO, "Clearing req & resp retransmit queues\n");
|
||||
while (!queue_getfirst(gsn->queue_req, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_req, qmsg);
|
||||
}
|
||||
|
||||
while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_resp, qmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform restoration and recovery error handling as described in 29.060 */
|
||||
static void log_restart(struct gsn_t *gsn)
|
||||
{
|
||||
FILE *f;
|
||||
int i, rc;
|
||||
int counter = 0;
|
||||
char *filename;
|
||||
|
||||
filename = talloc_asprintf(NULL, "%s/%s", gsn->statedir, RESTART_FILE);
|
||||
OSMO_ASSERT(filename);
|
||||
|
||||
/* We try to open file. On failure we will later try to create file */
|
||||
if (!(f = fopen(filename, "r"))) {
|
||||
LOGP(DLGTP, LOGL_NOTICE,
|
||||
"State information file (%s) not found. Creating new file.\n",
|
||||
filename);
|
||||
} else {
|
||||
rc = fscanf(f, "%d", &counter);
|
||||
if (rc != 1) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fscanf failed to read counter value\n");
|
||||
goto close_file;
|
||||
}
|
||||
if (fclose(f)) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fclose failed: Error = %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
gsn->restart_counter = (unsigned char)counter;
|
||||
gsn->restart_counter++;
|
||||
|
||||
/* Keep the umask closely wrapped around our fopen() call in case the
|
||||
* log outputs cause file creation. */
|
||||
i = umask(022);
|
||||
f = fopen(filename, "w");
|
||||
umask(i);
|
||||
if (!f) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fopen(path=%s, mode=%s) failed: Error = %s\n", filename,
|
||||
"w", strerror(errno));
|
||||
goto free_filename;
|
||||
}
|
||||
|
||||
fprintf(f, "%d\n", gsn->restart_counter);
|
||||
close_file:
|
||||
if (fclose(f))
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fclose failed: Error = %s\n", strerror(errno));
|
||||
free_filename:
|
||||
talloc_free(filename);
|
||||
}
|
||||
|
||||
static int create_and_bind_socket(const char *name, struct gsn_t *gsn, int *fd, int domain,
|
||||
const struct in_addr *listen, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int type = SOCK_DGRAM;
|
||||
int protocol = 0;
|
||||
|
||||
*fd = socket(domain, type, protocol);
|
||||
|
||||
if (*fd < 0) {
|
||||
rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"%s socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
|
||||
name, domain, type, protocol, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = domain;
|
||||
addr.sin_addr = *listen;
|
||||
addr.sin_port = htons(port);
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
addr.sin_len = sizeof(addr);
|
||||
#endif
|
||||
|
||||
if (bind(*fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, &addr,
|
||||
"%s bind(fd=%d) failed: Error = %s\n",
|
||||
name, *fd, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode)
|
||||
{
|
||||
LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen));
|
||||
|
||||
*gsn = talloc_zero(tall_libgtp_ctx, struct gsn_t);
|
||||
|
||||
(*gsn)->statedir = statedir;
|
||||
log_restart(*gsn);
|
||||
|
||||
/* Initialise sequence number */
|
||||
(*gsn)->seq_next = (*gsn)->restart_counter * 1024;
|
||||
|
||||
/* Initialize timers: */
|
||||
(*gsn)->tdef = gtp_T_defs;
|
||||
/* Small hack to properly reset tdef for old clients not using the tdef_group: */
|
||||
OSMO_ASSERT(gtp_T_defs[0].default_val != 0);
|
||||
if (gtp_T_defs[0].val == 0)
|
||||
osmo_tdefs_reset((*gsn)->tdef);
|
||||
|
||||
|
||||
/* Initialise request retransmit queue */
|
||||
queue_new(&(*gsn)->queue_req);
|
||||
queue_new(&(*gsn)->queue_resp);
|
||||
|
||||
/* Initialise pdp table */
|
||||
pdp_init(*gsn);
|
||||
|
||||
/* Initialize internal queue timer */
|
||||
osmo_timer_setup(&(*gsn)->queue_timer, queue_timer_cb, *gsn);
|
||||
|
||||
/* Initialize counter group: */
|
||||
(*gsn)->ctrg = rate_ctr_group_alloc(NULL, &gsn_ctrg_desc, gsn_ctr_next_idx++);
|
||||
|
||||
/* Initialise call back functions */
|
||||
(*gsn)->cb_create_context_ind = 0;
|
||||
(*gsn)->cb_update_context_ind = 0;
|
||||
(*gsn)->cb_delete_context = 0;
|
||||
(*gsn)->cb_unsup_ind = 0;
|
||||
(*gsn)->cb_conf = 0;
|
||||
(*gsn)->cb_data_ind = 0;
|
||||
|
||||
/* Store function parameters */
|
||||
/* Same IP for user traffic and signalling */
|
||||
(*gsn)->gsnc = *listen;
|
||||
(*gsn)->gsnu = *listen;
|
||||
(*gsn)->mode = mode;
|
||||
|
||||
(*gsn)->fd0 = -1;
|
||||
(*gsn)->fd1c = -1;
|
||||
(*gsn)->fd1u = -1;
|
||||
|
||||
/* Create GTP version 0 socket */
|
||||
if (create_and_bind_socket("GTPv0", *gsn, &(*gsn)->fd0, AF_INET, listen, GTP0_PORT) < 0)
|
||||
goto error;
|
||||
|
||||
/* Create GTP version 1 control plane socket */
|
||||
if (create_and_bind_socket("GTPv1 control plane", *gsn, &(*gsn)->fd1c, AF_INET, listen, GTP1C_PORT) < 0)
|
||||
goto error;
|
||||
|
||||
/* Create GTP version 1 user plane socket */
|
||||
if (create_and_bind_socket("GTPv1 user plane", *gsn, &(*gsn)->fd1u, AF_INET, listen, GTP1U_PORT) < 0)
|
||||
goto error;
|
||||
|
||||
/* Start internal queue timer */
|
||||
gtp_queue_timer_start(*gsn);
|
||||
|
||||
(*gsn)->sgsn_ctx = sgsn_ctx_reqs_init(*gsn, 2048, 4095);
|
||||
if (!(*gsn)->sgsn_ctx)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
gtp_free(*gsn);
|
||||
*gsn = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gtp_free(struct gsn_t *gsn)
|
||||
{
|
||||
|
||||
/* Cleanup internal queue timer */
|
||||
osmo_timer_del(&gsn->queue_timer);
|
||||
|
||||
/* Clean up retransmit queues */
|
||||
queue_free(gsn->queue_req);
|
||||
queue_free(gsn->queue_resp);
|
||||
|
||||
close(gsn->fd0);
|
||||
close(gsn->fd1c);
|
||||
close(gsn->fd1u);
|
||||
|
||||
rate_ctr_group_free(gsn->ctrg);
|
||||
|
||||
talloc_free(gsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Register create context indication callback */
|
||||
int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct pdp_t *
|
||||
pdp))
|
||||
{
|
||||
gsn->cb_create_context_ind = cb_create_context_ind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_update_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_update_context_ind)(struct pdp_t *pdp))
|
||||
{
|
||||
gsn->cb_update_context_ind = cb_update_context_ind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_retrans(struct gsn_t *gsn)
|
||||
{
|
||||
/* dummy API, deprecated. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
||||
{
|
||||
timeout->tv_sec = 24*60*60;
|
||||
timeout->tv_usec = 0;
|
||||
/* dummy API, deprecated. Return a huge timer to do nothing */
|
||||
return 0;
|
||||
}
|
||||
3
gtp/gsn_internal.h
Normal file
3
gtp/gsn_internal.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void gtp_queue_timer_start(struct gsn_t *gsn);
|
||||
24
gtp/gtp_internal.h
Normal file
24
gtp/gtp_internal.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define GTP_LOGPKG(pri, peer, pack, len, fmt, args...) \
|
||||
logp2(DLGTP, pri, __FILE__, __LINE__, 0, \
|
||||
"Packet from %s:%u, length: %d content: %s: " fmt, \
|
||||
inet_ntoa((peer)->sin_addr), ntohs((peer)->sin_port), \
|
||||
len, osmo_hexdump((const uint8_t *) pack, len), \
|
||||
##args)
|
||||
|
||||
#define LOGP_WITH_ADDR(ss, level, peer, fmt, args...) \
|
||||
LOGP(ss, level, "addr(%s:%d) " fmt, \
|
||||
inet_ntoa((peer)->sin_addr), ntohs((peer)->sin_port), \
|
||||
##args)
|
||||
|
||||
uint64_t gtp_imsi_str2gtp(const char *str);
|
||||
|
||||
extern TALLOC_CTX *tall_libgtp_ctx;
|
||||
512
gtp/gtp_sgsn_ctx.c
Normal file
512
gtp/gtp_sgsn_ctx.c
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2024 sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
*
|
||||
* 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 <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/gtp/gsn.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
#include "gtp_sgsn_ctx.h"
|
||||
|
||||
#define X(s) (1 << (s))
|
||||
|
||||
/* Handle the logic part of the SGSN Context Req/Resp/Ack */
|
||||
|
||||
const struct value_string sgsn_ctx_event_names[] = {
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_RX_REQ),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_TX_RESP),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_TX_RESP_FAIL),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_RX_ACK),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_RX_NACK),
|
||||
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_TX_REQ),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_RX_RESP),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_RX_RESP_FAIL),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_TX_ACK),
|
||||
OSMO_VALUE_STRING(SGSN_CTX_REQ_E_TX_NACK),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout sgsn_ctx_fsm_timeouts[32] = {
|
||||
[SGSN_CTX_REQ_ST_START] = { },
|
||||
|
||||
[SGSN_CTX_REQ_ST_WAIT_LOCAL_RESP] = { },
|
||||
[SGSN_CTX_REQ_ST_WAIT_REMOTE_ACK] = { },
|
||||
|
||||
[SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP] = { },
|
||||
[SGSN_CTX_REQ_ST_WAIT_LOCAL_ACK] = { },
|
||||
};
|
||||
|
||||
struct osmo_tdef gtp_tdefs_sgsn_ctx[] = {
|
||||
{ /* terminator */ }
|
||||
};
|
||||
|
||||
/* Used to pass all relevant data to the fsm via data */
|
||||
struct ctx_msg {
|
||||
/* Message type */
|
||||
uint8_t msg;
|
||||
union gtpie_member * const *ie;
|
||||
unsigned int ie_size;
|
||||
};
|
||||
|
||||
#define sgsn_ctx_fsm_state_chg(fi, NEXT_STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, sgsn_ctx_fsm_timeouts, gtp_tdefs_sgsn_ctx, 10)
|
||||
|
||||
/* Direction of the SGSN context.
|
||||
* Outgoing Ctx: From local to remote.
|
||||
* Incoming Ctx: From remote to local.
|
||||
*/
|
||||
static void sgsn_ctx_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct sgsn_ctx_req *req = fi->priv;
|
||||
struct gsn_t *gsn = req->gsn;
|
||||
struct ctx_msg *msg = data;
|
||||
|
||||
switch (event) {
|
||||
case SGSN_CTX_REQ_E_RX_REQ:
|
||||
/* remote SGSN ask this peer */
|
||||
sgsn_ctx_fsm_state_chg(fi, SGSN_CTX_REQ_ST_WAIT_LOCAL_RESP);
|
||||
if (gsn->cb_sgsn_context_request_ind)
|
||||
gsn->cb_sgsn_context_request_ind(gsn, &req->peer, req->local_teic, msg->ie, msg->ie_size);
|
||||
break;
|
||||
case SGSN_CTX_REQ_E_TX_REQ:
|
||||
/* local SGSN ask remote peer */
|
||||
sgsn_ctx_fsm_state_chg(fi, SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Outgoing Ctx: remote SGSN ask this peer, waiting for a resp from this node (SGSN) */
|
||||
static void sgsn_ctx_wait_local_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case SGSN_CTX_REQ_E_TX_RESP:
|
||||
sgsn_ctx_fsm_state_chg(fi, SGSN_CTX_REQ_ST_WAIT_REMOTE_ACK);
|
||||
break;
|
||||
case SGSN_CTX_REQ_E_TX_RESP_FAIL:
|
||||
sgsn_ctx_fsm_state_chg(fi, SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Outgoing Ctx: This node (SGSN) answered with a Ctx Resp, waiting for a remote Ack or Nack */
|
||||
static void sgsn_ctx_wait_remote_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct sgsn_ctx_req *req = fi->priv;
|
||||
struct gsn_t *gsn = req->gsn;
|
||||
struct ctx_msg *msg = data;
|
||||
|
||||
switch (event) {
|
||||
case SGSN_CTX_REQ_E_RX_ACK:
|
||||
if (gsn->cb_sgsn_context_ack_ind)
|
||||
gsn->cb_sgsn_context_ack_ind(gsn, &req->peer, req->local_teic, msg->ie, msg->ie_size);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
break;
|
||||
case SGSN_CTX_REQ_E_RX_NACK:
|
||||
if (gsn->cb_sgsn_context_ack_ind)
|
||||
gsn->cb_sgsn_context_ack_ind(gsn, &req->peer, req->local_teic, msg->ie, msg->ie_size);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming Ctx: This node requested a remote SGSN. Waiting for a remote response */
|
||||
static void sgsn_ctx_wait_remote_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct sgsn_ctx_req *req = fi->priv;
|
||||
struct gsn_t *gsn = req->gsn;
|
||||
struct ctx_msg *msg = data;
|
||||
|
||||
switch (event) {
|
||||
case SGSN_CTX_REQ_E_RX_RESP:
|
||||
sgsn_ctx_fsm_state_chg(fi, SGSN_CTX_REQ_ST_WAIT_LOCAL_ACK);
|
||||
if (gsn->cb_sgsn_context_response_ind)
|
||||
gsn->cb_sgsn_context_response_ind(gsn, &req->peer, req->local_teic, msg->ie, msg->ie_size);
|
||||
break;
|
||||
case SGSN_CTX_REQ_E_RX_RESP_FAIL:
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming Ctx: Received a Response, waiting for this node to respond with an Ack or Nack */
|
||||
static void sgsn_ctx_local_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case SGSN_CTX_REQ_E_TX_ACK:
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
break;
|
||||
case SGSN_CTX_REQ_E_TX_NACK:
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
int sgsn_ctx_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Terminating the FSM when a timeout happens */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state sgsn_ctx_req_states[] = {
|
||||
[SGSN_CTX_REQ_ST_START] = {
|
||||
.in_event_mask =
|
||||
X(SGSN_CTX_REQ_E_RX_REQ) |
|
||||
X(SGSN_CTX_REQ_E_TX_REQ),
|
||||
.out_state_mask =
|
||||
X(SGSN_CTX_REQ_ST_WAIT_LOCAL_RESP) |
|
||||
X(SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP),
|
||||
.name = "Init",
|
||||
.action = sgsn_ctx_init,
|
||||
},
|
||||
|
||||
[SGSN_CTX_REQ_ST_WAIT_LOCAL_RESP] = {
|
||||
.in_event_mask =
|
||||
X(SGSN_CTX_REQ_E_TX_RESP) |
|
||||
X(SGSN_CTX_REQ_E_TX_RESP_FAIL),
|
||||
.out_state_mask =
|
||||
X(SGSN_CTX_REQ_ST_WAIT_REMOTE_ACK),
|
||||
.name = "Wait Local Response",
|
||||
.action = sgsn_ctx_wait_local_resp,
|
||||
},
|
||||
[SGSN_CTX_REQ_ST_WAIT_REMOTE_ACK] = {
|
||||
.in_event_mask =
|
||||
X(SGSN_CTX_REQ_E_RX_ACK) |
|
||||
X(SGSN_CTX_REQ_E_RX_NACK),
|
||||
.out_state_mask = 0,
|
||||
.name = "Wait Remote Ack",
|
||||
.action = sgsn_ctx_wait_remote_ack,
|
||||
},
|
||||
|
||||
[SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP] = {
|
||||
.in_event_mask =
|
||||
X(SGSN_CTX_REQ_E_RX_RESP) |
|
||||
X(SGSN_CTX_REQ_E_RX_RESP_FAIL),
|
||||
.out_state_mask =
|
||||
X(SGSN_CTX_REQ_ST_WAIT_LOCAL_ACK),
|
||||
.name = "Wait Remote Response",
|
||||
.action = sgsn_ctx_wait_remote_resp,
|
||||
},
|
||||
[SGSN_CTX_REQ_ST_WAIT_LOCAL_ACK] = {
|
||||
.in_event_mask =
|
||||
X(SGSN_CTX_REQ_E_TX_ACK) |
|
||||
X(SGSN_CTX_REQ_E_TX_NACK),
|
||||
.out_state_mask = 0,
|
||||
.name = "Wait Local Ack",
|
||||
.action = sgsn_ctx_local_ack,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm sgsn_ctx_req_fsm = {
|
||||
.name = "SGSNCtxReq",
|
||||
.states = sgsn_ctx_req_states,
|
||||
.num_states = ARRAY_SIZE(sgsn_ctx_req_states),
|
||||
.event_names = sgsn_ctx_event_names,
|
||||
.log_subsys = DLGTP,
|
||||
.timer_cb = sgsn_ctx_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void sgsn_ctx_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&sgsn_ctx_req_fsm) == 0);
|
||||
}
|
||||
|
||||
struct sgsn_ctx_reqs *sgsn_ctx_reqs_init(void *ctx, uint32_t lowest_teic, uint32_t highest_teic)
|
||||
{
|
||||
struct sgsn_ctx_reqs *reqs;
|
||||
|
||||
if (lowest_teic >= highest_teic)
|
||||
return NULL;
|
||||
|
||||
reqs = talloc_zero(ctx, struct sgsn_ctx_reqs);
|
||||
if (!reqs)
|
||||
return reqs;
|
||||
|
||||
reqs->lowest_teic = reqs->next_teic = lowest_teic;
|
||||
reqs->high_teic = highest_teic;
|
||||
|
||||
INIT_LLIST_HEAD(&reqs->local_reqs);
|
||||
INIT_LLIST_HEAD(&reqs->remote_reqs);
|
||||
|
||||
return reqs;
|
||||
}
|
||||
|
||||
static uint32_t get_next_teic(struct gsn_t *gsn)
|
||||
{
|
||||
uint32_t teic = gsn->sgsn_ctx->next_teic++;
|
||||
|
||||
if (gsn->sgsn_ctx->next_teic > gsn->sgsn_ctx->high_teic)
|
||||
gsn->sgsn_ctx->next_teic = gsn->sgsn_ctx->lowest_teic;
|
||||
|
||||
/* FIXME: check if this is already assigned! */
|
||||
|
||||
return teic;
|
||||
}
|
||||
|
||||
static struct sgsn_ctx_req *_sgsn_ctx_req_alloc(struct gsn_t *gsn)
|
||||
{
|
||||
struct sgsn_ctx_req *req = talloc_zero(gsn, struct sgsn_ctx_req);
|
||||
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->fi = osmo_fsm_inst_alloc(&sgsn_ctx_req_fsm, req, req, LOGL_INFO, NULL);
|
||||
if (!req->fi)
|
||||
goto out;
|
||||
|
||||
req->gsn = gsn;
|
||||
req->local_teic = get_next_teic(gsn);
|
||||
return req;
|
||||
|
||||
out:
|
||||
osmo_fsm_inst_free(req->fi);
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Alloc a SGSN_CTX for an outgoing Context (Remote SGSN requested) */
|
||||
static struct sgsn_ctx_req *sgsn_ctx_req_alloc_outgoing(struct gsn_t *gsn, const struct sockaddr_in *peer, uint16_t seq)
|
||||
{
|
||||
struct sgsn_ctx_req *req = _sgsn_ctx_req_alloc(gsn);
|
||||
if (!req)
|
||||
return req;
|
||||
|
||||
req->seq = seq;
|
||||
req->peer = *peer;
|
||||
llist_add_tail(&req->list, &gsn->sgsn_ctx->remote_reqs);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/* Alloc a SGSN_CTX for an incoming Context (Local SGSN requested) */
|
||||
static struct sgsn_ctx_req *sgsn_ctx_req_alloc_incoming(struct gsn_t *gsn, const struct sockaddr_in *peer, uint16_t seq)
|
||||
{
|
||||
struct sgsn_ctx_req *req = _sgsn_ctx_req_alloc(gsn);
|
||||
if (!req)
|
||||
return req;
|
||||
|
||||
req->seq = seq;
|
||||
req->peer = *peer;
|
||||
llist_add_tail(&req->list, &gsn->sgsn_ctx->local_reqs);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void sgsn_ctx_free(struct sgsn_ctx_req *req)
|
||||
{
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
osmo_fsm_inst_free(req->fi);
|
||||
llist_del(&req->list);
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
static struct sgsn_ctx_req *sgsn_ctx_by_teic(struct gsn_t *gsn, uint32_t teic, bool local_req)
|
||||
{
|
||||
struct sgsn_ctx_req *req;
|
||||
struct llist_head *head;
|
||||
|
||||
if (local_req)
|
||||
head = &gsn->sgsn_ctx->local_reqs;
|
||||
else
|
||||
head = &gsn->sgsn_ctx->remote_reqs;
|
||||
|
||||
llist_for_each_entry(req, head, list) {
|
||||
if (req->local_teic == teic)
|
||||
return req;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_rx_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq,
|
||||
union gtpie_member * const *ie, unsigned int ie_size)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_req_alloc_outgoing(gsn, peer, seq);
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_REQ,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!gsn->cb_sgsn_context_request_ind)
|
||||
goto err;
|
||||
|
||||
if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &req->remote_teic))
|
||||
goto err;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi, SGSN_CTX_REQ_E_RX_REQ, &ctx_msg))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
sgsn_ctx_free(req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_rx_resp(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq, uint32_t local_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_by_teic(gsn, local_teic, true);
|
||||
uint8_t cause;
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_RSP,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOENT;
|
||||
|
||||
if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause))
|
||||
return -EINVAL;
|
||||
|
||||
if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &req->remote_teic) && cause == GTPCAUSE_ACC_REQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi,
|
||||
cause == GTPCAUSE_ACC_REQ ? SGSN_CTX_REQ_E_RX_RESP : SGSN_CTX_REQ_E_RX_RESP_FAIL,
|
||||
&ctx_msg))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_rx_ack(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq, uint32_t local_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_by_teic(gsn, local_teic, false);
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_ACK,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOENT;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi, SGSN_CTX_REQ_E_RX_ACK, &ctx_msg))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
union gtpie_member * const *ie, unsigned int ie_size,
|
||||
uint32_t *local_teic, uint16_t seq)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_req_alloc_incoming(gsn, peer, seq);
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_REQ,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
/* TODO: move tx GTPv1 into the fsm */
|
||||
*local_teic = req->local_teic;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi, SGSN_CTX_REQ_E_TX_REQ, &ctx_msg))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
sgsn_ctx_free(req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
uint16_t *seq, uint32_t local_teic, uint32_t *remote_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_by_teic(gsn, local_teic, false);
|
||||
uint8_t cause;
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_RSP,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOENT;
|
||||
|
||||
/* TODO: move tx GTPv1 into the fsm */
|
||||
*seq = req->seq;
|
||||
*peer = req->peer;
|
||||
*remote_teic = req->remote_teic;
|
||||
|
||||
if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause))
|
||||
return -EINVAL;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi,
|
||||
cause == GTPCAUSE_ACC_REQ ? SGSN_CTX_REQ_E_TX_RESP : SGSN_CTX_REQ_E_TX_RESP_FAIL,
|
||||
&ctx_msg))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_ack(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
uint16_t *seq, uint32_t local_teic, uint32_t *remote_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size)
|
||||
{
|
||||
struct sgsn_ctx_req *req = sgsn_ctx_by_teic(gsn, local_teic, true);
|
||||
uint8_t cause;
|
||||
struct ctx_msg ctx_msg = {
|
||||
.msg = GTP_SGSN_CONTEXT_ACK,
|
||||
.ie = ie,
|
||||
.ie_size = ie_size,
|
||||
};
|
||||
|
||||
if (!req)
|
||||
return -ENOENT;
|
||||
|
||||
/* TODO: move tx GTPv1 into the fsm */
|
||||
*seq = req->seq;
|
||||
*peer = req->peer;
|
||||
*remote_teic = req->remote_teic;
|
||||
|
||||
if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause))
|
||||
return -EINVAL;
|
||||
|
||||
if (osmo_fsm_inst_dispatch(req->fi,
|
||||
cause == GTPCAUSE_ACC_REQ ? SGSN_CTX_REQ_E_TX_ACK : SGSN_CTX_REQ_E_TX_NACK,
|
||||
&ctx_msg))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
104
gtp/gtp_sgsn_ctx.h
Normal file
104
gtp/gtp_sgsn_ctx.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2024 sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
*
|
||||
* 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 <netinet/in.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct gsn_t;
|
||||
struct osmo_fsm_inst;
|
||||
union gtpie_member;
|
||||
|
||||
|
||||
enum gtp_sgsn_ctx_states {
|
||||
SGSN_CTX_REQ_ST_START,
|
||||
|
||||
/* remote SGSN/MME request a Ctx from this peer */
|
||||
SGSN_CTX_REQ_ST_WAIT_LOCAL_RESP, /*! wait for this peer to tx a SGSN Ctx Respond */
|
||||
SGSN_CTX_REQ_ST_WAIT_REMOTE_ACK, /*! wait for remote peer SGSN Ctx Ack */
|
||||
|
||||
/* local SGSN request a Ctx from a remote peer (SGSN/MME) */
|
||||
SGSN_CTX_REQ_ST_WAIT_REMOTE_RESP, /*! wait for remote peer to send this peer a SGSN Ctx Respond */
|
||||
SGSN_CTX_REQ_ST_WAIT_LOCAL_ACK, /*! wait for the local peer to ack */
|
||||
};
|
||||
|
||||
enum gtp_sgsn_ctx_req_event {
|
||||
/* remote SGSN/MME request a Ctx from this peer */
|
||||
SGSN_CTX_REQ_E_RX_REQ,
|
||||
SGSN_CTX_REQ_E_TX_RESP, /* a response with a success reason */
|
||||
SGSN_CTX_REQ_E_TX_RESP_FAIL,
|
||||
SGSN_CTX_REQ_E_RX_ACK,
|
||||
SGSN_CTX_REQ_E_RX_NACK, /* a nack with a reason != success */
|
||||
|
||||
/* local SGSN requests a Context from a remote peer (SGSN/MME) */
|
||||
SGSN_CTX_REQ_E_TX_REQ,
|
||||
SGSN_CTX_REQ_E_RX_RESP,
|
||||
SGSN_CTX_REQ_E_RX_RESP_FAIL,
|
||||
SGSN_CTX_REQ_E_TX_ACK,
|
||||
SGSN_CTX_REQ_E_TX_NACK,
|
||||
};
|
||||
|
||||
struct sgsn_ctx_reqs {
|
||||
/*! contains SGSN Context Request which this peer started by requesting a Ctx */
|
||||
struct llist_head local_reqs;
|
||||
/*! contains SGSN Context Requests which the remote peer started by sending a Ctx */
|
||||
struct llist_head remote_reqs;
|
||||
|
||||
/* TEID-C */
|
||||
uint32_t lowest_teic; /*! lowest valid teic for Gn interface */
|
||||
uint32_t next_teic;
|
||||
uint32_t high_teic; /*! highest valid teic for Gn interface */
|
||||
};
|
||||
|
||||
struct sgsn_ctx_req {
|
||||
/*! entry in sgsn_ctx_reqs local or remote reqs */
|
||||
struct llist_head list;
|
||||
struct gsn_t *gsn;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
struct sockaddr_in peer;
|
||||
|
||||
uint32_t local_teic;
|
||||
uint32_t remote_teic;
|
||||
|
||||
uint16_t seq;
|
||||
};
|
||||
|
||||
struct sgsn_ctx_reqs *sgsn_ctx_reqs_init(void *ctx, uint32_t lowest_teic, uint32_t highest_teic);
|
||||
|
||||
/*! Received a SGSN Context Request from a peeer */
|
||||
int sgsn_ctx_req_fsm_rx_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq,
|
||||
union gtpie_member * const *ie, unsigned int ie_size);
|
||||
|
||||
int sgsn_ctx_req_fsm_rx_resp(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq, uint32_t local_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size);
|
||||
|
||||
int sgsn_ctx_req_fsm_rx_ack(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
uint16_t seq, uint32_t local_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size);
|
||||
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
union gtpie_member * const *ie, unsigned int ie_size,
|
||||
uint32_t *local_teic, uint16_t seq);
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
uint16_t *seq, uint32_t local_teic, uint32_t *remote_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size);
|
||||
|
||||
int sgsn_ctx_req_fsm_tx_ack(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
uint16_t *seq, uint32_t local_teic, uint32_t *remote_teic,
|
||||
union gtpie_member * const *ie, unsigned int ie_size);
|
||||
210
gtp/gtpie.c
210
gtp/gtpie.c
@@ -37,7 +37,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gtpie.h"
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
/*! Encode a TLV type Information Element.
|
||||
* \param[inout] p Pointer to output packet to which IE is appended
|
||||
@@ -155,7 +155,7 @@ int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
|
||||
* \param[in] type Tag/IEI for which we're looking
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \returns index into \a ie on success; -1 if not found */
|
||||
int gtpie_getie(union gtpie_member *ie[], int type, int instance)
|
||||
int gtpie_getie(union gtpie_member * const ie[], int type, int instance)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < GTPIE_SIZE; j++) {
|
||||
@@ -172,7 +172,7 @@ int gtpie_getie(union gtpie_member *ie[], int type, int instance)
|
||||
* \param[in] type Tag/IEI for which we're looking
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \returns 1 if IEI instance present in \a ie; 0 if not */
|
||||
int gtpie_exist(union gtpie_member *ie[], int type, int instance)
|
||||
int gtpie_exist(union gtpie_member * const ie[], int type, int instance)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < GTPIE_SIZE; j++) {
|
||||
@@ -192,7 +192,7 @@ int gtpie_exist(union gtpie_member *ie[], int type, int instance)
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \param[in] size Size of \a dst in bytes
|
||||
* \returns 0 on sucess; EOF in case value is larger than \a size */
|
||||
int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettlv(union gtpie_member * const ie[], int type, int instance,
|
||||
unsigned int *length, void *dst, unsigned int size)
|
||||
{
|
||||
int ien;
|
||||
@@ -214,7 +214,7 @@ int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \param[in] size Size of value in bytes
|
||||
* \returns 0 on sucess; EOF in case IE not found */
|
||||
int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettv0(union gtpie_member * const ie[], int type, int instance,
|
||||
void *dst, unsigned int size)
|
||||
{
|
||||
int ien;
|
||||
@@ -232,7 +232,7 @@ int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \returns 0 on sucess; EOF in case IE not found */
|
||||
int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettv1(union gtpie_member * const ie[], int type, int instance,
|
||||
uint8_t * dst)
|
||||
{
|
||||
int ien;
|
||||
@@ -250,7 +250,7 @@ int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \returns 0 on sucess; EOF in case IE not found */
|
||||
int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettv2(union gtpie_member * const ie[], int type, int instance,
|
||||
uint16_t * dst)
|
||||
{
|
||||
int ien;
|
||||
@@ -268,7 +268,7 @@ int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \returns 0 on sucess; EOF in case IE not found */
|
||||
int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettv4(union gtpie_member * const ie[], int type, int instance,
|
||||
uint32_t * dst)
|
||||
{
|
||||
int ien;
|
||||
@@ -286,7 +286,7 @@ int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
||||
* \param[in] instance Instance (number of occurence) of this IEI
|
||||
* \param[inout] dst Caller-allocated buffer where to store value
|
||||
* \returns 0 on sucess; EOF in case IE not found */
|
||||
int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
||||
int gtpie_gettv8(union gtpie_member * const ie[], int type, int instance,
|
||||
uint64_t * dst)
|
||||
{
|
||||
int ien;
|
||||
@@ -599,7 +599,7 @@ int gtpie_decaps(union gtpie_member *ie[], int version, const void *pack,
|
||||
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
|
||||
* \param[out] len Encoded length of \a pack in bytes
|
||||
* \returns 0 on sucess; 2 for out-of-space */
|
||||
int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
||||
int gtpie_encaps(union gtpie_member * const ie[], void *pack, unsigned *len)
|
||||
{
|
||||
int i;
|
||||
unsigned char *p;
|
||||
@@ -770,11 +770,11 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
|
||||
|
||||
/*! Encode GTP packet payload from Array of Information Elements.
|
||||
* \param[out] ie Input Array of GTPIE
|
||||
* \param[in] size Size of ?
|
||||
* \param[in] size Size of ie
|
||||
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
|
||||
* \param[out] len Encoded length of \a pack in bytes
|
||||
* \returns 0 on sucess; 2 for out-of-space */
|
||||
int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
||||
int gtpie_encaps2(const union gtpie_member ie[], unsigned int size,
|
||||
void *pack, unsigned *len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
@@ -944,3 +944,189 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Encode GTP packet payload from Array of Information Elements.
|
||||
* \param[in] ie Input Array of GTPIE
|
||||
* \param[in] ie_len Length of \a ie array
|
||||
* \param[in] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
|
||||
* \param[in] pack_len Length of \a pack buffer
|
||||
* \param[out] encoded_len Encoded length of \a pack in bytes
|
||||
* \returns 0 on success; 2 for out-of-space
|
||||
* GTP requires a certain order, the call must follow those which are defined for every message */
|
||||
int gtpie_encaps3(union gtpie_member * const ies[], unsigned int ie_len,
|
||||
void *pack, unsigned pack_len, unsigned *encoded_len)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char *p;
|
||||
unsigned char *end;
|
||||
int iesize;
|
||||
union gtpie_member *ie;
|
||||
|
||||
*encoded_len = 0;
|
||||
p = pack;
|
||||
|
||||
memset(pack, 0, pack_len);
|
||||
end = p + pack_len;
|
||||
for (i = 0; i < ie_len; i++) {
|
||||
if (!ies[i])
|
||||
continue;
|
||||
ie = ies[i];
|
||||
|
||||
if (GTPIE_DEBUG)
|
||||
printf
|
||||
("gtpie_encaps. Number %d, Type %d\n",
|
||||
i, ie->t);
|
||||
switch (ie->t) {
|
||||
case GTPIE_CAUSE: /* TV GTPIE types with value length 1 */
|
||||
case GTPIE_REORDER:
|
||||
case GTPIE_MAP_CAUSE:
|
||||
case GTPIE_MS_VALIDATED:
|
||||
case GTPIE_RECOVERY:
|
||||
case GTPIE_SELECTION_MODE:
|
||||
case GTPIE_TEARDOWN:
|
||||
case GTPIE_NSAPI:
|
||||
case GTPIE_RANAP_CAUSE:
|
||||
case GTPIE_RP_SMS:
|
||||
case GTPIE_RP:
|
||||
case GTPIE_MS_NOT_REACH:
|
||||
case GTPIE_BCM:
|
||||
iesize = 2;
|
||||
break;
|
||||
case GTPIE_PFI: /* TV GTPIE types with value length 2 */
|
||||
case GTPIE_CHARGING_C:
|
||||
case GTPIE_TRACE_REF:
|
||||
case GTPIE_TRACE_TYPE:
|
||||
iesize = 3;
|
||||
break;
|
||||
case GTPIE_QOS_PROFILE0: /* TV GTPIE types with value length 3 */
|
||||
case GTPIE_P_TMSI_S:
|
||||
iesize = 4;
|
||||
break;
|
||||
case GTPIE_TLLI: /* TV GTPIE types with value length 4 */
|
||||
case GTPIE_P_TMSI:
|
||||
case GTPIE_TEI_DI:
|
||||
case GTPIE_TEI_C:
|
||||
case GTPIE_CHARGING_ID:
|
||||
iesize = 5;
|
||||
break;
|
||||
case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */
|
||||
iesize = 6;
|
||||
break;
|
||||
case GTPIE_RAI: /* TV GTPIE types with value length 6 */
|
||||
iesize = 7;
|
||||
break;
|
||||
case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */
|
||||
iesize = 8;
|
||||
break;
|
||||
case GTPIE_IMSI: /* TV GTPIE types with value length 8 */
|
||||
iesize = 9;
|
||||
break;
|
||||
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
|
||||
iesize = 29;
|
||||
break;
|
||||
case GTPIE_EXT_HEADER_T: /* GTP extension header */
|
||||
iesize = 2 + hton8(ie->ext.l);
|
||||
break;
|
||||
case GTPIE_EUA: /* TLV GTPIE types with length length 2 */
|
||||
case GTPIE_MM_CONTEXT:
|
||||
case GTPIE_PDP_CONTEXT:
|
||||
case GTPIE_APN:
|
||||
case GTPIE_PCO:
|
||||
case GTPIE_GSN_ADDR:
|
||||
case GTPIE_MSISDN:
|
||||
case GTPIE_QOS_PROFILE:
|
||||
case GTPIE_AUTH_QUINTUP:
|
||||
case GTPIE_TFT:
|
||||
case GTPIE_TARGET_INF:
|
||||
case GTPIE_UTRAN_TRANS:
|
||||
case GTPIE_RAB_SETUP:
|
||||
case GTPIE_TRIGGER_ID:
|
||||
case GTPIE_OMC_ID:
|
||||
case GTPIE_RAN_T_CONTAIN:
|
||||
case GTPIE_PDP_CTX_PRIO:
|
||||
case GTPIE_ADDL_RAB_S_I:
|
||||
case GTPIE_SGSN_NUMBER:
|
||||
case GTPIE_COMMON_FLAGS:
|
||||
case GTPIE_APN_RESTR:
|
||||
case GTPIE_R_PRIO_LCS:
|
||||
case GTPIE_RAT_TYPE:
|
||||
case GTPIE_USER_LOC:
|
||||
case GTPIE_MS_TZ:
|
||||
case GTPIE_IMEI_SV:
|
||||
case GTPIE_CML_CHG_I_CT:
|
||||
case GTPIE_MBMS_UE_CTX:
|
||||
case GTPIE_TMGI:
|
||||
case GTPIE_RIM_ROUT_ADDR:
|
||||
case GTPIE_MBMS_PCO:
|
||||
case GTPIE_MBMS_SA:
|
||||
case GTPIE_SRNC_PDCP_CTX:
|
||||
case GTPIE_ADDL_TRACE:
|
||||
case GTPIE_HOP_CTR:
|
||||
case GTPIE_SEL_PLMN_ID:
|
||||
case GTPIE_MBMS_SESS_ID:
|
||||
case GTPIE_MBMS_2_3G_IND:
|
||||
case GTPIE_ENH_NSAPI:
|
||||
case GTPIE_MBMS_SESS_DUR:
|
||||
case GTPIE_A_MBMS_TRAC_I:
|
||||
case GTPIE_MBMS_S_REP_N:
|
||||
case GTPIE_MBMS_TTDT:
|
||||
case GTPIE_PS_HO_REQ_CTX:
|
||||
case GTPIE_BSS_CONTAINER:
|
||||
case GTPIE_CELL_ID:
|
||||
case GTPIE_PDU_NUMBERS:
|
||||
case GTPIE_BSSGP_CAUSE:
|
||||
case GTPIE_RQD_MBMS_BCAP:
|
||||
case GTPIE_RIM_RA_DISCR:
|
||||
case GTPIE_L_SETUP_PFCS:
|
||||
case GTPIE_PS_HO_XID_PAR:
|
||||
case GTPIE_MS_CHG_REP_A:
|
||||
case GTPIE_DIR_TUN_FLAGS:
|
||||
case GTPIE_CORREL_ID:
|
||||
case GTPIE_MBMS_FLOWI:
|
||||
case GTPIE_MBMS_MC_DIST:
|
||||
case GTPIE_MBMS_DIST_ACK:
|
||||
case GTPIE_R_IRAT_HO_INF:
|
||||
case GTPIE_RFSP_IDX:
|
||||
case GTPIE_FQDN:
|
||||
case GTPIE_E_ALL_PRIO_1:
|
||||
case GTPIE_E_ALL_PRIO_2:
|
||||
case GTPIE_E_CMN_FLAGS:
|
||||
case GTPIE_U_CSG_INFO:
|
||||
case GTPIE_CSG_I_REP_ACT:
|
||||
case GTPIE_CSG_ID:
|
||||
case GTPIE_CSG_MEMB_IND:
|
||||
case GTPIE_AMBR:
|
||||
case GTPIE_UE_NET_CAPA:
|
||||
case GTPIE_UE_AMBR:
|
||||
case GTPIE_APN_AMBR_NS:
|
||||
case GTPIE_GGSN_BACKOFF:
|
||||
case GTPIE_S_PRIO_IND:
|
||||
case GTPIE_S_PRIO_IND_NS:
|
||||
case GTPIE_H_BR_16MBPS_F:
|
||||
case GTPIE_A_MMCTX_SRVCC:
|
||||
case GTPIE_A_FLAGS_SRVCC:
|
||||
case GTPIE_STN_SR:
|
||||
case GTPIE_C_MSISDN:
|
||||
case GTPIE_E_RANAP_CAUSE:
|
||||
case GTPIE_ENODEB_ID:
|
||||
case GTPIE_SEL_MODE_NS:
|
||||
case GTPIE_ULI_TIMESTAMP:
|
||||
case GTPIE_CHARGING_ADDR:
|
||||
case GTPIE_PRIVATE:
|
||||
iesize = 3 + hton16(ie->tlv.l);
|
||||
break;
|
||||
default:
|
||||
return 2; /* We received something unknown */
|
||||
}
|
||||
|
||||
if (p + iesize < end) {
|
||||
memcpy(p, ie, iesize);
|
||||
p += iesize;
|
||||
*encoded_len += iesize;
|
||||
} else
|
||||
return 2; /* Out of space */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
10
gtp/pdp.c
10
gtp/pdp.c
@@ -28,8 +28,10 @@
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "pdp.h"
|
||||
#include "gtp.h"
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "lookupa.h"
|
||||
#include "queue.h"
|
||||
|
||||
@@ -338,9 +340,7 @@ int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
|
||||
|
||||
int gtp_pdp_getimsi(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
return gtp_pdp_tidget(gsn, pdp,
|
||||
(imsi & 0x0fffffffffffffffull) +
|
||||
((uint64_t) nsapi << 60));
|
||||
return gtp_pdp_tidget(gsn, pdp, pdp_gettid(imsi, nsapi));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include "pdp.h"
|
||||
#include "gtp.h"
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
/*! \brief dump a queue_t to stdout */
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "gtp.h"
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#define QUEUE_DEBUG 0 /* Print debug information */
|
||||
|
||||
#define QUEUE_SIZE 1024 /* Size of retransmission queue */
|
||||
#define QUEUE_SIZE (PDP_MAX*2) /* Size of retransmission queue */
|
||||
#define QUEUE_HASH_SIZE 65536 /* Size of hash table (2^16) */
|
||||
|
||||
struct qmsg_t { /* Holder for queued packets */
|
||||
|
||||
3
include/Makefile.am
Normal file
3
include/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
SUBDIRS = \
|
||||
osmocom \
|
||||
$(NULL)
|
||||
3
include/osmocom/Makefile.am
Normal file
3
include/osmocom/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
SUBDIRS = \
|
||||
gtp \
|
||||
$(NULL)
|
||||
22
include/osmocom/gtp/Makefile.am
Normal file
22
include/osmocom/gtp/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
||||
version.h: version.h.tpl
|
||||
$(AM_V_GEN)$(MKDIR_P) $(dir $@)
|
||||
$(AM_V_GEN)sed \
|
||||
-e "s/{{VERSION}}/$$(echo '@VERSION@' | cut -d. -f1-3)/g" \
|
||||
-e "s/{{VERSION_MAJOR}}/$$(echo '@VERSION@' | cut -d. -f1)/g" \
|
||||
-e "s/{{VERSION_MINOR}}/$$(echo '@VERSION@' | cut -d. -f2)/g" \
|
||||
-e "s/{{VERSION_PATCH}}/$$(echo '@VERSION@' | cut -d. -f3)/g" \
|
||||
$< > $@
|
||||
|
||||
EXTRA_DIST = \
|
||||
version.h.tpl \
|
||||
$(NULL)
|
||||
|
||||
libgtp_HEADERS = \
|
||||
gsn.h \
|
||||
gtp.h \
|
||||
gtpie.h \
|
||||
pdp.h \
|
||||
version.h \
|
||||
$(NULL)
|
||||
|
||||
libgtpdir = $(includedir)/osmocom/gtp
|
||||
197
include/osmocom/gtp/gsn.h
Normal file
197
include/osmocom/gtp/gsn.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
*
|
||||
* The contents of this file may be used under the terms of the GNU
|
||||
* General Public License Version 2, provided that the above copyright
|
||||
* notice and this permission notice is included in all copies or
|
||||
* substantial portions of the software.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GSN_H
|
||||
#define _GSN_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include "gtpie.h"
|
||||
#include "pdp.h"
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
|
||||
struct sgsn_ctx_reqs;
|
||||
|
||||
extern struct osmo_tdef gtp_T_defs[];
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
enum gsn_rate_ctr_keys {
|
||||
GSN_CTR_ERR_SOCKET,
|
||||
GSN_CTR_ERR_READFROM, /* Number of readfrom errors */
|
||||
GSN_CTR_ERR_SENDTO, /* Number of sendto errors */
|
||||
GSN_CTR_ERR_QUEUEFULL, /* Number of times queue was full */
|
||||
GSN_CTR_ERR_SEQ, /* Number of seq out of range */
|
||||
GSN_CTR_ERR_ADDRESS, /* GSN address conversion failed */
|
||||
GSN_CTR_ERR_UNKNOWN_PDP, /* GSN address conversion failed */
|
||||
GSN_CTR_ERR_UNEXPECTED_CAUSE, /* Unexpected cause value received */
|
||||
GSN_CTR_ERR_OUT_OF_PDP, /* Out of storage for PDP contexts */
|
||||
GSN_CTR_PKT_EMPTY, /* Number of empty packets */
|
||||
GSN_CTR_PKT_UNSUP, /* Number of unsupported version 29.60 11.1.1 */
|
||||
GSN_CTR_PKT_TOOSHORT, /* Number of too short headers 29.60 11.1.2 */
|
||||
GSN_CTR_PKT_UNKNOWN, /* Number of unknown messages 29.60 11.1.3 */
|
||||
GSN_CTR_PKT_UNEXPECT, /* Number of unexpected messages 29.60 11.1.4 */
|
||||
GSN_CTR_PKT_DUPLICATE, /* Number of duplicate or unsolicited replies */
|
||||
GSN_CTR_PKT_MISSING, /* Number of missing information field messages */
|
||||
GSN_CTR_PKT_INCORRECT, /* Number of incorrect information field messages */
|
||||
GSN_CTR_PKT_INVALID, /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.006 14.1, 14,2 */
|
||||
enum gtp_gsn_timers {
|
||||
GTP_GSN_TIMER_T3_RESPONSE = 3,
|
||||
GTP_GSN_TIMER_N3_REQUESTS = 1003,
|
||||
GTP_GSN_TIMER_T3_HOLD_RESPONSE = -3,
|
||||
};
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
|
||||
struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context)(struct pdp_t *pdp);
|
||||
int (*cb_create_context_ind)(struct pdp_t *pdp);
|
||||
int (*cb_update_context_ind)(struct pdp_t *pdp);
|
||||
int (*cb_unsup_ind)(struct sockaddr_in *peer);
|
||||
int (*cb_extheader_ind)(struct sockaddr_in *peer);
|
||||
int (*cb_ran_info_relay_ind)(struct sockaddr_in *peer, union gtpie_member **ie);
|
||||
int (*cb_conf)(int type, int cause, struct pdp_t *pdp, void *cbp);
|
||||
int (*cb_data_ind)(struct pdp_t *pdp, void *pack, unsigned len);
|
||||
int (*cb_recovery)(struct sockaddr_in *peer, uint8_t recovery);
|
||||
int (*cb_recovery2)(struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
int (*cb_recovery3)(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
int (*cb_sgsn_context_request_ind)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size);
|
||||
int (*cb_sgsn_context_response_ind)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size);
|
||||
int (*cb_sgsn_context_ack_ind)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size);
|
||||
|
||||
/* Counters */
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
/* Timers: */
|
||||
struct osmo_tdef *tdef;
|
||||
|
||||
struct sgsn_ctx_reqs *sgsn_ctx;
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind)(struct pdp_t *pdp));
|
||||
extern int gtp_set_cb_update_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_update_context_ind)(struct pdp_t *pdp));
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind)(struct pdp_t *pdp,
|
||||
void *pack, unsigned len));
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context)(struct pdp_t *pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t *pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer));
|
||||
|
||||
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer));
|
||||
|
||||
extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer, union gtpie_member **ie));
|
||||
|
||||
extern int gtp_set_cb_sgsn_context_request_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size));
|
||||
|
||||
extern int gtp_set_cb_sgsn_context_response_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size));
|
||||
|
||||
extern int gtp_set_cb_sgsn_context_ack_ind(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, const struct sockaddr_in *peer, uint32_t local_ref, union gtpie_member * const *ie, unsigned int ie_size));
|
||||
|
||||
extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb)(int type, int cause, struct pdp_t *pdp,
|
||||
void *cbp));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb)(struct sockaddr_in *peer,
|
||||
struct pdp_t *pdp,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb)(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
struct pdp_t *pdp,
|
||||
uint8_t recovery));
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
extern int gtp_fd(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
|
||||
|
||||
#endif /* !_GSN_H */
|
||||
@@ -13,13 +13,10 @@
|
||||
#define _GTP_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "gtpie.h"
|
||||
#include "pdp.h"
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
#include "gsn.h"
|
||||
|
||||
#define GTP0_PORT 3386
|
||||
#define GTP1C_PORT 2123
|
||||
@@ -31,12 +28,10 @@
|
||||
#define GTP1_HEADER_SIZE_SHORT 8
|
||||
#define GTP1_HEADER_SIZE_LONG 12
|
||||
|
||||
#define NAMESIZE 1024
|
||||
#define SYSLOG_PRINTSIZE 255
|
||||
#define ERRMSG_SIZE 255
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
#define NAMESIZE 1024
|
||||
|
||||
/* GTP version 1 extension header type definitions. */
|
||||
#define GTP_EXT_PDCP_PDU 0xC0 /* PDCP PDU Number */
|
||||
|
||||
@@ -58,7 +53,7 @@
|
||||
#define GTP_UPDATE_PDP_RSP 19 /* Update PDP Context Response */
|
||||
#define GTP_DELETE_PDP_REQ 20 /* Delete PDP Context Request */
|
||||
#define GTP_DELETE_PDP_RSP 21 /* Delete PDP Context Response */
|
||||
/* 22-25 For future use. *//* In version GTP 1 anonomous PDP context */
|
||||
/* 22-25 For future use. *//* In version GTP 1 anonomous PDP context */
|
||||
#define GTP_ERROR 26 /* Error Indication */
|
||||
#define GTP_PDU_NOT_REQ 27 /* PDU Notification Request */
|
||||
#define GTP_PDU_NOT_RSP 28 /* PDU Notification Response */
|
||||
@@ -85,6 +80,7 @@
|
||||
#define GTP_FWD_SRNS 58 /* Forward SRNS Context */
|
||||
#define GTP_FWD_RELOC_ACK 59 /* Forward Relocation Complete Acknowledge */
|
||||
#define GTP_FWD_SRNS_ACK 60 /* Forward SRNS Context Acknowledge */
|
||||
#define GTP_RAN_INFO_RELAY 70 /* RAN Information Relay */
|
||||
/* 61-239 For future use. */
|
||||
#define GTP_DATA_TRAN_REQ 240 /* Data Record Transfer Request */
|
||||
#define GTP_DATA_TRAN_RSP 241 /* Data Record Transfer Response */
|
||||
@@ -95,7 +91,7 @@ extern const struct value_string gtp_type_names[];
|
||||
static inline const char *gtp_type_name(uint8_t val)
|
||||
{ return get_value_string(gtp_type_names, val); }
|
||||
|
||||
/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
|
||||
/* GTP information element cause codes from 29.060 v15.3.0 7.7.1 */
|
||||
/* */
|
||||
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
|
||||
#define GTPCAUSE_REQ_IMEI 1 /* Request IMEI */
|
||||
@@ -103,19 +99,26 @@ static inline const char *gtp_type_name(uint8_t val)
|
||||
#define GTPCAUSE_NO_ID_NEEDED 3 /* No identity needed */
|
||||
#define GTPCAUSE_MS_REFUSES_X 4 /* MS refuses */
|
||||
#define GTPCAUSE_MS_NOT_RESP_X 5 /* MS is not GPRS responding */
|
||||
#define GTPCAUSE_006 6 /* For future use 6-48 */
|
||||
#define GTPCAUSE_049 49 /* Cause values reserved for GPRS charging protocol use (See GTP' in GSM 12.15) 49-63 */
|
||||
#define GTPCAUSE_064 64 /* For future use 64-127 */
|
||||
#define GTPCAUSE_REACTIVATION_REQ 6 /* Reactivation Requested */
|
||||
#define GTPCAUSE_PDP_ADDR_INACT 7 /* PDP address inactivity timer expires */
|
||||
#define GTPCAUSE_NET_FAILURE 8 /* Network failure */
|
||||
#define GTPCAUSE_QOS_MISMATCH 9 /* QoS parameter mismatch */
|
||||
|
||||
/* 10-48 For future use */
|
||||
/* 49-63 Cause values reserved for GPRS charging protocol use (See GTP' 3GPP TS 32.295) */
|
||||
/* 64-127 For future use */
|
||||
#define GTPCAUSE_ACC_REQ 128 /* Request accepted */
|
||||
#define GTPCAUSE_129 129 /* For future use 129-176 */
|
||||
#define GTPCAUSE_177 177 /* Cause values reserved for GPRS charging protocol use (See GTP' In GSM 12.15) 177-191 */
|
||||
#define GTPCAUSE_NEW_PDP_NET_PREF 129 /* New PDP type due to network preference */
|
||||
#define GTPCAUSE_NEW_PDP_ADDR_BEAR 130 /* New PDP type due to single address bearer only */
|
||||
/* 131-176 For future use */
|
||||
/* 177-191 Cause values reserved for GPRS charging protocol use (See GTP' 3GPP TS 32.295) */
|
||||
#define GTPCAUSE_NON_EXIST 192 /* Non-existent */
|
||||
#define GTPCAUSE_INVALID_MESSAGE 193 /* Invalid message format */
|
||||
#define GTPCAUSE_IMSI_NOT_KNOWN 194 /* IMSI not known */
|
||||
#define GTPCAUSE_MS_DETACHED 195 /* MS is GPRS detached */
|
||||
#define GTPCAUSE_MS_NOT_RESP 196 /* MS is not GPRS responding */
|
||||
#define GTPCAUSE_MS_REFUSES 197 /* MS refuses */
|
||||
#define GTPCAUSE_198 198 /* For future use */
|
||||
#define GTPCAUSE_VERSION_NOT_SUPPORTED 198 /* Version not supported */
|
||||
#define GTPCAUSE_NO_RESOURCES 199 /* No resources available */
|
||||
#define GTPCAUSE_NOT_SUPPORTED 200 /* Service not supported */
|
||||
#define GTPCAUSE_MAN_IE_INCORRECT 201 /* Mandatory IE incorrect */
|
||||
@@ -138,8 +141,15 @@ static inline const char *gtp_type_name(uint8_t val)
|
||||
#define GTPCAUSE_SYN_ERR_FILTER 218 /* Syntactic errors in packet filter(s) */
|
||||
#define GTPCAUSE_MISSING_APN 219 /* Missing or unknown APN */
|
||||
#define GTPCAUSE_UNKNOWN_PDP 220 /* Unknown PDP address or PDP type */
|
||||
#define GTPCAUSE_221 221 /* For Future Use 221-240 */
|
||||
#define GTPCAUSE_241 241 /* Cause Values Reserved For Gprs Charging Protocol Use (See Gtp' In Gsm 12.15) 241-255 */
|
||||
/* 234-240 For future use */
|
||||
/* 241-255 Cause Values Reserved For Gprs Charging Protocol Use (See Gtp' 3GPP TS 32.295) */
|
||||
|
||||
static inline bool gtp_cause_successful(uint8_t cause)
|
||||
{
|
||||
return cause == GTPCAUSE_ACC_REQ ||
|
||||
cause == GTPCAUSE_NEW_PDP_NET_PREF ||
|
||||
cause == GTPCAUSE_NEW_PDP_ADDR_BEAR;
|
||||
}
|
||||
|
||||
struct ul66_t;
|
||||
struct ul16_t;
|
||||
@@ -230,110 +240,22 @@ union gtp_packet {
|
||||
struct gtp1_packet_long gtp1l;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
|
||||
struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context) (struct pdp_t *);
|
||||
int (*cb_create_context_ind) (struct pdp_t *);
|
||||
int (*cb_unsup_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_extheader_ind) (struct sockaddr_in * peer);
|
||||
int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
|
||||
int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
|
||||
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
|
||||
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
|
||||
/* Counters */
|
||||
|
||||
uint64_t err_socket; /* Number of socket errors */
|
||||
uint64_t err_readfrom; /* Number of readfrom errors */
|
||||
uint64_t err_sendto; /* Number of sendto errors */
|
||||
uint64_t err_memcpy; /* Number of memcpy */
|
||||
uint64_t err_queuefull; /* Number of times queue was full */
|
||||
uint64_t err_seq; /* Number of seq out of range */
|
||||
uint64_t err_address; /* GSN address conversion failed */
|
||||
uint64_t err_unknownpdp; /* GSN address conversion failed */
|
||||
uint64_t err_unknowntid; /* Application supplied unknown imsi+nsapi */
|
||||
uint64_t err_cause; /* Unexpected cause value received */
|
||||
uint64_t err_outofpdp; /* Out of storage for PDP contexts */
|
||||
|
||||
uint64_t empty; /* Number of empty packets */
|
||||
uint64_t unsup; /* Number of unsupported version 29.60 11.1.1 */
|
||||
uint64_t tooshort; /* Number of too short headers 29.60 11.1.2 */
|
||||
uint64_t unknown; /* Number of unknown messages 29.60 11.1.3 */
|
||||
uint64_t unexpect; /* Number of unexpected messages 29.60 11.1.4 */
|
||||
uint64_t duplicate; /* Number of duplicate or unsolicited replies */
|
||||
uint64_t missing; /* Number of missing information field messages */
|
||||
uint64_t incorrect; /* Number of incorrect information field messages */
|
||||
uint64_t invalid; /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern const char *gtp_version();
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct
|
||||
pdp_t *
|
||||
pdp));
|
||||
|
||||
extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
int cause);
|
||||
|
||||
extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp, struct in_addr *inetaddr);
|
||||
|
||||
extern int gtp_update_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
int cause);
|
||||
|
||||
extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp, int teardown)
|
||||
OSMO_DEPRECATED("Use gtp_delete_context_req2() instead, to avoid freeing pdp ctx before reply");
|
||||
@@ -343,103 +265,47 @@ extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len));
|
||||
extern int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
|
||||
const uint8_t *ran_container, size_t ran_container_len,
|
||||
const uint8_t *rim_route_addr, size_t rim_route_addr_len,
|
||||
uint8_t rim_route_addr_discr);
|
||||
|
||||
/* Tx a SGSN Context Request */
|
||||
extern int gtp_sgsn_context_req(struct gsn_t *gsn, uint32_t *local_ref,
|
||||
const struct sockaddr_in *peer, union gtpie_member **ie);
|
||||
|
||||
/* Tx a SGSN Context Response */
|
||||
extern int gtp_sgsn_context_resp(struct gsn_t *gsn, uint32_t local_ref,
|
||||
union gtpie_member **ie);
|
||||
|
||||
/* Tx a SGSN Context Response, simplified when returning an error */
|
||||
int gtp_sgsn_context_resp_error(struct gsn_t *gsn, uint32_t local_ref,
|
||||
uint8_t cause);
|
||||
|
||||
/* Tx a SGSN Context Ack */
|
||||
extern int gtp_sgsn_context_ack(struct gsn_t *gsn, uint32_t local_ref,
|
||||
union gtpie_member **ie);
|
||||
|
||||
/* Tx a SGSN Context Ack, simplified when returning an error */
|
||||
int gtp_sgsn_context_ack_error(struct gsn_t *gsn, uint32_t local_ref,
|
||||
uint8_t cause);
|
||||
|
||||
|
||||
extern int gtp_fd(struct gsn_t *gsn);
|
||||
extern int gtp_decaps0(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1c(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1u(struct gsn_t *gsn);
|
||||
extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
|
||||
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context) (struct pdp_t *
|
||||
pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t* pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb) (int type, int cause, struct pdp_t * pdp,
|
||||
void *cbp));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");;
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery));
|
||||
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
|
||||
/* Internal functions (not part of the API */
|
||||
|
||||
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
|
||||
struct in_addr *inetaddrs);
|
||||
extern int gtp_echo_resp(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len);
|
||||
extern int gtp_echo_ind(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len);
|
||||
extern int gtp_echo_conf(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, void *pack, unsigned len);
|
||||
|
||||
extern int gtp_unsup_req(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer,
|
||||
int fd, void *pack, unsigned len);
|
||||
extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_create_pdp_resp(struct gsn_t *gsn, int version,
|
||||
struct pdp_t *pdp, uint8_t cause);
|
||||
|
||||
extern int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *cbp,
|
||||
struct in_addr *inetaddr, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *cbp,
|
||||
struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len,
|
||||
struct pdp_t *pdp, struct pdp_t *linked_pdp,
|
||||
uint8_t cause, int teardown);
|
||||
|
||||
extern int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer, int fd,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
|
||||
struct sockaddr_in *peer,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int ipv42eua(struct ul66_t *eua, struct in_addr *src);
|
||||
extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua);
|
||||
extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna);
|
||||
extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src);
|
||||
|
||||
extern int gtp_encode_pdp_ctx(uint8_t *buf, unsigned int size, const struct pdp_t *pdp, uint16_t sapi);
|
||||
extern int gtp_decode_pdp_ctx(const uint8_t *buf, unsigned int size, struct pdp_t *pdp, uint16_t *sapi);
|
||||
|
||||
extern const char *imsi_gtp2str(const uint64_t *imsi);
|
||||
|
||||
/*! Set the talloc context for internal objects */
|
||||
void gtp_set_talloc_ctx(void *ctx);
|
||||
|
||||
#endif /* !_GTP_H */
|
||||
@@ -301,25 +301,27 @@ extern int gtpie_tv4(void *p, unsigned int *length, unsigned int size,
|
||||
uint8_t t, uint32_t v);
|
||||
extern int gtpie_tv8(void *p, unsigned int *length, unsigned int size,
|
||||
uint8_t t, uint64_t v);
|
||||
extern int gtpie_getie(union gtpie_member *ie[], int type, int instance);
|
||||
extern int gtpie_exist(union gtpie_member *ie[], int type, int instance);
|
||||
extern int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_getie(union gtpie_member * const ie[], int type, int instance);
|
||||
extern int gtpie_exist(union gtpie_member * const ie[], int type, int instance);
|
||||
extern int gtpie_gettlv(union gtpie_member * const ie[], int type, int instance,
|
||||
unsigned int *length, void *dst, unsigned int size);
|
||||
extern int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_gettv0(union gtpie_member * const ie[], int type, int instance,
|
||||
void *dst, unsigned int size);
|
||||
extern int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_gettv1(union gtpie_member * const ie[], int type, int instance,
|
||||
uint8_t * dst);
|
||||
extern int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_gettv2(union gtpie_member * const ie[], int type, int instance,
|
||||
uint16_t * dst);
|
||||
extern int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_gettv4(union gtpie_member * const ie[], int type, int instance,
|
||||
uint32_t * dst);
|
||||
extern int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
|
||||
extern int gtpie_gettv8(union gtpie_member * const ie[], int type, int instance,
|
||||
uint64_t * dst);
|
||||
|
||||
extern int gtpie_decaps(union gtpie_member *ie[], int version,
|
||||
const void *pack, unsigned len);
|
||||
extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len);
|
||||
extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
|
||||
extern int gtpie_encaps(union gtpie_member * const ie[], void *pack, unsigned *len);
|
||||
extern int gtpie_encaps2(const union gtpie_member ie[], unsigned int size,
|
||||
void *pack, unsigned *len);
|
||||
extern int gtpie_encaps3(union gtpie_member * const ie[], unsigned int ie_len,
|
||||
void *pack, unsigned pack_len, unsigned *encoded_len);
|
||||
|
||||
#endif /* !_GTPIE_H */
|
||||
@@ -46,6 +46,11 @@ struct ul_t {
|
||||
unsigned char *v;
|
||||
};
|
||||
|
||||
struct ul1_t {
|
||||
unsigned int l;
|
||||
unsigned char v[1];
|
||||
};
|
||||
|
||||
struct ul16_t {
|
||||
unsigned int l;
|
||||
unsigned char v[16];
|
||||
@@ -244,6 +249,8 @@ struct pdp_t {
|
||||
bool tx_gpdu_seq; /* Transmit (true) or suppress G-PDU sequence numbers */
|
||||
|
||||
struct llist_head qmsg_list_req; /* list of req qmsg_t in retrans queue belonging this pdp ctx */
|
||||
|
||||
struct ul1_t dir_tun_flags; /* Direct Tunnel Flags, TS 29.060 7.7.81 */
|
||||
};
|
||||
|
||||
/* functions related to pdp_t management */
|
||||
16
include/osmocom/gtp/version.h.tpl
Normal file
16
include/osmocom/gtp/version.h.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#define LIBGTP_VERSION {{VERSION}}
|
||||
#define LIBGTP_VERSION_STR "{{VERSION}}"
|
||||
|
||||
#define LIBGTP_VERSION_MAJOR {{VERSION_MAJOR}}
|
||||
#define LIBGTP_VERSION_MINOR {{VERSION_MINOR}}
|
||||
#define LIBGTP_VERSION_PATCH {{VERSION_PATCH}}
|
||||
|
||||
#define LIBGTP_VERSION_GREATER_EQUAL(major, minor, patch) \
|
||||
(LIBGTP_VERSION_MAJOR > (major) || \
|
||||
(LIBGTP_VERSION_MAJOR == (major) && \
|
||||
LIBGTP_VERSION_MINOR > (minor)) || \
|
||||
(LIBGTP_VERSION_MAJOR == (major) && \
|
||||
LIBGTP_VERSION_MINOR == (minor) && \
|
||||
LIBGTP_VERSION_PATCH >= (patch)))
|
||||
@@ -1,10 +1,42 @@
|
||||
noinst_LIBRARIES = libmisc.a
|
||||
|
||||
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h netns.h util.h icmpv6.h checksum.h
|
||||
noinst_HEADERS = \
|
||||
checksum.h \
|
||||
gnugetopt.h \
|
||||
gtp-kernel.h \
|
||||
icmpv6.h \
|
||||
in46_addr.h \
|
||||
ippool.h \
|
||||
lookup.h \
|
||||
netdev.h \
|
||||
netns.h \
|
||||
syserr.h \
|
||||
tun.h \
|
||||
util.h \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
AM_CFLAGS = \
|
||||
-fno-builtin \
|
||||
-Wall \
|
||||
-DSBINDIR='"$(sbindir)"' \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c netns.c util.c icmpv6.c checksum.c
|
||||
libmisc_a_SOURCES = \
|
||||
checksum.c \
|
||||
debug.c \
|
||||
getopt.c \
|
||||
getopt1.c \
|
||||
icmpv6.c \
|
||||
in46_addr.c \
|
||||
ippool.c \
|
||||
lookup.c \
|
||||
netdev.c \
|
||||
netns.c \
|
||||
tun.c \
|
||||
util.c \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||
|
||||
@@ -29,7 +29,7 @@ static const struct log_info_cat default_categories[] = {
|
||||
[DICMP6] = {
|
||||
.name = "DICMP6",
|
||||
.description = "ICMPv6",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -18,22 +18,18 @@
|
||||
|
||||
#include <libgtpnl/gtp.h>
|
||||
#include <libgtpnl/gtpnl.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/util.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
|
||||
#include <libgtpnl/gtp.h>
|
||||
#include <libgtpnl/gtpnl.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
|
||||
#include "gtp-kernel.h"
|
||||
|
||||
@@ -109,23 +105,43 @@ void gtp_kernel_stop(const char *devname)
|
||||
|
||||
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
|
||||
{
|
||||
struct in_addr ms, sgsn;
|
||||
int ms_addr_count;
|
||||
struct in46_addr ms[2];
|
||||
struct in46_addr sgsn;
|
||||
struct gtp_tunnel *t;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
pdp_debug(__func__, devname, pdp);
|
||||
|
||||
in46a_from_gsna(&pdp->gsnrc, &sgsn);
|
||||
|
||||
ms_addr_count = in46a_from_eua(&pdp->eua, ms);
|
||||
if (ms_addr_count < 0)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < ms_addr_count; i++) {
|
||||
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 (in46a_to_af(&ms[i]) == AF_INET)
|
||||
gtp_tunnel_set_ms_ip4(t, &ms[i].v4);
|
||||
else {
|
||||
/* In IPv6, EUA doesn't contain the actual IP
|
||||
* addr/prefix. Set higher bits to 0 to get the 64 bit
|
||||
* netmask. */
|
||||
memset(((void *)&ms[i].v6) + 8, 0, 8);
|
||||
gtp_tunnel_set_ms_ip6(t, &ms[i].v6);
|
||||
}
|
||||
|
||||
if (in46a_to_af(&sgsn) == AF_INET)
|
||||
gtp_tunnel_set_sgsn_ip4(t, &sgsn.v4);
|
||||
else
|
||||
gtp_tunnel_set_sgsn_ip6(t, &sgsn.v6);
|
||||
|
||||
if (pdp->version == 0) {
|
||||
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
||||
gtp_tunnel_set_flowid(t, pdp->flru);
|
||||
@@ -139,21 +155,33 @@ int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
|
||||
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
||||
gtp_tunnel_free(t);
|
||||
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
|
||||
{
|
||||
int ms_addr_count;
|
||||
struct in46_addr ms[2];
|
||||
struct gtp_tunnel *t;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
pdp_debug(__func__, devname, pdp);
|
||||
|
||||
ms_addr_count = in46a_from_eua(&pdp->eua, ms);
|
||||
if (ms_addr_count < 0)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < ms_addr_count; i++) {
|
||||
t = gtp_tunnel_alloc();
|
||||
if (t == NULL)
|
||||
return -1;
|
||||
|
||||
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
|
||||
gtp_tunnel_set_family(t, in46a_to_af(&ms[i]));
|
||||
gtp_tunnel_set_version(t, pdp->version);
|
||||
if (pdp->version == 0) {
|
||||
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
||||
@@ -165,5 +193,9 @@ int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
|
||||
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
||||
gtp_tunnel_free(t);
|
||||
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
103
lib/icmpv6.c
103
lib/icmpv6.c
@@ -21,10 +21,11 @@
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "checksum.h"
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include "checksum.h"
|
||||
#include "ippool.h"
|
||||
#include "syserr.h"
|
||||
#include "icmpv6.h"
|
||||
@@ -41,6 +42,14 @@ const struct in6_addr all_router_mcast_addr = {
|
||||
.s6_addr = { 0xff,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 }
|
||||
};
|
||||
|
||||
/* RFC4291 link-local solicited-node multicast address, FF02:0:0:0:0:1:FF, 104 bits = 13 bytes */
|
||||
const uint8_t solicited_node_mcast_addr_prefix[13] = {
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xFF
|
||||
};
|
||||
|
||||
/* Prepends the ipv6 header and returns checksum content */
|
||||
uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr)
|
||||
@@ -93,11 +102,13 @@ struct msgb *icmpv6_construct_rs(const struct in6_addr *saddr)
|
||||
* \returns callee-allocated message buffer containing router advertisement */
|
||||
static struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *prefix)
|
||||
const struct in6_addr *prefix,
|
||||
uint32_t mtu)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(512,128, "IPv6 RA");
|
||||
struct icmpv6_radv_hdr *ra;
|
||||
struct icmpv6_opt_prefix *ra_opt_pref;
|
||||
struct icmpv6_opt_mtu *ra_opt_mtu;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
@@ -134,6 +145,25 @@ static struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
|
||||
ra_opt_pref->res2 = 0;
|
||||
memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
|
||||
|
||||
/* RFC4861 Section 4.6.4, MTU */
|
||||
ra_opt_mtu = (struct icmpv6_opt_mtu *) msgb_put(msg, sizeof(*ra_opt_mtu));
|
||||
ra_opt_mtu->hdr.type = 5; /* RFC4861 4.6.4 */
|
||||
ra_opt_mtu->hdr.len = 1; /* RFC4861 4.6.4 */
|
||||
ra_opt_mtu->reserved = 0;
|
||||
ra_opt_mtu->mtu = htonl(mtu);
|
||||
|
||||
/* The Prefix is contained in the Prefix Information Option of
|
||||
* the Router Advertisements and shall have the A-flag set
|
||||
* and the L-flag cleared */
|
||||
ra_opt_pref->a = 1;
|
||||
ra_opt_pref->l = 0;
|
||||
ra_opt_pref->res = 0;
|
||||
/* The lifetime of the prefix shall be set to infinity */
|
||||
ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime);
|
||||
ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime);
|
||||
ra_opt_pref->res2 = 0;
|
||||
memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
|
||||
|
||||
/* checksum */
|
||||
ra->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, daddr);
|
||||
|
||||
@@ -158,6 +188,25 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validate an ICMPv6 neighbor solicitation according to RFC4861 7.1.1 */
|
||||
static bool icmpv6_validate_neigh_solicit(const uint8_t *pack, unsigned len)
|
||||
{
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
|
||||
/* Hop limit field must have 255 */
|
||||
if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255)
|
||||
return false;
|
||||
/* FIXME: ICMP checksum is valid */
|
||||
/* ICMP length (derived from IP length) is 24 or more octets */
|
||||
if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 24)
|
||||
return false;
|
||||
/* FIXME: All included options have a length > 0 */
|
||||
/* FIXME: If the IP source address is the unspecified address, the IP
|
||||
* destination address is a solicited-node multicast address. */
|
||||
/* FIXME: If IP source is unspecified, no source link-layer addr option */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validate an ICMPv6 router advertisement according to RFC4861 6.1.2.
|
||||
Returns pointer packet header on success, NULL otherwise. */
|
||||
struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len)
|
||||
@@ -193,6 +242,7 @@ struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned
|
||||
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,
|
||||
uint32_t mtu,
|
||||
const uint8_t *pack, unsigned len)
|
||||
{
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
@@ -229,7 +279,7 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
/* 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, pdp_prefix);
|
||||
msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, pdp_prefix, mtu);
|
||||
/* Send the constructed RA to the MS */
|
||||
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
|
||||
msgb_free(msg);
|
||||
@@ -240,3 +290,46 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle incoming packets to the solicited-node multicast address */
|
||||
int handle_solicited_node_mcast(const uint8_t *pack, unsigned len)
|
||||
{
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
||||
|
||||
if (len < sizeof(*ip6h)) {
|
||||
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we only treat ICMPv6 here */
|
||||
if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_ICMPV6) {
|
||||
LOGP(DICMP6, LOGL_DEBUG, "Ignoring non-ICMP solicited-node mcast\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < sizeof(*ip6h) + sizeof(*ic6h)) {
|
||||
LOGP(DICMP6, LOGL_NOTICE, "Short ICMPv6 packet: %s\n", osmo_hexdump(pack, len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ic6h->type) {
|
||||
case 135: /* Neighbor Solicitation. RFC2461, RFC2462 */
|
||||
if (ic6h->code != 0) {
|
||||
LOGP(DICMP6, LOGL_NOTICE, "ICMPv6 type 135 but code %d\n", ic6h->code);
|
||||
return -1;
|
||||
}
|
||||
if (!icmpv6_validate_neigh_solicit(pack, len)) {
|
||||
LOGP(DICMP6, LOGL_NOTICE, "Invalid Neighbor Solicitation: %s\n",
|
||||
osmo_hexdump(pack, len));
|
||||
return -1;
|
||||
}
|
||||
/* RFC 2462: Ignore Neighbor (Duplicate Address Detection) */
|
||||
LOGP(DICMP6, LOGL_DEBUG, "Ignoring Rx ICMPv6 Neighbor Soliciation: %s\n", osmo_hexdump(pack, len));
|
||||
break;
|
||||
default:
|
||||
LOGP(DICMP6, LOGL_DEBUG, "Unknown ICMPv6 type %u\n", ic6h->type);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
29
lib/icmpv6.h
29
lib/icmpv6.h
@@ -5,8 +5,8 @@
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#define ICMPv6_OPT_TYPE_PREFIX_INFO 0x03
|
||||
|
||||
@@ -44,10 +44,9 @@ struct icmpv6_radv_hdr {
|
||||
uint8_t res:6,
|
||||
m:1,
|
||||
o:1;
|
||||
#else
|
||||
uint8_t m:1,
|
||||
o:1,
|
||||
res:6;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t o:1, m:1, res:6;
|
||||
#endif
|
||||
uint16_t router_lifetime;
|
||||
uint32_t reachable_time;
|
||||
@@ -72,10 +71,9 @@ struct icmpv6_opt_prefix {
|
||||
uint8_t res:6,
|
||||
a:1,
|
||||
l:1;
|
||||
#else
|
||||
uint8_t l:1,
|
||||
a:1,
|
||||
res:6;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t l:1, a:1, res:6;
|
||||
#endif
|
||||
uint32_t valid_lifetime;
|
||||
uint32_t preferred_lifetime;
|
||||
@@ -83,6 +81,13 @@ struct icmpv6_opt_prefix {
|
||||
uint8_t prefix[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC4861 Section 4.6.4 */
|
||||
struct icmpv6_opt_mtu {
|
||||
struct icmpv6_opt_hdr hdr;
|
||||
uint16_t reserved;
|
||||
uint32_t mtu;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr);
|
||||
|
||||
@@ -91,10 +96,14 @@ struct msgb *icmpv6_construct_rs(const struct in6_addr *saddr);
|
||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
const struct in6_addr *pdp_prefix,
|
||||
const struct in6_addr *own_ll_addr,
|
||||
uint32_t mtu,
|
||||
const uint8_t *pack, unsigned len);
|
||||
int handle_solicited_node_mcast(const uint8_t *pack, unsigned len);
|
||||
|
||||
struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len);
|
||||
|
||||
|
||||
/* RFC3307 link-local scope multicast address */
|
||||
extern const struct in6_addr all_router_mcast_addr;
|
||||
|
||||
extern const uint8_t solicited_node_mcast_addr_prefix[13];
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
@@ -375,3 +375,10 @@ default_to_dyn_v4:
|
||||
dst->v4.s_addr = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void in46a_from_gsna(const struct ul16_t *in, struct in46_addr *dst)
|
||||
{
|
||||
dst->len = in->l;
|
||||
OSMO_ASSERT(in->l <= sizeof(dst->v6));
|
||||
memcpy(&dst->v6, in->v, in->l);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
/* a simple wrapper around an in6_addr to also contain the length of the address,
|
||||
* thereby implicitly indicating the address family of the address */
|
||||
@@ -39,3 +39,5 @@ static inline bool in46a_is_v6(const struct in46_addr *addr) {
|
||||
static inline bool in46a_is_v4(const struct in46_addr *addr) {
|
||||
return addr->len == sizeof(struct in_addr);
|
||||
}
|
||||
|
||||
void in46a_from_gsna(const struct ul16_t *in, struct in46_addr *dst);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define _IPPOOL_H
|
||||
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../gtp/gtp.h"
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
/* Assuming that the address space is fragmented we need a hash table
|
||||
in order to return the addresses.
|
||||
|
||||
508
lib/netdev.c
508
lib/netdev.c
@@ -37,522 +37,14 @@
|
||||
#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_addroute4(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 = prefixlen; /* 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_route4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
{
|
||||
int fd;
|
||||
|
||||
12
lib/netdev.h
12
lib/netdev.h
@@ -53,18 +53,6 @@ struct iphdr
|
||||
};
|
||||
#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_addroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_delroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_addroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface);
|
||||
|
||||
359
lib/tun.c
359
lib/tun.c
@@ -41,255 +41,171 @@
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#elif defined (__FreeBSD__)
|
||||
#include <net/if_tun.h>
|
||||
#include <net/if_var.h>
|
||||
#include <netinet/in_var.h>
|
||||
|
||||
#elif defined (__APPLE__)
|
||||
#include <net/if.h>
|
||||
|
||||
#else
|
||||
#error "Unknown platform!"
|
||||
#endif
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "tun.h"
|
||||
#include "syserr.h"
|
||||
#include "gtp-kernel.h"
|
||||
|
||||
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, size_t prefixlen)
|
||||
{
|
||||
struct osmo_sockaddr osa = {0};
|
||||
int rc;
|
||||
rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
OSMO_ASSERT(this->netdev);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
if (addr) {
|
||||
this->addr.len = sizeof(struct in_addr);
|
||||
this->addr.v4.s_addr = addr->s_addr;
|
||||
}
|
||||
if (dstaddr) {
|
||||
this->dstaddr.len = sizeof(struct in_addr);
|
||||
this->dstaddr.v4.s_addr = dstaddr->s_addr;
|
||||
}
|
||||
if (netmask)
|
||||
this->netmask.s_addr = netmask->s_addr;
|
||||
this->addrs++;
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
this->routes = 1;
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
|
||||
size_t prefixlen)
|
||||
{
|
||||
int rc;
|
||||
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (dstaddr) {
|
||||
this->dstaddr.len = sizeof(*dstaddr);
|
||||
memcpy(&this->dstaddr.v6, dstaddr, sizeof(*dstaddr));
|
||||
}
|
||||
this->addrs++;
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
this->routes = 1;
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tun_addaddr4(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* TODO: Is this needed on FreeBSD? */
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
|
||||
|
||||
rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
this->addrs++;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tun_addaddr6(struct tun_t *this,
|
||||
struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr6(this, addr, dstaddr, prefixlen);
|
||||
|
||||
rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
this->addrs++;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
|
||||
{
|
||||
struct in_addr netmask;
|
||||
switch (addr->len) {
|
||||
case 4:
|
||||
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
||||
osa.u.sin.sin_family = AF_INET;
|
||||
memcpy(&osa.u.sin.sin_addr, &addr->v4, sizeof(struct in_addr));
|
||||
/* Store first IPv4 IP address to be used in ipup script: */
|
||||
if (this->addrs == 0) {
|
||||
this->addr.len = sizeof(struct in_addr);
|
||||
this->addr.v4.s_addr = addr->v4.s_addr;
|
||||
this->netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
||||
osa.u.sin.sin_family = AF_INET6;
|
||||
memcpy(&osa.u.sin6.sin6_addr, &addr->v6, sizeof(struct in6_addr));
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = osmo_netdev_add_addr(this->netdev, &osa, prefixlen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
this->addrs++;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
|
||||
static int tun_tundev_data_ind_cb(struct osmo_tundev *tundev, struct msgb *msg)
|
||||
{
|
||||
struct tun_t *tun = osmo_tundev_get_priv_data(tundev);
|
||||
int rc = 0;
|
||||
if (tun->cb_ind)
|
||||
rc = tun->cb_ind(tun, msgb_data(msg), msgb_length(msg));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
struct ifreq ifr;
|
||||
static struct tun_t *tun_alloc_common(const char *devname)
|
||||
{
|
||||
struct tun_t *tun;
|
||||
|
||||
#elif defined(__FreeBSD__) || defined (__APPLE__)
|
||||
char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
|
||||
int devnum;
|
||||
struct ifaliasreq areq;
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
|
||||
return EOF;
|
||||
tun = talloc_zero(NULL, struct tun_t);
|
||||
if (!tun) {
|
||||
LOGP(DTUN, LOGL_ERROR, "tun_alloc_common() failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*tun)->cb_ind = NULL;
|
||||
(*tun)->addrs = 0;
|
||||
(*tun)->routes = 0;
|
||||
tun->cb_ind = NULL;
|
||||
tun->addrs = 0;
|
||||
tun->tundev.fd = -1;
|
||||
|
||||
#if defined(__linux__)
|
||||
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");
|
||||
OSMO_STRLCPY_ARRAY(tun->devname, devname);
|
||||
|
||||
return tun;
|
||||
}
|
||||
|
||||
struct tun_t *tun_alloc_tundev(const char *devname)
|
||||
{
|
||||
struct tun_t *tun;
|
||||
int rc;
|
||||
|
||||
tun = tun_alloc_common(devname);
|
||||
if (!tun)
|
||||
return NULL;
|
||||
|
||||
tun->tundev.tundev = osmo_tundev_alloc(tun, tun->devname);
|
||||
if (!tun->tundev.tundev)
|
||||
goto err_free;
|
||||
}
|
||||
osmo_tundev_set_priv_data(tun->tundev.tundev, tun);
|
||||
osmo_tundev_set_data_ind_cb(tun->tundev.tundev, tun_tundev_data_ind_cb);
|
||||
rc = osmo_tundev_set_dev_name(tun->tundev.tundev, tun->devname);
|
||||
if (rc < 0)
|
||||
goto err_free_tundev;
|
||||
|
||||
/* 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;
|
||||
/* Open the actual tun device */
|
||||
rc = osmo_tundev_open(tun->tundev.tundev);
|
||||
if (rc < 0)
|
||||
goto err_free_tundev;
|
||||
tun->tundev.fd = osmo_tundev_get_fd(tun->tundev.tundev);
|
||||
tun->netdev = osmo_tundev_get_netdev(tun->tundev.tundev);
|
||||
|
||||
/* Disable checksums */
|
||||
if (ioctl((*tun)->fd, TUNSETNOCSUM, 1) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", (*tun)->devname);
|
||||
if (ioctl(tun->tundev.fd, TUNSETNOCSUM, 1) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", tun->devname);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
strncpy((*tun)->devname, dev_name, IFNAMSIZ);
|
||||
(*tun)->devname[IFNAMSIZ - 1] = 0;
|
||||
(*tun)->fd = -1;
|
||||
|
||||
if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
|
||||
LOGP(DTUN, LOGL_NOTICE, "tun %s configured\n", tun->devname);
|
||||
return tun;
|
||||
|
||||
err_free_tundev:
|
||||
osmo_tundev_free(tun->tundev.tundev);
|
||||
err_free:
|
||||
talloc_free(tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tun_t *tun_alloc_gtpdev(const char *devname, int fd0, int fd1u)
|
||||
{
|
||||
struct tun_t *tun;
|
||||
int rc;
|
||||
|
||||
tun = tun_alloc_common(devname);
|
||||
if (!tun)
|
||||
return NULL;
|
||||
|
||||
if (gtp_kernel_create(-1, tun->devname, 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);
|
||||
if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
|
||||
break;
|
||||
if (errno != EBUSY)
|
||||
break;
|
||||
}
|
||||
if ((*tun)->fd < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"Can't find tunnel device");
|
||||
goto err_free;
|
||||
}
|
||||
tun->netdev = osmo_netdev_alloc(tun, tun->devname);
|
||||
if (!tun->netdev)
|
||||
goto err_kernel_create;
|
||||
rc = osmo_netdev_set_ifindex(tun->netdev, if_nametoindex(tun->devname));
|
||||
if (rc < 0)
|
||||
goto err_netdev_free;
|
||||
rc = osmo_netdev_register(tun->netdev);
|
||||
if (rc < 0)
|
||||
goto err_netdev_free;
|
||||
LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
|
||||
return tun;
|
||||
|
||||
snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
|
||||
(*tun)->devname[sizeof((*tun)->devname)-1] = 0;
|
||||
|
||||
/* The tun device we found might have "old" IP addresses allocated */
|
||||
/* We need to delete those. This problem is not present on Linux */
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
/* Set up interface name */
|
||||
strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
|
||||
areq.ifra_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");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/* Delete any IP addresses until SIOCDIFADDR fails */
|
||||
while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
err_close:
|
||||
close((*tun)->fd);
|
||||
err_netdev_free:
|
||||
osmo_netdev_free(tun->netdev);
|
||||
err_kernel_create:
|
||||
gtp_kernel_stop(tun->devname);
|
||||
err_free:
|
||||
free(*tun);
|
||||
*tun = NULL;
|
||||
return -1;
|
||||
talloc_free(tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tun_free(struct tun_t *tun)
|
||||
{
|
||||
|
||||
if (tun->routes) {
|
||||
netdev_delroute4(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
|
||||
if (tun->tundev.tundev) {
|
||||
if (osmo_tundev_close(tun->tundev.tundev) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "osmo_tundev_close() failed");
|
||||
}
|
||||
|
||||
if (tun->fd >= 0) {
|
||||
if (close(tun->fd)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
|
||||
}
|
||||
}
|
||||
|
||||
osmo_tundev_free(tun->tundev.tundev);
|
||||
tun->tundev.tundev = NULL;
|
||||
/* netdev is owned by tundev: */
|
||||
tun->netdev = NULL;
|
||||
} else {
|
||||
gtp_kernel_stop(tun->devname);
|
||||
/* netdev was allocated directly, free it: */
|
||||
osmo_netdev_free(tun->netdev);
|
||||
tun->netdev = NULL;
|
||||
}
|
||||
|
||||
/* TODO: For solaris we need to unlink streams */
|
||||
|
||||
free(tun);
|
||||
talloc_free(tun);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -300,25 +216,26 @@ int tun_set_cb_ind(struct tun_t *this,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tun_decaps(struct tun_t *this)
|
||||
int tun_inject_pkt(struct tun_t *tun, void *pack, unsigned len)
|
||||
{
|
||||
unsigned char buffer[PACKET_MAX];
|
||||
int status;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
|
||||
return -1;
|
||||
if (!tun->tundev.tundev) {
|
||||
LOGTUN(LOGL_ERROR, tun,
|
||||
"Injecting decapsulated packet not supported in kernel gtp mode: %s\n",
|
||||
osmo_hexdump(pack, len));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (this->cb_ind)
|
||||
return this->cb_ind(this, buffer, status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
|
||||
{
|
||||
return write(tun->fd, pack, len);
|
||||
msg = msgb_alloc(PACKET_MAX, "tun_tx");
|
||||
OSMO_ASSERT(msg);
|
||||
memcpy(msgb_put(msg, len), pack, len);
|
||||
rc = osmo_tundev_send(tun->tundev.tundev, msg);
|
||||
if (rc < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "TUN(%s): write() failed", tun->devname);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tun_runscript(struct tun_t *tun, char *script)
|
||||
|
||||
26
lib/tun.h
26
lib/tun.h
@@ -16,6 +16,9 @@
|
||||
#include <stdbool.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <osmocom/core/netdev.h>
|
||||
#include <osmocom/core/tun.h>
|
||||
|
||||
#include "../lib/in46_addr.h"
|
||||
|
||||
#define PACKET_MAX 8196 /* Maximum packet size we receive */
|
||||
@@ -30,25 +33,29 @@
|
||||
*************************************************************/
|
||||
|
||||
struct tun_t {
|
||||
int fd; /* File descriptor to tun interface */
|
||||
/* In tun device mode: operates on the tun interface, owned by tun.tundev below.
|
||||
* In gtp kernel mode: operates on the gtp device, allocated explicitly */
|
||||
struct osmo_netdev *netdev;
|
||||
struct in46_addr addr;
|
||||
struct in46_addr dstaddr;
|
||||
struct in_addr netmask;
|
||||
int addrs; /* Number of allocated IP addresses */
|
||||
int routes; /* One if we allocated an automatic route */
|
||||
char devname[IFNAMSIZ]; /* Name of the tun device */
|
||||
int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len);
|
||||
/* to be used by libgtp callers/users (to attach their own private state) */
|
||||
void *priv;
|
||||
/* Fields only in use when using the tun device mode: */
|
||||
struct {
|
||||
struct osmo_tundev *tundev; /* Manages the tun interface; NULL on gtp kernel mode */
|
||||
int fd; /* File descriptor to tun interface; -1 on gtp kernel mode */
|
||||
} tundev;
|
||||
};
|
||||
|
||||
extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u);
|
||||
extern struct tun_t *tun_alloc_tundev(const char *devname);
|
||||
extern struct tun_t *tun_alloc_gtpdev(const char *devname, 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_inject_pkt(struct tun_t *tun, void *pack, unsigned len);
|
||||
|
||||
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
|
||||
struct in46_addr *dstaddr, size_t prefixlen);
|
||||
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr, size_t prefixlen);
|
||||
|
||||
extern int tun_set_cb_ind(struct tun_t *this,
|
||||
int (*cb_ind) (struct tun_t * tun, void *pack,
|
||||
@@ -59,4 +66,7 @@ extern int tun_runscript(struct tun_t *tun, char *script);
|
||||
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
|
||||
size_t prefix_size, int flags);
|
||||
|
||||
#define LOGTUN(level, tun, fmt, args...) \
|
||||
LOGP(DTUN, level, "TUN(%s): " fmt, (tun)->devname, ## args)
|
||||
|
||||
#endif /* !_TUN_H */
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include "ippool.h"
|
||||
#include "in46_addr.h"
|
||||
|
||||
@@ -7,5 +7,8 @@ Name: OsmoGGSN GTP Library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lgtp
|
||||
Cflags: -I${includedir}/
|
||||
|
||||
# Add two include paths to support:
|
||||
# * #include <osmocom/gtp/gtp.h> (like other Osmocom headers)
|
||||
# * #include <gtp.h> (legacy compat)
|
||||
Cflags: -I${includedir}/osmocom/gtp/ -I${includedir}/
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
Summary: Osmocom Gateway GPRS Support Node (GGSN)
|
||||
Name: @PACKAGE@
|
||||
Version: @VERSION@
|
||||
Release: 1
|
||||
URL: https://osmocom.org/projects/openggsn
|
||||
Source0: http://prdownloads.sourceforge.net/ggsn/%{name}-%{version}.tar.gz
|
||||
License: GPL
|
||||
Group: System Environment/Daemons
|
||||
BuildRoot: %{_tmppath}/%{name}-root
|
||||
|
||||
%description
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
operators as the interface between the Internet and the rest of the
|
||||
mobile network infrastructure. The project also provides an SGSN
|
||||
emulator suitable for GPRS core network testing.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
|
||||
./configure --prefix=/usr --enable-static-exec
|
||||
|
||||
make
|
||||
|
||||
%install
|
||||
|
||||
make install prefix=$RPM_BUILD_ROOT/usr
|
||||
strip $RPM_BUILD_ROOT/usr/bin/osmo-ggsn
|
||||
strip $RPM_BUILD_ROOT/usr/bin/sgsnemu
|
||||
|
||||
#Copy osmo-ggsn init script in place
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
|
||||
install -m755 examples/osmo-ggsn.init \
|
||||
$RPM_BUILD_ROOT/etc/rc.d/init.d/osmo-ggsn
|
||||
|
||||
#Copy osmo-ggsn.conf in place
|
||||
install -m755 examples/osmo-ggsn.cfg \
|
||||
$RPM_BUILD_ROOT/etc/osmo-ggsn.cfg
|
||||
|
||||
#Copy gsn_restart file in place
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/osmo-ggsn
|
||||
echo "0" > $RPM_BUILD_ROOT/var/lib/osmo-ggsn/gsn_restart
|
||||
|
||||
#Clean up unwanted library files
|
||||
rm -rf $RPM_BUILD_ROOT/usr/include/*
|
||||
rm -rf $RPM_BUILD_ROOT/usr/lib/*
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make clean
|
||||
|
||||
%post
|
||||
/sbin/chkconfig --add osmo-ggsn
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
/usr/bin/osmo-ggsn
|
||||
/usr/bin/sgsnemu
|
||||
/etc/rc.d/init.d/osmo-ggsn
|
||||
%dir /var/lib/osmo-ggsn
|
||||
/var/lib/osmo-ggsn/gsn_restart
|
||||
|
||||
%doc AUTHORS COPYING INSTALL NEWS README.md
|
||||
%doc examples/osmo-ggsn.conf
|
||||
%doc examples/sgsnemu.conf
|
||||
%doc examples/osmo-ggsn.init
|
||||
%doc examples/firewall
|
||||
%doc /usr/man/man8/osmo-ggsn.8.gz
|
||||
%doc /usr/man/man8/sgsnemu.8.gz
|
||||
|
||||
%config /etc/osmo-ggsn.cfg
|
||||
|
||||
|
||||
#/usr/lib/libgtp.a
|
||||
#/usr/lib/libgtp.la
|
||||
#/usr/lib/libgtp.so
|
||||
#/usr/lib/libgtp.so.0
|
||||
#/usr/lib/libgtp.so.0.0.0
|
||||
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Jun 30 2017 <laforge@gnumonks.org>
|
||||
- Update to OsmoGGSN
|
||||
|
||||
* Mon Jun 30 2003 <jj@openggsn.org>
|
||||
- Initial build.
|
||||
@@ -2,12 +2,22 @@ bin_PROGRAMS = sgsnemu
|
||||
|
||||
AM_LDFLAGS = @EXEC_LDFLAGS@
|
||||
|
||||
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
AM_CFLAGS = \
|
||||
-D_GNU_SOURCE \
|
||||
-fno-builtin \
|
||||
-Wall \
|
||||
-DSBINDIR='"$(sbindir)"' \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
sgsnemu_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS)
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||
AM_CFLAGS += \
|
||||
-DGTP_KERNEL \
|
||||
$(LIBGTPNL_CFLAGS) \
|
||||
$(NULL)
|
||||
sgsnemu_LDADD += $(LIBGTPNL_LIBS)
|
||||
endif
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
File autogenerated by gengetopt version 2.22.6
|
||||
generated with the following command:
|
||||
gengetopt --conf-parser
|
||||
|
||||
The developers of gengetopt consider the fixed text that goes in all
|
||||
gengetopt output files to be in the public domain:
|
||||
we make no copyright claims on it.
|
||||
*/
|
||||
* File autogenerated by gengetopt version 2.22.6
|
||||
* generated with the following command:
|
||||
* gengetopt --conf-parser
|
||||
*
|
||||
* The developers of gengetopt consider the fixed text that goes in all
|
||||
* gengetopt output files to be in the public domain:
|
||||
* we make no copyright claims on it.
|
||||
*/
|
||||
|
||||
/* If we use autoconf. */
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@@ -80,7 +80,7 @@ const char *gengetopt_args_info_help[] = {
|
||||
" --pingcount=INT Number of ping req to send (default=`0')",
|
||||
" --pingquiet Do not print ping packet info (default=off)",
|
||||
" --no-tx-gpdu-seq Don't transmit G-PDU sequence nums\n (default=off)",
|
||||
" -t, --pdp-type=(v4|v6) PDP Type (default=`v4')",
|
||||
" -t, --pdp-type=(v4|v6|v4v6) PDP Type (default=`v4')",
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -68,4 +68,4 @@ modeoption "pingcount" - "Number of ping req to send" int default="0" d
|
||||
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)"
|
||||
option "pdp-type" t "PDP Type" string default="v4" no typestr="(v4|v6|v4v6)"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
@@ -54,13 +55,15 @@
|
||||
#endif // HAVE_IN6_ADDR_GEN_MODE_NONE
|
||||
#endif
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/netns.h"
|
||||
#include "../lib/icmpv6.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/gtp_internal.h"
|
||||
#include "cmdline.h"
|
||||
|
||||
#define IPADDRLEN 256 /* Character length of addresses */
|
||||
@@ -492,27 +495,12 @@ static int process_options(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* imsi */
|
||||
if (strlen(args_info.imsi_arg) != 15) {
|
||||
if (strlen(args_info.imsi_arg) < 6 || strlen(args_info.imsi_arg) > 15) {
|
||||
printf("Invalid IMSI\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
options.imsi = 0xf000000000000000ull;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[0] - 48));
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[1] - 48)) << 4;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[2] - 48)) << 8;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[3] - 48)) << 12;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[4] - 48)) << 16;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[5] - 48)) << 20;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[6] - 48)) << 24;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[7] - 48)) << 28;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[8] - 48)) << 32;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[9] - 48)) << 36;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[10] - 48)) << 40;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[11] - 48)) << 44;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[12] - 48)) << 48;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[13] - 48)) << 52;
|
||||
options.imsi |= ((uint64_t) (args_info.imsi_arg[14] - 48)) << 56;
|
||||
options.imsi = gtp_imsi_str2gtp(args_info.imsi_arg);
|
||||
|
||||
printf("IMSI is: %s (%#08llx)\n",
|
||||
args_info.imsi_arg, options.imsi);
|
||||
@@ -937,6 +925,8 @@ static int process_options(int argc, char **argv)
|
||||
options.pdp_type = PDP_EUA_TYPE_v6;
|
||||
else if (!strcmp(args_info.pdp_type_arg, "v4"))
|
||||
options.pdp_type = PDP_EUA_TYPE_v4;
|
||||
else if (!strcmp(args_info.pdp_type_arg, "v4v6"))
|
||||
options.pdp_type = PDP_EUA_TYPE_v4v6;
|
||||
else {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unsupported/unknown PDP Type '%s'\n",
|
||||
args_info.pdp_type_arg);
|
||||
@@ -956,6 +946,9 @@ static int process_options(int argc, char **argv)
|
||||
case PDP_EUA_TYPE_v6:
|
||||
hints.ai_family = AF_INET6;
|
||||
break;
|
||||
case PDP_EUA_TYPE_v4v6:
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
break;
|
||||
default:
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0, "lookup(AF_UNSPEC) %d", options.pdp_type);
|
||||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
@@ -1208,7 +1201,7 @@ static int ping_finish()
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encaps_ping4(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
static int cb_gtpu_data_ind_ping4(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct timeval *tp;
|
||||
@@ -1270,7 +1263,7 @@ static int encaps_ping4(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encaps_ping6(struct pdp_t *pdp, struct ip6_hdr *ip6h, unsigned len)
|
||||
static int cb_gtpu_data_ind_ping6(struct pdp_t *pdp, struct ip6_hdr *ip6h, unsigned len)
|
||||
{
|
||||
const struct icmpv6_echo_hdr *ic6h = (struct icmpv6_echo_hdr *) ((uint8_t*)ip6h + sizeof(*ip6h));
|
||||
struct timeval tv;
|
||||
@@ -1337,7 +1330,7 @@ static int encaps_ping6(struct pdp_t *pdp, struct ip6_hdr *ip6h, unsigned len)
|
||||
}
|
||||
|
||||
/* Handle a received ping packet. Print out line and update statistics. */
|
||||
static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
static int cb_gtpu_data_ind_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)pack;
|
||||
struct timeval tv;
|
||||
@@ -1355,9 +1348,9 @@ static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
}
|
||||
switch(iph->version) {
|
||||
case 4:
|
||||
return encaps_ping4(pdp, pack, len);
|
||||
return cb_gtpu_data_ind_ping4(pdp, pack, len);
|
||||
case 6:
|
||||
return encaps_ping6(pdp, (struct ip6_hdr *)pack, len);
|
||||
return cb_gtpu_data_ind_ping6(pdp, (struct ip6_hdr *)pack, len);
|
||||
default:
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unknown ip header version %d", iph->version);
|
||||
return -1;
|
||||
@@ -1661,7 +1654,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
if (addr[i].len == 16)
|
||||
prefixlen = 64;
|
||||
/* printf("Setting up interface and routing\n"); */
|
||||
tun_addaddr(tun, &addr[i], NULL, prefixlen);
|
||||
tun_addaddr(tun, &addr[i], prefixlen);
|
||||
if (options.defaultroute) {
|
||||
if (in46a_is_v4(&addr[i])) {
|
||||
struct in_addr rm;
|
||||
@@ -1676,7 +1669,8 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.createif && options.pdp_type == PDP_EUA_TYPE_v6) {
|
||||
if (options.createif && (options.pdp_type == PDP_EUA_TYPE_v6 ||
|
||||
options.pdp_type == PDP_EUA_TYPE_v4v6)) {
|
||||
struct in6_addr *saddr6;
|
||||
struct msgb *msg;
|
||||
if (in46a_is_v6(&addr[0])) {
|
||||
@@ -1798,7 +1792,7 @@ static void handle_router_adv(struct pdp_t *pdp, struct ip6_hdr *ip6h, struct ic
|
||||
}
|
||||
}
|
||||
#endif
|
||||
rc = tun_addaddr(tun, &addr, NULL, opt_prefix->prefix_len);
|
||||
rc = tun_addaddr(tun, &addr, opt_prefix->prefix_len);
|
||||
if (rc < 0) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0, "Failed to add addr %s to tun %s",
|
||||
in46a_ntoa(&addr), tun->devname);
|
||||
@@ -1824,7 +1818,7 @@ static void handle_router_adv(struct pdp_t *pdp, struct ip6_hdr *ip6h, struct ic
|
||||
}
|
||||
}
|
||||
|
||||
static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
static int cb_gtpu_data_ind(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)pack;
|
||||
struct icmpv6_radv_hdr *ra;
|
||||
@@ -1838,8 +1832,8 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
break;
|
||||
}
|
||||
|
||||
/* printf("encaps_tun. Packet received: forwarding to tun\n"); */
|
||||
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
|
||||
/* printf("cb_gtpu_data_ind. Packet received: forwarding to tun\n"); */
|
||||
return tun_inject_pkt((struct tun_t *)pdp->ipif, pack, len);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -1895,9 +1889,9 @@ int main(int argc, char **argv)
|
||||
gtp_set_cb_delete_context(gsn, delete_context);
|
||||
gtp_set_cb_conf(gsn, _gtp_cb_conf);
|
||||
if (options.createif)
|
||||
gtp_set_cb_data_ind(gsn, encaps_tun);
|
||||
gtp_set_cb_data_ind(gsn, cb_gtpu_data_ind);
|
||||
else
|
||||
gtp_set_cb_data_ind(gsn, encaps_ping);
|
||||
gtp_set_cb_data_ind(gsn, cb_gtpu_data_ind_ping);
|
||||
|
||||
#if defined(__linux__)
|
||||
if ((options.createif) && (options.netns)) {
|
||||
@@ -1917,7 +1911,8 @@ 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, false, -1, -1)) {
|
||||
tun = tun_alloc_tundev(options.tun_dev_name);
|
||||
if (!tun) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"Failed to create tun");
|
||||
exit(1);
|
||||
@@ -1928,27 +1923,27 @@ int main(int argc, char **argv)
|
||||
we don't need it. Don't exit on error since this sysctl is
|
||||
only available starting with linux 4.11. */
|
||||
snprintf(buf, sizeof(buf), "%u", IN6_ADDR_GEN_MODE_NONE);
|
||||
if (proc_ipv6_conf_write(options.tun_dev_name, "addr_gen_mode", buf) < 0) {
|
||||
if (proc_ipv6_conf_write(tun->devname, "addr_gen_mode", buf) < 0) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, errno,
|
||||
"Failed to disable addr_gen_mode on %s, an extra link-local "
|
||||
"ip address will appear on the tun device.\n",
|
||||
options.tun_dev_name);
|
||||
tun->devname);
|
||||
}
|
||||
#endif
|
||||
|
||||
tun_set_cb_ind(tun, cb_tun_ind);
|
||||
if (tun->fd > maxfd)
|
||||
maxfd = tun->fd;
|
||||
if (tun->tundev.fd > maxfd)
|
||||
maxfd = tun->tundev.fd;
|
||||
|
||||
if (proc_ipv6_conf_write(options.tun_dev_name, "accept_ra", "0") < 0) {
|
||||
if (proc_ipv6_conf_write(tun->devname, "accept_ra", "0") < 0) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"Failed to disable IPv6 SLAAC on %s\n", options.tun_dev_name);
|
||||
"Failed to disable IPv6 SLAAC on %s\n", tun->devname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((options.createif) && (options.netaddr.len)) {
|
||||
tun_addaddr(tun, &options.netaddr, NULL, options.prefixlen);
|
||||
tun_addaddr(tun, &options.netaddr, options.prefixlen);
|
||||
if (options.defaultroute) {
|
||||
if (in46a_is_v4(&options.netaddr)) {
|
||||
struct in_addr rm;
|
||||
@@ -2171,7 +2166,7 @@ int main(int argc, char **argv)
|
||||
|
||||
FD_ZERO(&fds);
|
||||
if (tun)
|
||||
FD_SET(tun->fd, &fds);
|
||||
FD_SET(tun->tundev.fd, &fds);
|
||||
FD_SET(gsn->fd0, &fds);
|
||||
FD_SET(gsn->fd1c, &fds);
|
||||
FD_SET(gsn->fd1u, &fds);
|
||||
@@ -2199,9 +2194,8 @@ int main(int argc, char **argv)
|
||||
|
||||
if (!signal_received) {
|
||||
|
||||
if ((tun) && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"TUN decaps failed");
|
||||
if ((tun) && FD_ISSET(tun->tundev.fd, &fds)) {
|
||||
osmo_select_main(1);
|
||||
}
|
||||
|
||||
if (FD_ISSET(gsn->fd0, &fds))
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = \
|
||||
gtpie_test.ok \
|
||||
queue_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
gtpie_test \
|
||||
queue_test \
|
||||
$(NULL)
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
#include "../../lib/syserr.h"
|
||||
#include "../../gtp/gtpie.h"
|
||||
|
||||
static const uint8_t in[] = { 1,2,3,4,5,6 };
|
||||
static uint8_t buf[256];
|
||||
@@ -107,13 +108,53 @@ static void test_gtpie_tv8()
|
||||
OSMO_ASSERT(osmo_load32be(&buf[5]) == 0x04050607);
|
||||
}
|
||||
|
||||
static void test_gtpie_getie(void)
|
||||
{
|
||||
union gtpie_member *ie[GTPIE_SIZE] = {};
|
||||
union gtpie_member ie_elem[3] = {};
|
||||
|
||||
printf("Testing gtpie_getie()\n");
|
||||
|
||||
/* Empty ie should always return -1 */
|
||||
OSMO_ASSERT(gtpie_getie(ie, 0, 0) == -1);
|
||||
OSMO_ASSERT(gtpie_getie(ie, 0, 1) == -1);
|
||||
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_ENODEB_ID, 0) == -1);
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_ENODEB_ID, 1) == -1);
|
||||
|
||||
ie_elem[0].tv1.t = GTPIE_CAUSE;
|
||||
ie_elem[0].tv1.v = GTPIE_CAUSE;
|
||||
ie[0] = &ie_elem[0];
|
||||
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 0) == 0);
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 1) == -1);
|
||||
|
||||
|
||||
ie_elem[1].tv1.t = GTPIE_CAUSE;
|
||||
ie_elem[1].tv1.v = GTPIE_CAUSE;
|
||||
ie[2] = &ie_elem[0];
|
||||
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 0) == 0);
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 1) == 2);
|
||||
|
||||
|
||||
ie_elem[2].tv1.t = GTPIE_CAUSE;
|
||||
ie_elem[2].tv1.v = GTPIE_CAUSE;
|
||||
ie[GTPIE_SIZE - 1] = &ie_elem[0];
|
||||
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 0) == 0);
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 1) == 2);
|
||||
OSMO_ASSERT(gtpie_getie(ie, GTPIE_CAUSE, 2) == GTPIE_SIZE - 1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
@@ -124,9 +165,11 @@ int main(int argc, char **argv)
|
||||
test_gtpie_tv4();
|
||||
test_gtpie_tv8();
|
||||
|
||||
test_gtpie_getie();
|
||||
|
||||
/* TODO: gtpie_decaps() */
|
||||
/* TODO: gtpie_encaps() */
|
||||
/* TODO: gtpie_encaps2() */
|
||||
/* TODO: gtpie_getie(), gtpie_exist(), gtpie_get*() */
|
||||
/* TODO: gtpie_exist(), gtpie_get*() */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ Testing gtpie_tv1()
|
||||
Testing gtpie_tv2()
|
||||
Testing gtpie_tv4()
|
||||
Testing gtpie_tv8()
|
||||
Testing gtpie_getie()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
@@ -27,7 +28,7 @@ static void queue_print(struct queue_t *queue, char* str)
|
||||
OSMO_ASSERT(memcmp(&qmsg_zero, &queue->qmsga[n], sizeof(qmsg_zero)) == 0);
|
||||
continue;
|
||||
}
|
||||
printf("%d\t%d\t%d\t%d\t%d\t%d\t%u\t%ld\n",
|
||||
printf("%d\t%d\t%d\t%d\t%d\t%d\t%u\t%" PRIuPTR "\n",
|
||||
n,
|
||||
queue->qmsga[n].seq,
|
||||
queue->qmsga[n].next,
|
||||
@@ -35,7 +36,7 @@ static void queue_print(struct queue_t *queue, char* str)
|
||||
(int)queue->qmsga[n].timeout,
|
||||
queue->qmsga[n].retrans,
|
||||
queue->qmsga[n].type,
|
||||
((uintptr_t)queue->qmsga[n].cbp & 0xFFFFFFFF)
|
||||
(uintptr_t)queue->qmsga[n].cbp
|
||||
);
|
||||
}
|
||||
printf("======================================================\n");
|
||||
@@ -222,7 +223,7 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
test_queue_empty();
|
||||
test_queue_one();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,9 @@
|
||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/include \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = ippool_test.ok \
|
||||
ippool_test.err \
|
||||
@@ -7,7 +12,7 @@ EXTRA_DIST = ippool_test.ok \
|
||||
in46a_test.ok \
|
||||
in46a_v6_test.ok
|
||||
|
||||
noinst_PROGRAMS = ippool_test in46a_test
|
||||
check_PROGRAMS = ippool_test in46a_test
|
||||
|
||||
ippool_test_SOURCES = \
|
||||
ippool_test.c \
|
||||
|
||||
@@ -146,7 +146,7 @@ static void test_in46a_to_eua(void)
|
||||
|
||||
static void test_in46a_from_eua(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
struct in46_addr ia[2];
|
||||
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 };
|
||||
@@ -155,35 +155,35 @@ static void test_in46a_from_eua(void)
|
||||
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
||||
|
||||
/* default: v4 unspec */
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
|
||||
/* invalid */
|
||||
eua.v[0] = 0x23;
|
||||
eua.v[1] = PDP_EUA_TYPE_v4;
|
||||
eua.l = 6;
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
|
||||
|
||||
/* invalid */
|
||||
eua.v[0] = PDP_EUA_ORG_IETF;
|
||||
eua.v[1] = 0x23;
|
||||
eua.l = 6;
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
|
||||
|
||||
/* unspecified V4 */
|
||||
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
||||
eua.l = sizeof(v4_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
|
||||
/* specified V4 */
|
||||
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
||||
eua.l = sizeof(v4_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
|
||||
}
|
||||
|
||||
static void test_in46a_netmasklen(void)
|
||||
@@ -318,7 +318,7 @@ static void test_in46a_to_eua_v4v6() {
|
||||
|
||||
static void test_in46a_from_eua_v6(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
struct in46_addr ia[2];
|
||||
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,
|
||||
@@ -331,16 +331,16 @@ static void test_in46a_from_eua_v6(void)
|
||||
/* 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));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 16);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[0].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));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 16);
|
||||
OSMO_ASSERT(!memcmp(&ia[0].v6, v6_spec+2, ia[0].len));
|
||||
}
|
||||
|
||||
static void test_in46a_from_eua_v4v6(void) {
|
||||
@@ -431,7 +431,7 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
@@ -131,7 +131,9 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_category(osmo_stderr_target, 0);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
3
utils/Makefile.am
Normal file
3
utils/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
bin_PROGRAMS = gtp-echo-responder
|
||||
|
||||
gtp_echo_responder_SOURCES = gtp_echo_responder.c
|
||||
470
utils/gtp_echo_responder.c
Normal file
470
utils/gtp_echo_responder.c
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* For more info see:
|
||||
* 3GPP TS 29.060 (GTPv1 and GTPv0)
|
||||
* 3GPP TS 29.274 (GTPv2C)
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define GTP1C_PORT 2123
|
||||
#define GTP_MSGTYPE_ECHO_REQ 1
|
||||
#define GTP_MSGTYPE_ECHO_RSP 2
|
||||
#define GTP1C_IE_RECOVERY 14
|
||||
#define GTP2C_IE_RECOVERY 3
|
||||
#define GTP2C_IE_NODE_FEATURES 152
|
||||
|
||||
struct gtp1_hdr {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
|
||||
#else
|
||||
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
|
||||
#endif
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
uint32_t tei;
|
||||
uint16_t seq;
|
||||
uint8_t npdu;
|
||||
uint8_t next;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gtp2_hdr {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t reserved:3, t:1, p:1, version:3;
|
||||
#else
|
||||
uint8_t version:3, p:1, t:1, reserved:1;
|
||||
#endif
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint32_t reserved2:8, seq:24;
|
||||
#else
|
||||
uint8_t seq:24, reserved2:1;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gtp_echo_resp_state {
|
||||
struct {
|
||||
char laddr[INET6_ADDRSTRLEN];
|
||||
uint8_t recovery_ctr;
|
||||
uint8_t node_features;
|
||||
} cfg;
|
||||
struct sockaddr_storage laddr_gtpc;
|
||||
int fd_gtpc;
|
||||
};
|
||||
|
||||
struct gtp_echo_resp_state *g_st;
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("Usage: gtp-echo-responder [-h] [-V] [-l listen_addr]\n");
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf(" Some useful help...\n"
|
||||
" -h --help This help text\n"
|
||||
" -V --version Print the version of gtp-echo-responder\n"
|
||||
" -l --listen-addr Listend address for GTPCv1 and GTPCv2\n"
|
||||
" -R --recovery-counter GTP Recovery Counter to transmit in GTP Echo Response message\n"
|
||||
" -n --node-features GTPCv2 Node Features bitmask to transmit in GTP Echo Response message\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
printf("gtp-echo-responder version %s\n", PACKAGE_VERSION);
|
||||
}
|
||||
|
||||
static uint8_t parse_node_features_mask(const char *arg)
|
||||
{
|
||||
unsigned long res;
|
||||
char *end;
|
||||
errno = 0;
|
||||
|
||||
res = strtoul(arg, &end, 0);
|
||||
if ((errno == ERANGE && res == ULONG_MAX) || (errno && !res) ||
|
||||
arg == end || *end != '\0') {
|
||||
fprintf(stderr, "Failed parsing Node Features bitmask: '%s'\n", arg);
|
||||
exit(1);
|
||||
}
|
||||
if (res > 0xff) {
|
||||
fprintf(stderr, "Failed parsing Node Features bitmask: '%s' > 0xFF\n", arg);
|
||||
exit(1);
|
||||
}
|
||||
return (uint8_t)res;
|
||||
}
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "listen-addr", 1, 0, 'l'},
|
||||
{ "recovery-counter", 1, 0, 'R'},
|
||||
{ "node-features", 1, 0, 'N'},
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hVl:R:N:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 'V':
|
||||
print_version();
|
||||
exit(0);
|
||||
break;
|
||||
case 'l':
|
||||
strncpy(&g_st->cfg.laddr[0], optarg, sizeof(g_st->cfg.laddr));
|
||||
g_st->cfg.laddr[sizeof(g_st->cfg.laddr) - 1] = '\0';
|
||||
break;
|
||||
case 'R':
|
||||
g_st->cfg.recovery_ctr = (uint8_t)atoi(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
g_st->cfg.node_features = parse_node_features_mask(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int init_socket(void)
|
||||
{
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
struct sockaddr_in *saddr;
|
||||
struct sockaddr_in6 *saddr6;
|
||||
int family;
|
||||
|
||||
if (inet_pton(AF_INET6, g_st->cfg.laddr, &addr6) == 1) {
|
||||
family = AF_INET6;
|
||||
saddr6 = (struct sockaddr_in6 *)&g_st->laddr_gtpc;
|
||||
saddr6->sin6_family = family;
|
||||
saddr6->sin6_port = htons(GTP1C_PORT);
|
||||
memcpy(&saddr6->sin6_addr, &addr6, sizeof(addr6));
|
||||
} else if (inet_pton(AF_INET, g_st->cfg.laddr, &addr) == 1) {
|
||||
family = AF_INET;
|
||||
saddr = (struct sockaddr_in *)&g_st->laddr_gtpc;
|
||||
saddr->sin_family = family;
|
||||
saddr->sin_port = htons(GTP1C_PORT);
|
||||
memcpy(&saddr->sin_addr, &addr, sizeof(addr));
|
||||
} else {
|
||||
fprintf(stderr, "Failed parsing address %s\n", g_st->cfg.laddr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((g_st->fd_gtpc = socket(family, SOCK_DGRAM, 0)) < 0) {
|
||||
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (bind(g_st->fd_gtpc, (struct sockaddr *)&g_st->laddr_gtpc, sizeof(g_st->laddr_gtpc)) < 0) {
|
||||
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *sockaddr2str(const struct sockaddr *saddr)
|
||||
{
|
||||
static char _rem_addr_str[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in *saddr4;
|
||||
struct sockaddr_in6 *saddr6;
|
||||
|
||||
switch (saddr->sa_family) {
|
||||
case AF_INET6:
|
||||
saddr6 = (struct sockaddr_in6 *)saddr;
|
||||
if (!inet_ntop(saddr6->sin6_family, &saddr6->sin6_addr, _rem_addr_str, sizeof(_rem_addr_str)))
|
||||
strcpy(_rem_addr_str, "unknown");
|
||||
return _rem_addr_str;
|
||||
case AF_INET:
|
||||
saddr4 = (struct sockaddr_in *)saddr;
|
||||
if (!inet_ntop(saddr4->sin_family, &saddr4->sin_addr, _rem_addr_str, sizeof(_rem_addr_str)))
|
||||
strcpy(_rem_addr_str, "unknown");
|
||||
return _rem_addr_str;
|
||||
default:
|
||||
strcpy(_rem_addr_str, "unknown-family");
|
||||
return _rem_addr_str;
|
||||
}
|
||||
}
|
||||
|
||||
static int write_cb(int fd, const uint8_t *buf, size_t buf_len, const struct sockaddr *rem_saddr)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
||||
rc = sendto(fd, buf, buf_len, 0, rem_saddr, sizeof(struct sockaddr_storage));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "sendto() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (rc != buf_len) {
|
||||
fprintf(stderr, "sendto() short write: %zd vs exp %zu\n", rc, buf_len);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gen_gtpc1_echo_rsp(uint8_t *buf, struct gtp1_hdr *echo_req)
|
||||
{
|
||||
int offset = 0;
|
||||
struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
|
||||
unsigned exp_hdr_len = (echo_req->s || echo_req->pn || echo_req->e) ? 12 : 8;
|
||||
|
||||
memcpy(echo_rsp, echo_req, exp_hdr_len);
|
||||
echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
|
||||
offset = exp_hdr_len;
|
||||
buf[offset++] = GTP1C_IE_RECOVERY;
|
||||
buf[offset++] = g_st->cfg.recovery_ctr;
|
||||
|
||||
/* Update Length */
|
||||
echo_rsp->length = htons(offset - 8);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int gen_gtpc2_echo_rsp(uint8_t *buf, struct gtp2_hdr *echo_req)
|
||||
{
|
||||
int offset = 0;
|
||||
struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
|
||||
unsigned exp_hdr_len = 8;
|
||||
|
||||
memcpy(echo_rsp, echo_req, exp_hdr_len);
|
||||
echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
|
||||
offset = exp_hdr_len;
|
||||
|
||||
/* 3GPP TS 29.274 sec 8.5 Recovery (Restart Counter) */
|
||||
buf[offset++] = GTP2C_IE_RECOVERY;
|
||||
buf[offset++] = 0; /* IE Length (high) */
|
||||
buf[offset++] = 1; /* IE Length (low) */
|
||||
buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
|
||||
buf[offset++] = g_st->cfg.recovery_ctr;
|
||||
|
||||
/* 3GPP TS 29.274 sec 8.83 Node Features */
|
||||
if (g_st->cfg.node_features > 0) {
|
||||
buf[offset++] = GTP2C_IE_NODE_FEATURES;
|
||||
buf[offset++] = 0; /* IE Length (high) */
|
||||
buf[offset++] = 1; /* IE Length (low) */
|
||||
buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
|
||||
buf[offset++] = g_st->cfg.node_features;
|
||||
}
|
||||
|
||||
/* Update Length */
|
||||
echo_rsp->length = htons(offset - 4);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int rx_gtpc1_echo_req(struct gtp1_hdr *echo_req, unsigned buf_len, const struct sockaddr *rem_saddr)
|
||||
{
|
||||
int rc;
|
||||
const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
|
||||
uint8_t *tx_buf = alloca(tx_buf_len);
|
||||
|
||||
printf("Rx GTPCv1_ECHO_REQ from %s, Tx GTPCv1_ECHO_RSP\n", sockaddr2str(rem_saddr));
|
||||
|
||||
memset(tx_buf, 0, tx_buf_len);
|
||||
rc = gen_gtpc1_echo_rsp(tx_buf, echo_req);
|
||||
return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
|
||||
}
|
||||
|
||||
static int rx_gtpc1(struct gtp1_hdr *hdr, unsigned buf_len, const struct sockaddr *rem_saddr)
|
||||
{
|
||||
unsigned exp_hdr_len = (hdr->s || hdr->pn || hdr->e) ? 12 : 8;
|
||||
unsigned pdu_len;
|
||||
|
||||
if (buf_len < exp_hdr_len) {
|
||||
fprintf(stderr, "GTPCv1 packet size smaller than header! %u < exp %u\n", buf_len, exp_hdr_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu_len = ntohs(hdr->length);
|
||||
if (buf_len < 8 + pdu_len) {
|
||||
fprintf(stderr, "GTPCv1 packet size smaller than announced! %u < exp %u\n", buf_len, 8 + pdu_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hdr->pt != 1) {
|
||||
fprintf(stderr, "GTPCv1 Protocol Type GTP' not supported!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (hdr->type) {
|
||||
case GTP_MSGTYPE_ECHO_REQ:
|
||||
return rx_gtpc1_echo_req(hdr, buf_len, rem_saddr);
|
||||
default:
|
||||
fprintf(stderr, "Silently ignoring unexpected packet of type %u\n", hdr->type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int rx_gtpc2_echo_req(struct gtp2_hdr *echo_req, unsigned buf_len, const struct sockaddr *rem_saddr)
|
||||
{
|
||||
int rc;
|
||||
const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
|
||||
uint8_t *tx_buf = alloca(tx_buf_len);
|
||||
|
||||
if (echo_req->t) {
|
||||
fprintf(stderr, "GTPCv2 ECHO message should contain T=0!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Rx GTPCv2_ECHO_REQ from %s, Tx GTPCv2_ECHO_RSP\n", sockaddr2str(rem_saddr));
|
||||
|
||||
memset(tx_buf, 0, tx_buf_len);
|
||||
rc = gen_gtpc2_echo_rsp(tx_buf, echo_req);
|
||||
return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
|
||||
}
|
||||
|
||||
static int rx_gtpc2(struct gtp2_hdr *hdr, unsigned buf_len, const struct sockaddr *rem_saddr)
|
||||
{
|
||||
unsigned exp_hdr_len = hdr->t ? 12 : 8;
|
||||
unsigned pdu_len;
|
||||
|
||||
if (hdr->p) {
|
||||
fprintf(stderr, "GTPCv2 piggybacked message not supported!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf_len < exp_hdr_len) {
|
||||
fprintf(stderr, "GTPCv2 packet size smaller than header! %u < exp %u\n", buf_len, exp_hdr_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu_len = ntohs(hdr->length);
|
||||
/* 3GPP TS 29.274 sec 5.5.1: "Octets 3 to 4 represent the Message Length
|
||||
* field. This field shall indicate the length of the message in octets
|
||||
* excluding the mandatory part of the GTP-C header (the first 4
|
||||
* octets). The TEID (if present) and the Sequence Number shall be
|
||||
* included in the length count" */
|
||||
if (buf_len < 4 + pdu_len) {
|
||||
fprintf(stderr, "GTPCv2 packet size smaller than announced! %u < exp %u\n", buf_len, 4 + pdu_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (hdr->type) {
|
||||
case GTP_MSGTYPE_ECHO_REQ:
|
||||
return rx_gtpc2_echo_req(hdr, buf_len, rem_saddr);
|
||||
default:
|
||||
fprintf(stderr, "Silently ignoring unexpected packet of type %u\n", hdr->type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int read_cb(int fd)
|
||||
{
|
||||
ssize_t sz;
|
||||
uint8_t buf[4096];
|
||||
struct sockaddr_storage rem_saddr;
|
||||
socklen_t rem_saddr_len = sizeof(rem_saddr);
|
||||
struct gtp1_hdr *hdr1;
|
||||
|
||||
if ((sz = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&rem_saddr, &rem_saddr_len)) < 0) {
|
||||
fprintf(stderr, "recvfrom() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (sz == 0) {
|
||||
fprintf(stderr, "recvfrom() read zero bytes!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr1 = (struct gtp1_hdr *)&buf[0];
|
||||
switch (hdr1->version) {
|
||||
case 1:
|
||||
return rx_gtpc1(hdr1, sz, (const struct sockaddr *)&rem_saddr);
|
||||
case 2:
|
||||
return rx_gtpc2((struct gtp2_hdr *)&buf[0], sz, (const struct sockaddr *)&rem_saddr);
|
||||
default:
|
||||
fprintf(stderr, "Rx GTPv%u: not supported (flags=0x%x)\n", hdr1->version, buf[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int loop(void)
|
||||
{
|
||||
int rc;
|
||||
fd_set rfds;
|
||||
int nfds;
|
||||
|
||||
while (true) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(g_st->fd_gtpc, &rfds);
|
||||
nfds = g_st->fd_gtpc + 1;
|
||||
rc = select(nfds, &rfds, NULL, NULL, NULL);
|
||||
if (rc == 0)
|
||||
continue;
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "select() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(g_st->fd_gtpc, &rfds))
|
||||
read_cb(g_st->fd_gtpc);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_st = calloc(1, sizeof(struct gtp_echo_resp_state));
|
||||
|
||||
strcpy(g_st->cfg.laddr, "::");
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
printf("Listening on: %s\n", g_st->cfg.laddr);
|
||||
|
||||
if (init_socket() < 0)
|
||||
exit(1);
|
||||
|
||||
printf("Socket bound successfully, listening for requests...\n");
|
||||
|
||||
if (loop() < 0)
|
||||
exit(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
111
utils/gtp_echo_responder_test.py
Executable file
111
utils/gtp_echo_responder_test.py
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
# Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the next
|
||||
# paragraph) shall be included in all copies or substantial portions of the
|
||||
# Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
import socket
|
||||
import argparse
|
||||
import struct
|
||||
from ipaddress import ip_address, IPv4Address
|
||||
|
||||
GTP1C_PORT = 2123
|
||||
BUF_SIZE = 4096
|
||||
|
||||
GTP_HDRv1_FLAG_PN = (1<<0)
|
||||
GTP_HDRv1_FLAG_S = (1<<1)
|
||||
GTP_HDRv1_FLAG_E = (1<<2)
|
||||
GTP_HDRv1_PT_GTP = (1<<4)
|
||||
GTP_HDRv1_VER_GTP1 = (1<<5)
|
||||
|
||||
GTP_HDRv2_FLAG_T = (1<<3)
|
||||
GTP_HDRv2_FLAG_P = (1<<4)
|
||||
GTP_HDRv2_VER_GTP2 = (2<<5)
|
||||
|
||||
def gen_gtpc_v1_hdr(flags, type, length, tei, seq=0, npdu=0, next=0):
|
||||
spare = 0
|
||||
if (flags & (GTP_HDRv1_FLAG_PN|GTP_HDRv1_FLAG_S|GTP_HDRv1_FLAG_E)):
|
||||
#long format
|
||||
length += 4
|
||||
d = struct.pack('!BBHIHBB', flags, type, length, tei, seq, npdu, next)
|
||||
else:
|
||||
#short format
|
||||
d = struct.pack('!BBHI', flags, type, length, tei)
|
||||
return d
|
||||
|
||||
def gen_gtpc_v2_hdr(flags, type, length, tei=0, seq=0):
|
||||
spare = 0
|
||||
if (flags & (GTP_HDRv2_FLAG_T)):
|
||||
#long format, with TEI
|
||||
length += 4 + 4
|
||||
d = struct.pack('!BBHIHBB', flags, type, length, tei, seq >> 8, seq & 0xff, spare)
|
||||
else:
|
||||
#short format
|
||||
length += 4
|
||||
d = struct.pack('!BBHHBB', flags, type, length, seq >> 8, seq & 0xff, spare)
|
||||
return d
|
||||
|
||||
def gen_gtpc_v1_echo_req(tei=0, append_flags=0, seq=0, npdu=0, next=0):
|
||||
return gen_gtpc_v1_hdr(GTP_HDRv1_VER_GTP1 | GTP_HDRv1_PT_GTP | append_flags, 1, 0, tei, seq, npdu, next)
|
||||
|
||||
def gen_gtpc_v2_echo_req(append_flags=0, seq=0, recovery=0, node_features=-1):
|
||||
length = 0
|
||||
payload = b''
|
||||
if (recovery > 0):
|
||||
recovery_ie = struct.pack('!BHBB', 3, 1, 0, recovery)
|
||||
payload += recovery_ie
|
||||
length += len(recovery_ie)
|
||||
if (node_features > 0):
|
||||
node_features_ie = struct.pack('!BHBB', 152, 1, 0, node_features)
|
||||
payload += node_features_ie
|
||||
length += len(node_features_ie)
|
||||
return gen_gtpc_v2_hdr(GTP_HDRv2_VER_GTP2 | append_flags, 1, length, 0, seq) + payload
|
||||
|
||||
def tx_rx(sk, rem_addr, tx_buf, exp_rx = True):
|
||||
print('Tx ECHO_REQ to %r: %r' % (repr(rem_addr), repr(tx_buf)))
|
||||
sk.sendto(tx_buf, rem_addr)
|
||||
if exp_rx:
|
||||
rx_buf = sk.recvfrom(BUF_SIZE)
|
||||
msg = "Message from Server {}".format(rx_buf)
|
||||
print(msg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = argparse.ArgumentParser(description='Tester for gtp-echo-recorder.')
|
||||
p.add_argument('-l', '--local-address', default='127.0.0.2', help="Local GTP address")
|
||||
p.add_argument('-r', '--remote-address', default='127.0.0.1', help="Remote GTP address")
|
||||
args = p.parse_args()
|
||||
|
||||
print('Binding socket on %r...' % repr((args.local_address, GTP1C_PORT)))
|
||||
family = socket.AF_INET if type(ip_address(args.local_address)) is IPv4Address else socket.AF_INET6
|
||||
sk = socket.socket(family=family, type=socket.SOCK_DGRAM)
|
||||
sk.bind((args.local_address, GTP1C_PORT));
|
||||
|
||||
rem_addr = (args.remote_address, GTP1C_PORT)
|
||||
|
||||
tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req())
|
||||
tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req(1, GTP_HDRv1_FLAG_S, seq=67))
|
||||
tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=300, recovery=-1, node_features=-1))
|
||||
tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=99, node_features=-1))
|
||||
tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=100, node_features=0xbb))
|
||||
Reference in New Issue
Block a user