mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn.git
				synced 2025-11-04 06:03:23 +00:00 
			
		
		
		
	Compare commits
	
		
			72 Commits
		
	
	
		
			daniel/nit
			...
			1.13.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | 
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
open_collective: osmocom
 | 
			
		||||
							
								
								
									
										13
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile.am
									
									
									
									
									
								
							@@ -1,5 +1,15 @@
 | 
			
		||||
## Process this file with automake to produce Makefile.in
 | 
			
		||||
SUBDIRS = lib gtp ggsn sgsnemu doc contrib utils tests
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	  include \
 | 
			
		||||
	  lib \
 | 
			
		||||
	  gtp \
 | 
			
		||||
	  ggsn \
 | 
			
		||||
	  sgsnemu \
 | 
			
		||||
	  doc \
 | 
			
		||||
	  contrib \
 | 
			
		||||
	  utils \
 | 
			
		||||
	  tests \
 | 
			
		||||
	  $(NULL)
 | 
			
		||||
 | 
			
		||||
pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = libgtp.pc
 | 
			
		||||
@@ -15,7 +25,6 @@ EXTRA_DIST = \
 | 
			
		||||
	     README.FreeBSD \
 | 
			
		||||
	     README.MacOSX \
 | 
			
		||||
	     README.md \
 | 
			
		||||
	     contrib/osmo-ggsn.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     $(NULL)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										160
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								README.md
									
									
									
									
									
								
							@@ -5,15 +5,14 @@ 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
 | 
			
		||||
--------------
 | 
			
		||||
@@ -28,35 +27,49 @@ Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The user manual and VTY reference are optionally built in PDF form
 | 
			
		||||
as part of the build process. Find pre-rendered versions here:
 | 
			
		||||
as part of the build process. Pre-rendered versions are available here:
 | 
			
		||||
 | 
			
		||||
https://ftp.osmocom.org/docs/osmo-ggsn/master/
 | 
			
		||||
* [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
 | 
			
		||||
@@ -76,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
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
@@ -145,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
 | 
			
		||||
@@ -183,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
 | 
			
		||||
---------
 | 
			
		||||
@@ -196,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
 | 
			
		||||
@@ -214,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
 | 
			
		||||
@@ -244,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:
 | 
			
		||||
@@ -270,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)
 | 
			
		||||
@@ -290,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
 | 
			
		||||
@@ -307,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)
 | 
			
		||||
@@ -331,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
 | 
			
		||||
@@ -345,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,9 @@
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								configure.ac
									
									
									
									
									
								
							@@ -71,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"])
 | 
			
		||||
@@ -154,9 +154,9 @@ adl_FUNC_GETOPT_LONG
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([foreign])
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.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)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -262,10 +262,12 @@ AC_CONFIG_FILES([Makefile
 | 
			
		||||
                 doc/manuals/Makefile
 | 
			
		||||
                 contrib/Makefile
 | 
			
		||||
                 contrib/systemd/Makefile
 | 
			
		||||
                 contrib/osmo-ggsn.spec
 | 
			
		||||
                 tests/Makefile
 | 
			
		||||
                 tests/lib/Makefile
 | 
			
		||||
                 tests/gtp/Makefile
 | 
			
		||||
                 include/Makefile
 | 
			
		||||
                 include/osmocom/Makefile
 | 
			
		||||
                 include/osmocom/gtp/Makefile
 | 
			
		||||
                 libgtp.pc])
 | 
			
		||||
AC_OUTPUT
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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]")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,137 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-ggsn
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 | 
			
		||||
#
 | 
			
		||||
# All modifications and additions to the file contributed by third parties
 | 
			
		||||
# remain the property of their copyright owners, unless otherwise agreed
 | 
			
		||||
# upon. The license for this file, and modifications and additions to the
 | 
			
		||||
# file, is the same license as for the pristine package itself (unless the
 | 
			
		||||
# license for the pristine package is not an Open Source License, in which
 | 
			
		||||
# case the license is the MIT License). An "Open Source License" is a
 | 
			
		||||
# license that conforms to the Open Source Definition (Version 1.9)
 | 
			
		||||
# published by the Open Source Initiative.
 | 
			
		||||
 | 
			
		||||
## Disable LTO for now since it breaks compilation of the tests
 | 
			
		||||
## https://osmocom.org/issues/4114
 | 
			
		||||
%define _lto_cflags %{nil}
 | 
			
		||||
 | 
			
		||||
Name:           osmo-ggsn
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        GPRS Support Node
 | 
			
		||||
License:        GPL-2.0-only AND LGPL-2.1-or-later
 | 
			
		||||
Group:          Productivity/Telephony/Servers
 | 
			
		||||
URL:            https://osmocom.org/projects/openggsn
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  libtool >= 2
 | 
			
		||||
BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libgtpnl) >= 1.2.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.9.0
 | 
			
		||||
Obsoletes:      openggsn
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
Osmo-GGSN is 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.
 | 
			
		||||
 | 
			
		||||
%package -n libgtp6
 | 
			
		||||
Summary:        Library implementing GTP between SGSN and GGSN
 | 
			
		||||
License:        GPL-2.0-only
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libgtp6
 | 
			
		||||
libgtp implements the GPRS Tunneling Protocol between SGSN and GGSN.
 | 
			
		||||
 | 
			
		||||
%package -n libgtp-devel
 | 
			
		||||
Summary:        Development files for the GTP library
 | 
			
		||||
License:        GPL-2.0-only
 | 
			
		||||
Group:          Development/Libraries/C and C++
 | 
			
		||||
Requires:       libgtp6 = %{version}
 | 
			
		||||
 | 
			
		||||
%description -n libgtp-devel
 | 
			
		||||
libgtp implements the GPRS Tunneling Protocol between SGSN and GGSN.
 | 
			
		||||
 | 
			
		||||
This subpackage contains libraries and header files for developing
 | 
			
		||||
applications that want to make use of libgtp.
 | 
			
		||||
 | 
			
		||||
%package -n gtp-echo-responder
 | 
			
		||||
Summary:        Small program answering GTP ECHO Request with GTP ECHO Response
 | 
			
		||||
License:        MIT
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n gtp-echo-responder
 | 
			
		||||
Small program answering GTP ECHO Request with GTP ECHO Response for both GTPCv1
 | 
			
		||||
and GTPCv2.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "%{version}" >.tarball-version
 | 
			
		||||
autoreconf -fi
 | 
			
		||||
%configure \
 | 
			
		||||
  --enable-gtp-linux \
 | 
			
		||||
  --disable-static \
 | 
			
		||||
  --docdir="%{_docdir}/%{name}" \
 | 
			
		||||
  --with-systemdsystemunitdir=%{_unitdir} \
 | 
			
		||||
  --includedir="%{_includedir}/%{name}"
 | 
			
		||||
make %{?_smp_mflags} V=1
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
%make_install
 | 
			
		||||
find %{buildroot} -type f -name "*.la" -delete -print
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
%pre
 | 
			
		||||
%service_add_pre %{name}.service
 | 
			
		||||
 | 
			
		||||
%post
 | 
			
		||||
%service_add_post %{name}.service
 | 
			
		||||
 | 
			
		||||
%preun
 | 
			
		||||
%service_del_preun %{name}.service
 | 
			
		||||
 | 
			
		||||
%postun
 | 
			
		||||
%service_del_postun %{name}.service
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%post   -n libgtp6 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libgtp6 -p /sbin/ldconfig
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%license COPYING
 | 
			
		||||
%doc AUTHORS README.md
 | 
			
		||||
%{_bindir}/osmo-ggsn
 | 
			
		||||
%{_bindir}/sgsnemu
 | 
			
		||||
%{_mandir}/man8/osmo-ggsn.8%{?ext_man}
 | 
			
		||||
%{_mandir}/man8/sgsnemu.8%{?ext_man}
 | 
			
		||||
%{_unitdir}/%{name}.service
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-ggsn-kernel-gtp.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-ggsn.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/sgsnemu.conf
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-ggsn.cfg
 | 
			
		||||
 | 
			
		||||
%files -n libgtp6
 | 
			
		||||
%{_libdir}/libgtp.so.6*
 | 
			
		||||
 | 
			
		||||
%files -n libgtp-devel
 | 
			
		||||
%{_includedir}/%{name}/
 | 
			
		||||
%{_libdir}/libgtp.so
 | 
			
		||||
%{_libdir}/pkgconfig/libgtp.pc
 | 
			
		||||
 | 
			
		||||
%files -n gtp-echo-responder
 | 
			
		||||
%{_bindir}/gtp-echo-responder
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
@@ -11,6 +11,10 @@ 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										99
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,102 @@
 | 
			
		||||
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 ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -7,9 +7,9 @@ Build-Depends: debhelper (>= 10),
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               libdpkg-perl, git,
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               libosmocore-dev (>= 1.9.0),
 | 
			
		||||
               osmo-gsm-manuals-dev,
 | 
			
		||||
               libgtpnl-dev (>= 1.2.0)
 | 
			
		||||
               libosmocore-dev (>= 1.11.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.6.0),
 | 
			
		||||
               libgtpnl-dev (>= 1.3.0)
 | 
			
		||||
Standards-Version: 3.9.6
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
 | 
			
		||||
@@ -24,7 +24,7 @@ Description: Osmocom Gateway GPRS Support Node (GGSN)
 | 
			
		||||
 operators as the interface between the Internet and the rest of the
 | 
			
		||||
 mobile network infrastructure.
 | 
			
		||||
 | 
			
		||||
Package: libgtp6
 | 
			
		||||
Package: libgtp11
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Section: libs
 | 
			
		||||
@@ -49,7 +49,7 @@ 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
 | 
			
		||||
@@ -62,7 +62,7 @@ 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
 | 
			
		||||
@@ -82,7 +82,7 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
# Most recent version of the package that added new symbols (OS#5318)
 | 
			
		||||
libgtp 6 libgtp6 (>= 1.8.0)
 | 
			
		||||
libgtp 11 libgtp11 (>= 1.13.0)
 | 
			
		||||
							
								
								
									
										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#
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -16,7 +16,7 @@ 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 -- \
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,12 +38,13 @@ 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 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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,12 +38,13 @@ 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
 | 
			
		||||
  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
 | 
			
		||||
@@ -51,6 +54,7 @@ ggsn ggsn0
 | 
			
		||||
  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,6 +64,7 @@ ggsn ggsn0
 | 
			
		||||
  gtpu-mode tun
 | 
			
		||||
  tun-device tun46
 | 
			
		||||
  type-support v4v6
 | 
			
		||||
  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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 network 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,
 | 
			
		||||
<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.
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,12 @@ 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[]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,16 @@ bin_PROGRAMS = osmo-ggsn
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = @EXEC_LDFLAGS@
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' $(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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										196
									
								
								ggsn/ggsn.c
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								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, OSMO_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;
 | 
			
		||||
@@ -536,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;
 | 
			
		||||
		}
 | 
			
		||||
@@ -584,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;
 | 
			
		||||
@@ -664,8 +750,10 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
 | 
			
		||||
	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;
 | 
			
		||||
@@ -699,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);
 | 
			
		||||
@@ -724,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 & OSMO_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 */
 | 
			
		||||
@@ -810,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -849,9 +968,10 @@ int ggsn_start(struct ggsn_ctx *ggsn)
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								ggsn/ggsn.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								ggsn/ggsn.h
									
									
									
									
									
								
							@@ -8,12 +8,12 @@
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
@@ -21,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 {
 | 
			
		||||
@@ -70,6 +83,8 @@ struct apn_ctx {
 | 
			
		||||
		bool shutdown;
 | 
			
		||||
		/* 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 */
 | 
			
		||||
@@ -80,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 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								ggsn/ggsn_vty.c
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								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>
 | 
			
		||||
@@ -35,88 +37,24 @@
 | 
			
		||||
#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 = {
 | 
			
		||||
@@ -223,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;
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -746,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);
 | 
			
		||||
 | 
			
		||||
@@ -1123,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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								ggsn/pco.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ggsn/pco.h
									
									
									
									
									
								
							@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "../gtp/pdp.h"
 | 
			
		||||
#include <osmocom/gtp/pdp.h>
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 24.008 10.5.6.3 */
 | 
			
		||||
enum pco_protocols {
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -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,30 @@
 | 
			
		||||
# 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=9:0:3
 | 
			
		||||
LIBVERSION=11:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = libgtp.la
 | 
			
		||||
 | 
			
		||||
include_HEADERS = gtp.h gsn.h pdp.h gtpie.h
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	    -fno-builtin \
 | 
			
		||||
	    -Wall \
 | 
			
		||||
	    -DSBINDIR='"$(sbindir)"' \
 | 
			
		||||
	    -I$(top_srcdir)/include \
 | 
			
		||||
	    $(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	    $(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
libgtp_la_SOURCES = \
 | 
			
		||||
		    gsn.c \
 | 
			
		||||
		    gsn_internal.h \
 | 
			
		||||
		    gtp.c \
 | 
			
		||||
		    gtp_internal.h \
 | 
			
		||||
		    gtpie.c \
 | 
			
		||||
		    lookupa.c \
 | 
			
		||||
		    lookupa.h \
 | 
			
		||||
		    pdp.c \
 | 
			
		||||
		    queue.c \
 | 
			
		||||
		    queue.h \
 | 
			
		||||
		    $(NULL)
 | 
			
		||||
 | 
			
		||||
libgtp_la_SOURCES = gtp.c gtp.h gsn.c gsn.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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										145
									
								
								gtp/gsn.c
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								gtp/gsn.c
									
									
									
									
									
								
							@@ -57,17 +57,20 @@
 | 
			
		||||
 | 
			
		||||
/* #include <stdint.h>  ISO C99 types */
 | 
			
		||||
 | 
			
		||||
#include "pdp.h"
 | 
			
		||||
#include "gtp.h"
 | 
			
		||||
#include "gtpie.h"
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
/* Error reporting functions */
 | 
			
		||||
 | 
			
		||||
#define LOGP_WITH_ADDR(ss, level, addr, fmt, args...)                    \
 | 
			
		||||
		LOGP(ss, level, "addr(%s:%d) " fmt,                      \
 | 
			
		||||
		     inet_ntoa((addr).sin_addr), htons((addr).sin_port), \
 | 
			
		||||
		     ##args);
 | 
			
		||||
		     ##args)
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc gsn_ctr_description[] = {
 | 
			
		||||
	[GSN_CTR_ERR_SOCKET] = { "err:socket", "Socket error" },
 | 
			
		||||
@@ -423,14 +426,48 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen));
 | 
			
		||||
 | 
			
		||||
	*gsn = calloc(sizeof(struct gsn_t), 1);	/* TODO */
 | 
			
		||||
	*gsn = talloc_zero(tall_libgtp_ctx, struct gsn_t);
 | 
			
		||||
 | 
			
		||||
	(*gsn)->statedir = statedir;
 | 
			
		||||
	log_restart(*gsn);
 | 
			
		||||
@@ -461,95 +498,42 @@ int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
 | 
			
		||||
 | 
			
		||||
	/* 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 (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR,
 | 
			
		||||
		     "GTPv0 socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
 | 
			
		||||
			AF_INET, SOCK_DGRAM, 0, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&addr, 0, sizeof(addr));
 | 
			
		||||
	addr.sin_family = AF_INET;
 | 
			
		||||
	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
 | 
			
		||||
	addr.sin_port = htons(GTP0_PORT);
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__APPLE__)
 | 
			
		||||
	addr.sin_len = sizeof(addr);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (bind((*gsn)->fd0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
 | 
			
		||||
			       "bind(fd0=%d) failed: Error = %s\n",
 | 
			
		||||
			       (*gsn)->fd0, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
	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 (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR,
 | 
			
		||||
		     "GTPv1 control plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
 | 
			
		||||
			AF_INET, SOCK_DGRAM, 0, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&addr, 0, sizeof(addr));
 | 
			
		||||
	addr.sin_family = AF_INET;
 | 
			
		||||
	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
 | 
			
		||||
	addr.sin_port = htons(GTP1C_PORT);
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__APPLE__)
 | 
			
		||||
	addr.sin_len = sizeof(addr);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (bind((*gsn)->fd1c, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
 | 
			
		||||
				"bind(fd1c=%d) failed: Error = %s\n",
 | 
			
		||||
				(*gsn)->fd1c, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
	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 (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR,
 | 
			
		||||
		     "GTPv1 user plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
 | 
			
		||||
			AF_INET, SOCK_DGRAM, 0, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&addr, 0, sizeof(addr));
 | 
			
		||||
	addr.sin_family = AF_INET;
 | 
			
		||||
	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
 | 
			
		||||
	addr.sin_port = htons(GTP1U_PORT);
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__APPLE__)
 | 
			
		||||
	addr.sin_len = sizeof(addr);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (bind((*gsn)->fd1u, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 | 
			
		||||
		rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
 | 
			
		||||
		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
 | 
			
		||||
			"bind(fd1u=%d) failed: Error = %s\n",
 | 
			
		||||
			(*gsn)->fd1u, strerror(errno));
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	gtp_free(*gsn);
 | 
			
		||||
	*gsn = NULL;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gtp_free(struct gsn_t *gsn)
 | 
			
		||||
@@ -568,7 +552,7 @@ int gtp_free(struct gsn_t *gsn)
 | 
			
		||||
 | 
			
		||||
	rate_ctr_group_free(gsn->ctrg);
 | 
			
		||||
 | 
			
		||||
	free(gsn);
 | 
			
		||||
	talloc_free(gsn);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -581,6 +565,13 @@ int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
 | 
			
		||||
	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. */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
							
								
								
									
										428
									
								
								gtp/gtp.c
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								gtp/gtp.c
									
									
									
									
									
								
							@@ -55,10 +55,13 @@
 | 
			
		||||
 | 
			
		||||
/* #include <stdint.h>  ISO C99 types */
 | 
			
		||||
 | 
			
		||||
#include "pdp.h"
 | 
			
		||||
#include "gtp.h"
 | 
			
		||||
#include "gtpie.h"
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
/* Error reporting functions */
 | 
			
		||||
 | 
			
		||||
@@ -74,6 +77,8 @@
 | 
			
		||||
		     inet_ntoa((addr).sin_addr), htons((addr).sin_port), \
 | 
			
		||||
		     ##args);
 | 
			
		||||
 | 
			
		||||
TALLOC_CTX *tall_libgtp_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
/* API Functions */
 | 
			
		||||
 | 
			
		||||
const char *gtp_version()
 | 
			
		||||
@@ -126,8 +131,6 @@ const struct value_string gtp_type_names[] = {
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
 | 
			
		||||
			     struct pdp_t * pdp, uint8_t recovery)
 | 
			
		||||
{
 | 
			
		||||
@@ -336,13 +339,12 @@ static uint32_t get_tei(void *pack)
 | 
			
		||||
 *   a predefined timeout.
 | 
			
		||||
 *************************************************************/
 | 
			
		||||
 | 
			
		||||
static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
 | 
			
		||||
static int gtp_req_transmit(struct gsn_t *gsn, uint8_t version, const struct in_addr *inetaddr,
 | 
			
		||||
			  union gtp_packet *packet, int len,
 | 
			
		||||
	    struct in_addr *inetaddr, void *cbp)
 | 
			
		||||
			  struct pdp_t *pdp, void *cbp)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct qmsg_t *qmsg;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	memset(&addr, 0, sizeof(addr));
 | 
			
		||||
@@ -352,33 +354,18 @@ static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
 | 
			
		||||
	addr.sin_len = sizeof(addr);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (ver == 0) {	/* Version 0 */
 | 
			
		||||
		addr.sin_port = htons(GTP0_PORT);
 | 
			
		||||
		packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
 | 
			
		||||
		packet->gtp0.h.seq = hton16(gsn->seq_next);
 | 
			
		||||
		if (pdp) {
 | 
			
		||||
			packet->gtp0.h.tid =
 | 
			
		||||
				htobe64(pdp_gettid(pdp->imsi, pdp->nsapi));
 | 
			
		||||
		}
 | 
			
		||||
		if (pdp && ((packet->gtp0.h.type == GTP_GPDU)
 | 
			
		||||
			    || (packet->gtp0.h.type == GTP_ERROR)))
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flru);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flrc);
 | 
			
		||||
	switch (version) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		fd = gsn->fd0;
 | 
			
		||||
	} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) {	/* Version 1 with seq */
 | 
			
		||||
		addr.sin_port = htons(GTP0_PORT);
 | 
			
		||||
		break;
 | 
			
		||||
	case 1:
 | 
			
		||||
		addr.sin_port = htons(GTP1C_PORT);
 | 
			
		||||
		packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
 | 
			
		||||
		packet->gtp1l.h.seq = hton16(gsn->seq_next);
 | 
			
		||||
		if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) ||
 | 
			
		||||
			    (packet->gtp1l.h.type == GTP_ERROR)))
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teid_gn);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teic_gn);
 | 
			
		||||
		fd = gsn->fd1c;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
 | 
			
		||||
		return -1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR, "Invalid GTP version %d\n", version);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sendto(fd, packet, len, 0,
 | 
			
		||||
@@ -417,6 +404,40 @@ static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
 | 
			
		||||
	    union gtp_packet *packet, int len,
 | 
			
		||||
	    const struct in_addr *inetaddr, void *cbp)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
 | 
			
		||||
 | 
			
		||||
	if (ver == 0) {	/* Version 0 */
 | 
			
		||||
		packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
 | 
			
		||||
		packet->gtp0.h.seq = hton16(gsn->seq_next);
 | 
			
		||||
		if (pdp) {
 | 
			
		||||
			packet->gtp0.h.tid =
 | 
			
		||||
				htobe64(pdp_gettid(pdp->imsi, pdp->nsapi));
 | 
			
		||||
		}
 | 
			
		||||
		if (pdp && ((packet->gtp0.h.type == GTP_GPDU)
 | 
			
		||||
			    || (packet->gtp0.h.type == GTP_ERROR)))
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flru);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flrc);
 | 
			
		||||
	} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) {	/* Version 1 with seq */
 | 
			
		||||
		packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
 | 
			
		||||
		packet->gtp1l.h.seq = hton16(gsn->seq_next);
 | 
			
		||||
		if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) ||
 | 
			
		||||
			    (packet->gtp1l.h.type == GTP_ERROR)))
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teid_gn);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teic_gn);
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gtp_req_transmit(gsn, version, inetaddr, packet, len, pdp, cbp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* gtp_conf
 | 
			
		||||
 * Remove signalling packet from retransmission queue.
 | 
			
		||||
 * return 0 on success, EOF if packet was not found */
 | 
			
		||||
@@ -451,9 +472,9 @@ static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
	     union gtp_packet *packet, int len,
 | 
			
		||||
	     struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
 | 
			
		||||
/* Send a GTP Response (generic call) */
 | 
			
		||||
static int gtp_resp(struct gsn_t *gsn, union gtp_packet *packet, int len, struct sockaddr_in *peer, int fd,
 | 
			
		||||
		     uint16_t seq, uint64_t tid, uint16_t flow, uint32_t teidc)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
 | 
			
		||||
	struct qmsg_t *qmsg;
 | 
			
		||||
@@ -462,18 +483,11 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
		packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
 | 
			
		||||
		packet->gtp0.h.seq = hton16(seq);
 | 
			
		||||
		packet->gtp0.h.tid = htobe64(tid);
 | 
			
		||||
		if (pdp && ((packet->gtp0.h.type == GTP_GPDU) ||
 | 
			
		||||
			    (packet->gtp0.h.type == GTP_ERROR)))
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flru);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp0.h.flow = hton16(pdp->flrc);
 | 
			
		||||
		packet->gtp0.h.flow = hton16(flow);
 | 
			
		||||
	} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) {	/* Version 1 with seq */
 | 
			
		||||
		packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
 | 
			
		||||
		packet->gtp1l.h.seq = hton16(seq);
 | 
			
		||||
		if (pdp && (fd == gsn->fd1u))
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teid_gn);
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			packet->gtp1l.h.tei = hton32(pdp->teic_gn);
 | 
			
		||||
		packet->gtp1l.h.tei = hton32(teidc);
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -485,7 +499,7 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sendto(fd, packet, len, 0,
 | 
			
		||||
		   (struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) {
 | 
			
		||||
		   (const struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) {
 | 
			
		||||
		rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SENDTO);
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR,
 | 
			
		||||
			"Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
 | 
			
		||||
@@ -521,6 +535,34 @@ static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send a GTP Response which relates to a PDP Context. See gtp_resp for a more generic function */
 | 
			
		||||
static int gtp_resp_pdp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
	     union gtp_packet *packet, int len,
 | 
			
		||||
	     struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
 | 
			
		||||
	uint16_t flow = 0;
 | 
			
		||||
	uint32_t tei = 0;
 | 
			
		||||
 | 
			
		||||
	if (ver == 0) {	/* Version 0 */
 | 
			
		||||
		if (pdp && ((packet->gtp0.h.type == GTP_GPDU) ||
 | 
			
		||||
			    (packet->gtp0.h.type == GTP_ERROR)))
 | 
			
		||||
			flow = pdp->flru;
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			flow = pdp->flrc;
 | 
			
		||||
	} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) {	/* Version 1 with seq */
 | 
			
		||||
		if (pdp && (fd == gsn->fd1u))
 | 
			
		||||
			tei = pdp->teid_gn;
 | 
			
		||||
		else if (pdp)
 | 
			
		||||
			tei = pdp->teic_gn;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gtp_resp(gsn, packet, len, peer, fd, seq, tid, flow, tei);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gtp_notification(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
		     union gtp_packet *packet, int len,
 | 
			
		||||
		     const struct sockaddr_in *peer, int fd, uint16_t seq)
 | 
			
		||||
@@ -638,19 +680,19 @@ int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send off an echo reply */
 | 
			
		||||
int gtp_echo_resp(struct gsn_t *gsn, int version,
 | 
			
		||||
static int gtp_echo_resp(struct gsn_t *gsn, int version,
 | 
			
		||||
			 struct sockaddr_in *peer, int fd, void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	union gtp_packet packet;
 | 
			
		||||
	unsigned int length = get_default_gtp(version, GTP_ECHO_RSP, &packet);
 | 
			
		||||
	gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY,
 | 
			
		||||
		  gsn->restart_counter);
 | 
			
		||||
	return gtp_resp(version, gsn, NULL, &packet, length, peer, fd,
 | 
			
		||||
	return gtp_resp_pdp(version, gsn, NULL, &packet, length, peer, fd,
 | 
			
		||||
			get_seq(pack), get_tid(pack));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle a received echo request */
 | 
			
		||||
int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
static int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
			int fd, void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@@ -663,7 +705,7 @@ int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle a received echo reply */
 | 
			
		||||
int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
static int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
			 void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	union gtpie_member *ie[GTPIE_SIZE];
 | 
			
		||||
@@ -716,7 +758,7 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
 * only listen to the GTP0 port, and therefore will never receive
 | 
			
		||||
 * anything else than GTP0 */
 | 
			
		||||
 | 
			
		||||
int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
static int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
			 int fd, void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	union gtp_packet packet;
 | 
			
		||||
@@ -727,7 +769,7 @@ int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle a Version Not Supported message */
 | 
			
		||||
int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
 | 
			
		||||
static int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
 | 
			
		||||
			 void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@@ -820,6 +862,40 @@ int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
 | 
			
		||||
	return gtp_notification(gsn, 1, &packet, length, peer, gsn->fd1c, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ***********************************************************
 | 
			
		||||
 * Conversion functions
 | 
			
		||||
 *************************************************************/
 | 
			
		||||
 | 
			
		||||
/* ***********************************************************
 | 
			
		||||
 * IP address conversion functions
 | 
			
		||||
 * There exist several types of address representations:
 | 
			
		||||
 * - eua: End User Address. (29.060, 7.7.27, message type 128)
 | 
			
		||||
 *   Used for signalling address to mobile station. Supports IPv4
 | 
			
		||||
 *   IPv6 x.25 etc. etc.
 | 
			
		||||
 * - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address
 | 
			
		||||
 *   of GSN. If length is 4 it is IPv4. If length is 16 it is IPv6.
 | 
			
		||||
 * - in_addr: IPv4 address struct.
 | 
			
		||||
 * - sockaddr_in: Socket API representation of IP address and
 | 
			
		||||
 *   port number.
 | 
			
		||||
 *************************************************************/
 | 
			
		||||
 | 
			
		||||
int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna)
 | 
			
		||||
{
 | 
			
		||||
	memset(dst, 0, sizeof(struct in_addr));
 | 
			
		||||
	if (gsna->l != 4)
 | 
			
		||||
		return EOF;	/* Return if not IPv4 */
 | 
			
		||||
	memcpy(dst, gsna->v, gsna->l);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src)
 | 
			
		||||
{
 | 
			
		||||
	memset(gsna, 0, sizeof(struct ul16_t));
 | 
			
		||||
	gsna->l = 4;
 | 
			
		||||
	memcpy(gsna->v, src, gsna->l);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ***********************************************************
 | 
			
		||||
 * Session management messages
 | 
			
		||||
 * Messages: create, update and delete PDP context
 | 
			
		||||
@@ -988,21 +1064,8 @@ int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Application response to context indication */
 | 
			
		||||
int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* Now send off a reply to the peer */
 | 
			
		||||
	gtp_create_pdp_resp(gsn, pdp->version, pdp, cause);
 | 
			
		||||
 | 
			
		||||
	if (cause != GTPCAUSE_ACC_REQ)
 | 
			
		||||
		gtp_freepdp(gsn, pdp);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send Create PDP Context Response */
 | 
			
		||||
int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 | 
			
		||||
static int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 | 
			
		||||
			       uint8_t cause)
 | 
			
		||||
{
 | 
			
		||||
	union gtp_packet packet;
 | 
			
		||||
@@ -1011,7 +1074,7 @@ int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 | 
			
		||||
 | 
			
		||||
	gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
 | 
			
		||||
 | 
			
		||||
	if (cause == GTPCAUSE_ACC_REQ) {
 | 
			
		||||
	if (gtp_cause_successful(cause)) {
 | 
			
		||||
 | 
			
		||||
		if (version == 0)
 | 
			
		||||
			gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
 | 
			
		||||
@@ -1060,12 +1123,25 @@ int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 | 
			
		||||
		/* TODO: Charging gateway address */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gtp_resp(version, gsn, pdp, &packet, length, &pdp->sa_peer,
 | 
			
		||||
	return gtp_resp_pdp(version, gsn, pdp, &packet, length, &pdp->sa_peer,
 | 
			
		||||
			pdp->fd, pdp->seq, pdp->tid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Application response to context indication */
 | 
			
		||||
int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* Now send off a reply to the peer */
 | 
			
		||||
	gtp_create_pdp_resp(gsn, pdp->version, pdp, cause);
 | 
			
		||||
 | 
			
		||||
	if (!gtp_cause_successful(cause))
 | 
			
		||||
		gtp_freepdp(gsn, pdp);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Create PDP Context Request */
 | 
			
		||||
int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
 | 
			
		||||
static int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
 | 
			
		||||
			      struct sockaddr_in *peer, int fd,
 | 
			
		||||
			      void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
@@ -1390,7 +1466,7 @@ recover_ret:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Create PDP Context Response */
 | 
			
		||||
int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
static int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
			       struct sockaddr_in *peer, void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	struct pdp_t *pdp;
 | 
			
		||||
@@ -1445,7 +1521,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check all conditional information elements */
 | 
			
		||||
	if (GTPCAUSE_ACC_REQ == cause) {
 | 
			
		||||
	if (gtp_cause_successful(cause)) {
 | 
			
		||||
 | 
			
		||||
		if (version == 0) {
 | 
			
		||||
			if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
 | 
			
		||||
@@ -1649,15 +1725,21 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
 | 
			
		||||
		gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID,
 | 
			
		||||
			  pdp->omcid.l, pdp->omcid.v);
 | 
			
		||||
 | 
			
		||||
	/* Direct Tunnel Flags */
 | 
			
		||||
	if ((pdp->version == 1) && pdp->dir_tun_flags.l)
 | 
			
		||||
		gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_DIR_TUN_FLAGS,
 | 
			
		||||
			  pdp->dir_tun_flags.l, pdp->dir_tun_flags.v);
 | 
			
		||||
 | 
			
		||||
	gtp_req(gsn, pdp->version, pdp, &packet, length, inetaddr, cbp);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Send Update PDP Context Response */
 | 
			
		||||
static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			struct sockaddr_in *peer, int fd,
 | 
			
		||||
			void *pack, unsigned len,
 | 
			
		||||
			uint16_t seq, uint64_t tid,
 | 
			
		||||
			struct pdp_t *pdp, uint8_t cause)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@@ -1667,7 +1749,7 @@ static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
 | 
			
		||||
	gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
 | 
			
		||||
 | 
			
		||||
	if (cause == GTPCAUSE_ACC_REQ) {
 | 
			
		||||
	if (gtp_cause_successful(cause)) {
 | 
			
		||||
 | 
			
		||||
		if (version == 0)
 | 
			
		||||
			gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
 | 
			
		||||
@@ -1710,10 +1792,30 @@ static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
				  pdp->qos_neg.l, pdp->qos_neg.v);
 | 
			
		||||
 | 
			
		||||
		/* TODO: Charging gateway address */
 | 
			
		||||
 | 
			
		||||
		/* Direct Tunnel Flags */
 | 
			
		||||
		if ((gsn->mode == GTP_MODE_SGSN) && (version == 1) && pdp->dir_tun_flags.l)
 | 
			
		||||
			gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_DIR_TUN_FLAGS,
 | 
			
		||||
				pdp->dir_tun_flags.l, pdp->dir_tun_flags.v);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gtp_resp(version, gsn, pdp, &packet, length, peer,
 | 
			
		||||
			fd, get_seq(pack), get_tid(pack));
 | 
			
		||||
	return gtp_resp_pdp(version, gsn, pdp, &packet, length, peer,
 | 
			
		||||
			fd, seq, tid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* API: Application response to context indication */
 | 
			
		||||
int gtp_update_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* Now send off a reply to the peer */
 | 
			
		||||
	gtp_update_pdp_resp(gsn, pdp->version, &pdp->sa_peer,
 | 
			
		||||
			pdp->fd, pdp->seq, pdp->tid, pdp, cause);
 | 
			
		||||
 | 
			
		||||
	if (!gtp_cause_successful(cause))
 | 
			
		||||
		gtp_freepdp(gsn, pdp);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Update PDP Context Request */
 | 
			
		||||
@@ -1721,13 +1823,15 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
		       struct sockaddr_in *peer, int fd,
 | 
			
		||||
		       void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	struct pdp_t *pdp;
 | 
			
		||||
	struct pdp_t *pdp = NULL;
 | 
			
		||||
	struct pdp_t pdp_backup;
 | 
			
		||||
	union gtpie_member *ie[GTPIE_SIZE];
 | 
			
		||||
	uint8_t recovery;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	uint16_t seq = get_seq(pack);
 | 
			
		||||
	int hlen = get_hlen(pack);
 | 
			
		||||
	uint64_t tid = get_tid(pack);
 | 
			
		||||
 | 
			
		||||
	uint64_t imsi;
 | 
			
		||||
	uint8_t nsapi;
 | 
			
		||||
@@ -1745,8 +1849,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
		if (0 == version)
 | 
			
		||||
			return EOF;
 | 
			
		||||
		else
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, NULL,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, NULL,
 | 
			
		||||
						   GTPCAUSE_INVALID_MESSAGE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1761,8 +1865,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
 | 
			
		||||
				   "Unknown PDP context: TID=0x%" PRIx64 "\n",
 | 
			
		||||
				   get_tid(pack));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, NULL,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, NULL,
 | 
			
		||||
						   GTPCAUSE_NON_EXIST);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1774,8 +1878,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, NULL,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, NULL,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1788,7 +1892,7 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
					   "Unknown PDP context: TEI=0x%" PRIx32 "\n",
 | 
			
		||||
					   get_tei(pack));
 | 
			
		||||
				return gtp_update_pdp_resp(gsn, version, peer,
 | 
			
		||||
							   fd, pack, len, NULL,
 | 
			
		||||
							   fd, seq, tid, NULL,
 | 
			
		||||
							   GTPCAUSE_NON_EXIST);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -1799,7 +1903,7 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
					   "Unknown PDP context: IMSI=0x%" PRIx64
 | 
			
		||||
					   " NSAPI=%" PRIu8 "\n", imsi, nsapi);
 | 
			
		||||
				return gtp_update_pdp_resp(gsn, version, peer,
 | 
			
		||||
							   fd, pack, len, NULL,
 | 
			
		||||
							   fd, seq, tid, NULL,
 | 
			
		||||
							   GTPCAUSE_NON_EXIST);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -1808,6 +1912,12 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
		return EOF;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Update internal state to be used when user calls gtp_update_context_resp(): */
 | 
			
		||||
	pdp->seq = seq;
 | 
			
		||||
	pdp->sa_peer = *peer;
 | 
			
		||||
	pdp->fd = fd;
 | 
			
		||||
	pdp->version = version;
 | 
			
		||||
 | 
			
		||||
	/* Make a backup copy in case anything is wrong */
 | 
			
		||||
	memcpy(&pdp_backup, pdp, sizeof(pdp_backup));
 | 
			
		||||
 | 
			
		||||
@@ -1818,8 +1928,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1835,8 +1945,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1845,21 +1955,22 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (version == 1) {
 | 
			
		||||
		/* TEID (mandatory) */
 | 
			
		||||
		if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) {
 | 
			
		||||
		/* TEID (mandatory SGSN->GGSN, Optional SGSN<-GGSN) */
 | 
			
		||||
		if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn) &&
 | 
			
		||||
		    gsn->mode == GTP_MODE_GGSN) {
 | 
			
		||||
			rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1875,8 +1986,8 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1903,45 +2014,60 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
		GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
 | 
			
		||||
			    "Missing mandatory information field\n");
 | 
			
		||||
		memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
		return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
 | 
			
		||||
		return gtp_update_pdp_resp(gsn, version, peer, fd, seq, tid,
 | 
			
		||||
					   pdp, GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* SGSN address for user traffic (mandatory) */
 | 
			
		||||
	/* SGSN address for user traffic (mandatory SGSN->GGSN, optional SGSN<-GGSN) */
 | 
			
		||||
	if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
 | 
			
		||||
			 &pdp->gsnru.v, sizeof(pdp->gsnru.v))) {
 | 
			
		||||
			 &pdp->gsnru.v, sizeof(pdp->gsnru.v)) &&
 | 
			
		||||
	    gsn->mode == GTP_MODE_GGSN) {
 | 
			
		||||
		rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
 | 
			
		||||
		GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
 | 
			
		||||
			    "Missing mandatory information field\n");
 | 
			
		||||
		memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
		return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
 | 
			
		||||
		return gtp_update_pdp_resp(gsn, version, peer, fd, seq, tid,
 | 
			
		||||
					   pdp, GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (version == 1) {
 | 
			
		||||
		/* QoS (mandatory) */
 | 
			
		||||
		/* QoS (mandatory SGSN->GGSN, optional SGSN<-GGSN) */
 | 
			
		||||
		if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_req.l,
 | 
			
		||||
				 &pdp->qos_req.v, sizeof(pdp->qos_req.v))) {
 | 
			
		||||
				 &pdp->qos_req.v, sizeof(pdp->qos_req.v)) &&
 | 
			
		||||
		    gsn->mode == GTP_MODE_GGSN) {
 | 
			
		||||
			rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_MISSING);
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, peer, pack,
 | 
			
		||||
				    len, "Missing mandatory information field\n");
 | 
			
		||||
			memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
 | 
			
		||||
						   len, pdp,
 | 
			
		||||
			return gtp_update_pdp_resp(gsn, version, peer, fd, seq,
 | 
			
		||||
						   tid, pdp,
 | 
			
		||||
						   GTPCAUSE_MAN_IE_MISSING);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* TFT (conditional) */
 | 
			
		||||
		/* TFT (conditional SGSN->GGSN, optional SGSN<-GGSN) */
 | 
			
		||||
		if (gtpie_gettlv(ie, GTPIE_TFT, 0, &pdp->tft.l,
 | 
			
		||||
				 &pdp->tft.v, sizeof(pdp->tft.v))) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* OMC identity */
 | 
			
		||||
 | 
			
		||||
		/* Direct Tunnel Flags */
 | 
			
		||||
		if (gtpie_gettlv(ie, GTPIE_DIR_TUN_FLAGS, 0, &pdp->dir_tun_flags.l,
 | 
			
		||||
				 &pdp->dir_tun_flags.v, sizeof(pdp->dir_tun_flags.v))) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Callback function to validate login */
 | 
			
		||||
	if (gsn->cb_update_context_ind != 0)
 | 
			
		||||
		rc = gsn->cb_update_context_ind(pdp);
 | 
			
		||||
	else {
 | 
			
		||||
		/* Confirm to peer that things were "successful" */
 | 
			
		||||
		rc = gtp_update_pdp_resp(gsn, version, peer, fd, seq, tid, pdp,
 | 
			
		||||
					 GTPCAUSE_ACC_REQ);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Confirm to peer that things were "successful" */
 | 
			
		||||
	return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
 | 
			
		||||
				   GTPCAUSE_ACC_REQ);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Update PDP Context Response */
 | 
			
		||||
@@ -1997,7 +2123,7 @@ static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
 | 
			
		||||
	/* Check all conditional information elements */
 | 
			
		||||
	/* TODO: This does not handle GGSN-initiated update responses */
 | 
			
		||||
	if (cause == GTPCAUSE_ACC_REQ) {
 | 
			
		||||
	if (gtp_cause_successful(cause)) {
 | 
			
		||||
		if (version == 0) {
 | 
			
		||||
			if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
 | 
			
		||||
					 &pdp->qos_neg0,
 | 
			
		||||
@@ -2046,6 +2172,11 @@ static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			     &pdp->qos_neg.v, sizeof(pdp->qos_neg.v))) {
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Direct Tunnel Flags */
 | 
			
		||||
			if (gtpie_gettlv(ie, GTPIE_DIR_TUN_FLAGS, 0, &pdp->dir_tun_flags.l,
 | 
			
		||||
					&pdp->dir_tun_flags.v, sizeof(pdp->dir_tun_flags.v))) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2157,7 +2288,7 @@ int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send Delete PDP Context Response */
 | 
			
		||||
int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
 | 
			
		||||
static 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,
 | 
			
		||||
@@ -2169,10 +2300,10 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
 | 
			
		||||
 | 
			
		||||
	gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
 | 
			
		||||
 | 
			
		||||
	gtp_resp(version, gsn, pdp, &packet, length, peer, fd,
 | 
			
		||||
	gtp_resp_pdp(version, gsn, pdp, &packet, length, peer, fd,
 | 
			
		||||
		 get_seq(pack), get_tid(pack));
 | 
			
		||||
 | 
			
		||||
	if (cause == GTPCAUSE_ACC_REQ) {
 | 
			
		||||
	if (gtp_cause_successful(cause)) {
 | 
			
		||||
		if ((teardown) || (version == 0)) {	/* Remove all contexts */
 | 
			
		||||
			gtp_freepdp_teardown(gsn, linked_pdp);
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -2198,12 +2329,11 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* if (cause == GTPCAUSE_ACC_REQ) */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Delete PDP Context Request */
 | 
			
		||||
int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
 | 
			
		||||
static int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
 | 
			
		||||
			      struct sockaddr_in *peer, int fd,
 | 
			
		||||
			      void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
@@ -2302,7 +2432,7 @@ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle Delete PDP Context Response */
 | 
			
		||||
int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
static int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
			       struct sockaddr_in *peer, void *pack, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	union gtpie_member *ie[GTPIE_SIZE];
 | 
			
		||||
@@ -2350,7 +2480,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check the cause value (again) */
 | 
			
		||||
	if ((GTPCAUSE_ACC_REQ != cause) && (GTPCAUSE_NON_EXIST != cause)) {
 | 
			
		||||
	if (!gtp_cause_successful(cause) && (GTPCAUSE_NON_EXIST != cause)) {
 | 
			
		||||
		rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_UNEXPECTED_CAUSE);
 | 
			
		||||
		GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
 | 
			
		||||
			    "Unexpected cause value received: %d\n", cause);
 | 
			
		||||
@@ -2384,7 +2514,7 @@ static int gtp_error_ind_resp(struct gsn_t *gsn, uint8_t version,
 | 
			
		||||
			  sizeof(gsn->gsnu), &gsn->gsnu);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gtp_resp(version, gsn, NULL, &packet, length, peer, fd,
 | 
			
		||||
	return gtp_resp_pdp(version, gsn, NULL, &packet, length, peer, fd,
 | 
			
		||||
			get_seq(pack), get_tid(pack));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2763,8 +2893,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((gsn->mode == GTP_MODE_GGSN) &&
 | 
			
		||||
		    ((pheader->type == GTP_CREATE_PDP_RSP) ||
 | 
			
		||||
		     (pheader->type == GTP_UPDATE_PDP_RSP))) {
 | 
			
		||||
		    (pheader->type == GTP_CREATE_PDP_RSP)) {
 | 
			
		||||
			rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_UNEXPECT);
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
 | 
			
		||||
				    status,
 | 
			
		||||
@@ -2774,8 +2903,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((gsn->mode == GTP_MODE_SGSN) &&
 | 
			
		||||
		    ((pheader->type == GTP_CREATE_PDP_REQ) ||
 | 
			
		||||
		     (pheader->type == GTP_UPDATE_PDP_REQ))) {
 | 
			
		||||
		    (pheader->type == GTP_CREATE_PDP_REQ)) {
 | 
			
		||||
			rate_ctr_inc2(gsn->ctrg, GSN_CTR_PKT_UNEXPECT);
 | 
			
		||||
			GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
 | 
			
		||||
				    status,
 | 
			
		||||
@@ -3051,61 +3179,6 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ***********************************************************
 | 
			
		||||
 * Conversion functions
 | 
			
		||||
 *************************************************************/
 | 
			
		||||
 | 
			
		||||
/* ***********************************************************
 | 
			
		||||
 * IP address conversion functions
 | 
			
		||||
 * There exist several types of address representations:
 | 
			
		||||
 * - eua: End User Address. (29.060, 7.7.27, message type 128)
 | 
			
		||||
 *   Used for signalling address to mobile station. Supports IPv4
 | 
			
		||||
 *   IPv6 x.25 etc. etc.
 | 
			
		||||
 * - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address
 | 
			
		||||
 *   of GSN. If length is 4 it is IPv4. If length is 16 it is IPv6.
 | 
			
		||||
 * - in_addr: IPv4 address struct.
 | 
			
		||||
 * - sockaddr_in: Socket API representation of IP address and
 | 
			
		||||
 *   port number.
 | 
			
		||||
 *************************************************************/
 | 
			
		||||
 | 
			
		||||
int ipv42eua(struct ul66_t *eua, struct in_addr *src)
 | 
			
		||||
{
 | 
			
		||||
	eua->v[0] = PDP_EUA_ORG_IETF;
 | 
			
		||||
	eua->v[1] = PDP_EUA_TYPE_v4;
 | 
			
		||||
	if (src) {
 | 
			
		||||
		eua->l = 6;
 | 
			
		||||
		memcpy(&eua->v[2], src, 4);
 | 
			
		||||
	} else {
 | 
			
		||||
		eua->l = 2;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int eua2ipv4(struct in_addr *dst, struct ul66_t *eua)
 | 
			
		||||
{
 | 
			
		||||
	if ((eua->l != 6) || (eua->v[0] != PDP_EUA_ORG_IETF) || (eua->v[1] != PDP_EUA_TYPE_v4))
 | 
			
		||||
		return -1;	/* Not IPv4 address */
 | 
			
		||||
	memcpy(dst, &eua->v[2], 4);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna)
 | 
			
		||||
{
 | 
			
		||||
	memset(dst, 0, sizeof(struct in_addr));
 | 
			
		||||
	if (gsna->l != 4)
 | 
			
		||||
		return EOF;	/* Return if not IPv4 */
 | 
			
		||||
	memcpy(dst, gsna->v, gsna->l);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src)
 | 
			
		||||
{
 | 
			
		||||
	memset(gsna, 0, sizeof(struct ul16_t));
 | 
			
		||||
	gsna->l = 4;
 | 
			
		||||
	memcpy(gsna->v, src, gsna->l);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TS 29.060 has yet again a different encoding for IMSIs than
 | 
			
		||||
 * what we have in other places, so we cannot use the gsm48
 | 
			
		||||
 * decoding functions.  Also, libgtp uses an uint64_t in
 | 
			
		||||
@@ -3156,3 +3229,8 @@ uint64_t gtp_imsi_str2gtp(const char *str)
 | 
			
		||||
	}
 | 
			
		||||
	return imsi64;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gtp_set_talloc_ctx(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	tall_libgtp_ctx = ctx;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								gtp/gtp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								gtp/gtp_internal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
 | 
			
		||||
uint64_t gtp_imsi_str2gtp(const char *str);
 | 
			
		||||
 | 
			
		||||
extern TALLOC_CTX *tall_libgtp_ctx;
 | 
			
		||||
							
								
								
									
										190
									
								
								gtp/gtpie.c
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								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
 | 
			
		||||
@@ -770,7 +770,7 @@ 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 */
 | 
			
		||||
@@ -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 *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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
 | 
			
		||||
#include "gtp.h"
 | 
			
		||||
#include <osmocom/gtp/gtp.h>
 | 
			
		||||
 | 
			
		||||
#define QUEUE_DEBUG 0		/* Print debug information */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
							
								
								
									
										8
									
								
								include/osmocom/gtp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								include/osmocom/gtp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
libgtp_HEADERS = \
 | 
			
		||||
	gsn.h \
 | 
			
		||||
	gtp.h \
 | 
			
		||||
	gtpie.h \
 | 
			
		||||
	pdp.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libgtpdir = $(includedir)/osmocom/gtp
 | 
			
		||||
@@ -99,6 +99,7 @@ struct gsn_t {
 | 
			
		||||
	/* Call back functions */
 | 
			
		||||
	int (*cb_delete_context) (struct pdp_t *);
 | 
			
		||||
	int (*cb_create_context_ind) (struct pdp_t *);
 | 
			
		||||
	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);
 | 
			
		||||
@@ -134,6 +135,8 @@ 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));
 | 
			
		||||
@@ -175,7 +178,4 @@ 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");
 | 
			
		||||
 | 
			
		||||
/* Internal APIs: */
 | 
			
		||||
void gtp_queue_timer_start(struct gsn_t *gsn);
 | 
			
		||||
 | 
			
		||||
#endif /* !_GSN_H */
 | 
			
		||||
@@ -53,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 */
 | 
			
		||||
@@ -91,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 */
 | 
			
		||||
@@ -99,21 +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_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 */
 | 
			
		||||
#define GTPCAUSE_131                      131	/* For future use 131-176 */
 | 
			
		||||
#define GTPCAUSE_177                      177	/* Cause values reserved for GPRS charging protocol use (See GTP' In GSM 12.15) 177-191 */
 | 
			
		||||
/* 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 */
 | 
			
		||||
@@ -136,8 +141,8 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -248,6 +253,9 @@ extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
 | 
			
		||||
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");
 | 
			
		||||
@@ -265,61 +273,15 @@ extern int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *p
 | 
			
		||||
extern int gtp_decaps0(struct gsn_t *gsn);
 | 
			
		||||
extern int gtp_decaps1c(struct gsn_t *gsn);
 | 
			
		||||
extern int gtp_decaps1u(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 const char *imsi_gtp2str(const uint64_t *imsi);
 | 
			
		||||
extern uint64_t gtp_imsi_str2gtp(const char *str);
 | 
			
		||||
 | 
			
		||||
/*! Set the talloc context for internal objects */
 | 
			
		||||
void gtp_set_talloc_ctx(void *ctx);
 | 
			
		||||
 | 
			
		||||
#endif /* !_GTP_H */
 | 
			
		||||
@@ -321,5 +321,7 @@ extern int gtpie_decaps(union gtpie_member *ie[], int version,
 | 
			
		||||
extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len);
 | 
			
		||||
extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
 | 
			
		||||
			 void *pack, unsigned *len);
 | 
			
		||||
extern int gtpie_encaps3(union gtpie_member *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 */
 | 
			
		||||
@@ -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 = -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' $(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)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,13 @@
 | 
			
		||||
 | 
			
		||||
#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 "gtp-kernel.h"
 | 
			
		||||
 | 
			
		||||
@@ -104,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);
 | 
			
		||||
@@ -134,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));
 | 
			
		||||
@@ -160,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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								lib/icmpv6.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								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
 | 
			
		||||
 | 
			
		||||
@@ -81,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);
 | 
			
		||||
 | 
			
		||||
@@ -89,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>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										358
									
								
								lib/tun.c
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								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,30 +216,24 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	rc = 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);
 | 
			
		||||
	} else if (rc < len) {
 | 
			
		||||
		LOGTUN(LOGL_ERROR, tun, "short write() %d < %u\n", rc, len);
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								lib/tun.h
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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}/
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,22 @@ bin_PROGRAMS = sgsnemu
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = @EXEC_LDFLAGS@
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' $(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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
@@ -1193,7 +1196,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;
 | 
			
		||||
@@ -1255,7 +1258,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;
 | 
			
		||||
@@ -1322,7 +1325,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;
 | 
			
		||||
@@ -1340,9 +1343,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;
 | 
			
		||||
@@ -1646,7 +1649,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;
 | 
			
		||||
@@ -1783,7 +1786,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);
 | 
			
		||||
@@ -1809,7 +1812,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;
 | 
			
		||||
@@ -1823,8 +1826,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)
 | 
			
		||||
@@ -1880,9 +1883,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)) {
 | 
			
		||||
@@ -1902,7 +1905,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);
 | 
			
		||||
@@ -1922,8 +1926,8 @@ int main(int argc, char **argv)
 | 
			
		||||
#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(tun->devname, "accept_ra", "0") < 0) {
 | 
			
		||||
			SYS_ERR(DSGSN, LOGL_ERROR, 0,
 | 
			
		||||
@@ -1933,7 +1937,7 @@ int main(int argc, char **argv)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
@@ -2156,7 +2160,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);
 | 
			
		||||
@@ -2184,9 +2188,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,4 +1,8 @@
 | 
			
		||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	    -Wall \
 | 
			
		||||
	    -I$(top_srcdir)/include \
 | 
			
		||||
	    $(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	    $(NULL)
 | 
			
		||||
AM_LDFLAGS = -no-install
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,8 @@
 | 
			
		||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	    -Wall \
 | 
			
		||||
	    -I$(top_srcdir)/include \
 | 
			
		||||
	    $(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	    $(NULL)
 | 
			
		||||
AM_LDFLAGS = -no-install
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = ippool_test.ok \
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user