Compare commits

...

72 Commits

Author SHA1 Message Date
Oliver Smith
ba3d8905f9 Bump version: 1.12.0.37-cf65-dirty → 1.13.0
Change-Id: I7dbdd71d62fb13ba2ffc30d7d50fb4207a06340b
2025-02-12 12:09:58 +01:00
Alexander Couzens
cf65292b0d gtp: split gtp_req into 2 parts: transmit and fill header
Split the preparing the data from the socket and queue handling into two functions:
gtp_req(): preparing the data
gtp_req_transmit(): selecting socket, port and adding it to the transmit queue.
In a following commit gtp_req_transmit() can be used by gtpv1 function
as well.

Change-Id: Icda0ef7b0ce3631e23da88827dd54cf019878b5d
2025-01-12 21:03:39 +01:00
Alexander Couzens
71fefdad09 gtpie: add gtp_encaps a modern encapsulation method
No idea why the previous ones started at the second IE.
Further add more length arguments to the encapsulation method.

Change-Id: I8bb086c568e07052c52d880df06049490346e91e
2025-01-12 21:03:27 +01:00
Daniel Willmann
4b8ebcd784 gtp: Rework gtp_resp() into gtp_resp_pdp()
gtp_resp() requires a PDP context. To support more generic messages
without a PDP context, rename gtp_resp() into gtp_resp_pdp() and
create a more generic function to transmit a GTP response.

Will be used by the SGSN Context Req/Resp/Ack code.

Change-Id: Id9ff95e0e2a10a22e65ecf42b2a2b06a0f2d1a45
2025-01-07 13:39:01 +01:00
Alexander Couzens
446dd65de1 gtp_new(): use talloc instead of calloc/free
Required for SGSN Context Req fsm which is using talloc.
Adds a new api to set the libgtp talloc context via gtp_set_talloc_ctx()

Change-Id: I7c4a29c4bb1ef3c7bf506e59e99b3a804cabe34b
2025-01-07 13:35:54 +01:00
Pau Espin Pedrol
09142e1c27 jenkins.sh: Use --disable-doxygen configure param
Change-Id: I0c73c5e5c94bb712975f8ae0c6cff4743e148210
2024-12-10 17:07:54 +01:00
Alexander Couzens
04f5ea4ffa gtp.h: add more GTP cause code from 29.060 v15.3.30
Change-Id: I1571286f01a24adf10243c9551ca81bacc12f8f9
2024-11-25 16:29:08 +01:00
Daniel Willmann
839c2b9c84 gtp: Make peer addr const in gtp_req/gtp_resp
Change-Id: I955b82ba8022754c3d23979d5c55bb61233047b1
2024-11-25 16:29:08 +01:00
Alexander Couzens
65e133b42c gtpie: fix comment
Change-Id: I5aed6a7a2ae4138567944988bc81cebdbf129276
2024-11-25 16:29:08 +01:00
Alexander Couzens
95746b9588 gtp_internal.h: add missing include to <stdint.h>
Change-Id: I4b9a9b582854368b3492cb7438b3acb1a9652f9e
2024-11-25 16:29:08 +01:00
Pau Espin Pedrol
2b161df6af doc: Update all iptables references with nftables
Change-Id: I3caf316e8ccf1d757b83f7a119271084c55e018c
2024-10-25 14:58:52 +00:00
Pau Espin Pedrol
13d23077d2 doc: Document MTU features in User Manual and example config files
Related: OS#6298
Related: SYS#7122
Change-Id: Ib6e974b38107fe48072380f768e1881f0fc95e80
2024-10-25 14:58:52 +00:00
Pau Espin Pedrol
a4cb3eb011 tun: Use OSMO_STRLCPY_ARRAY
tun->devname buffer is already IFNAMSIZ bytes long, so simplify the
code.

Change-Id: I40b370f4152748d5acf6ee576462e54c9a46a8a3
2024-10-25 16:28:43 +02:00
Pau Espin Pedrol
9f1fd42148 Refactor tun_t fields and alloc APIs
The previous state of code made allocation code more complex for no good
reason; use usual alloc() type function instead, splitting between the 2
types of tun_t implementations available to make it easier to
understand.

Move the several tun-iface specific mode fields to a substruct to make
it clear those are only really containing useful information when the
tun_t is created in tun-iface mode.

Change-Id: Ic71f91c62cd5bd48c6d35534eaac2091e4c69735
2024-10-24 15:49:39 +02:00
Pau Espin Pedrol
ad03073219 Rename tun_encaps -> tun_inject_pkt
The terminology of "encapsulating" used in tun_encaps is confusing,
since in this case we are not really encapsulating anything, but
actually delivering ("injecting") the packet to the system network
stack.
Using the "encapsulating" term is also confusing because readers may
think it may be doing the GTP-U encapsulation, which is not.

Change-Id: Ibb17c792b81668571e69d79918f3adf0e9e0f6c8
2024-10-23 18:45:16 +02:00
Pau Espin Pedrol
234cd12ea5 tun.h: Remove non-existent tun_decaps()
Change-Id: If9cc5d2d54b87e38feefa55cb9410715fa9bd11f
2024-10-23 18:44:05 +02:00
Pau Espin Pedrol
d73801ebea ggsn: Rename confusing functions
It's really confusing having a function operating on a rx path called
"encaps" since it's not really encapsulating anything.
This confuses the reader by thinking this is the function encapsulating
packets into GTP-U.

Change-Id: I7ff54f5e57ccc2c436becd1be1b0f728cbcdb12c
2024-10-23 18:42:43 +02:00
Pau Espin Pedrol
1d7e86ae48 ggsn: Avoid forwarding IPv6 solicited-node multicast addr to tun device
There's no need to forward those. Morevoer, they cannot be forwarded
when using gtpu kernel mode since it doesn't support reinjecting packets
we got from it, so avoid erroring out when trying to inject them later
on.

Related: OS#6600
Related: OS#6382
Change-Id: Iac8f14083620d86f3872aa951930fbe8f680ff24
2024-10-23 18:20:57 +02:00
Pau Espin Pedrol
56a6e025d3 tun: Fix null pointer derefence when in kernel gtp mode
When using gtp kernel module, the kernel sends GTP-U packets to
userspace (osmo-ggsn) when it is unable to find a related pdp ctx for
the packet.
This is so far processes through the code path:
ggsn_gtp_fd_cb => gtp_decaps1u => gtp_gpdu_ind => encaps_tun = > tun_encaps

In usual circumstances, if the gtp module sends a packet to userspace
because it is unable to find a pdp ctx, userspace shouldn't be able
anyway, so it should go through a different code path and answer over
GTP-U with a "Error Indication".
Other specific packets (such as ICMPv6 with link local address) are also
being forwarded to userspace. Some of them are being handled in
encaps_tun() in a special way (eg. Router Soliciation), but others, such
as Neighbor Solicitation, are not being handled there and follow the
generic path where they try to be forwarded over the tun towards the
Internet.

When using the kernel gtp mode, there's no way to re-inject into the
network stack a packet we received from the gtp mode, like done when
using the tun device mode.

Prior to 38b607ece3, a bug existed in
tun_encaps() which would still try to use tun->fd (-1) when in gtp
kernel module, which ended up in an error being printed.
After the mentioned commit was applied, when in gtp kernel module it
started accessing tun->tundev which is is NULL under that setup, hence
making the bug consequences worse.

Add a pointer guard with a log line to inform about the problem, while
still discussing the originating problem in OS#6600.

Related: OS#6600
Change-Id: I508758696a0bcbb7c780a0ed33b28ba640602488
2024-10-23 15:39:47 +02:00
Pau Espin Pedrol
c075d032b9 doc: Reorder some chapters
Move general/generic osmocom chapters (logging, vty) further to the end,
so that the osmo-ggsn specific chapters stay together and don't get lost
in tons of generic content.

Change-Id: I6441944cc365bae40ab0f3cc4f1ecd1ac790c5c5
2024-10-22 19:45:18 +02:00
Pau Espin Pedrol
d44bf6fd73 doc: Fix typo: wrong interface named
Change-Id: Ib7ef619c74bf0e6453f0d5ba509e7727bf90f838
2024-10-22 19:45:18 +02:00
Pau Espin Pedrol
77d4ae0470 doc: Remove reference to non longer existing osmo-ggsn.init
The init file was removed at some point in the past.
Nowadays only a systemd service is provided.

Change-Id: I47f889a223bfaf7bd4848898211a3cc62df5e08c
2024-10-22 19:45:18 +02:00
Pau Espin Pedrol
e6feda5185 doc: Fix typo in user manual
Change-Id: I9b0024cfdb748febcecf01a097125103bc0f39c1
2024-10-22 19:45:18 +02:00
Pau Espin Pedrol
f92d875119 ggsn: apply configured APN MTU to tun
Related: OS#6298
Related: SYS#7122
Change-Id: Ifae556169d895860812c9ea5633292d7e3fab338
2024-10-22 19:45:18 +02:00
Pau Espin Pedrol
454110a007 ggsn: Use osmo_netdev_addaddr() libosmocore API
This allows dropping a lot of duplicated code.
While at it, drop references to non-linux systems, which are not
maintained anymore.
Param "dstaddr" was not used anywhere in the old APIs, so it can also be
dropped.
Param "tun->routes" was also used in BSD code, so it can also be
dropped.

Change-Id: I1fccfd658542481cd61536fbd3c7a3a32a1c253b
2024-10-22 19:45:06 +02:00
Pau Espin Pedrol
38b607ece3 ggsn: use libosmocore tundev API to create apn tun device
This way we can start dropping old osmo-ggsn specific API, avoiding
duplication of code.
Moreover, the osmo-ggsn code is using older ioctl APIs, which are
discouraged nowadays in favour of netlink, which osmo_tundev/osmo_netdev
from libosmocore is used.
Even better, we win for free non-blocking write behavior in the tundev
when switching to the new API, since it already has its own internal
wqueue.

While doing this, BSD code is dropped since anyway it's not been
maintained for a long time.
If needed, the BSD support can be added to libosmocore
osmo_tundev/osmo_netdev API.

This is a first step (already working). Follow-up commits will replace
the APIs to set up routes and addresses, and later on osmo-ggsn will win
support to set MTU on the interface.

Furthermore, this will allow easily adding netns support to osmo-ggsn
later on if ever needed.

Depends: libosmocore.git Change-Id Ia8a7e7ec6d96c7aebc80528236a0e0d035e7f38d
Change-Id: I4d99ba147ac0f3b414d2efef0068b6b8d6cf0014
2024-10-21 18:45:32 +02:00
Pau Espin Pedrol
41bec9529f ggsn: Support announcing APN MTU over ICMPv6 RA
Related: OS#6298
Related: SYS#7122
Change-Id: I8bb67915dd5f39ad9ffb80e5aaf9af1e7d70c96c
2024-10-21 18:45:32 +02:00
Pau Espin Pedrol
6041554cef ggsn: Support announcing APN MTU over PCO
This is only useful for IPv4 (or IPv4v6) APNs.
IPv6 APNs obtain this information from SLAAC RA.

Related: OS#6298
Related: SYS#7122
Change-Id: I8532acfffadda9e83962b30e4f6b17eb8b3362ac
2024-10-21 18:45:32 +02:00
Pau Espin Pedrol
6d1d181403 vty: Fix missing newline in description of 'apn tun-device' cmd
Change-Id: I92002acdb83455eb6becc688c33192d3856b0aa6
2024-10-21 18:45:24 +02:00
Pau Espin Pedrol
7d80216717 Move g_ggsn_list declaration to ggsn.c
The llist belongs to the data domain, not the vty.

Change-Id: I0a93aacbdecc283dd4f6b32892430ebdb0b94e50
2024-10-17 18:58:10 +02:00
Pau Espin Pedrol
de385e0253 Move ggsn allocation code out of vty file
The details of creation of data structures don't belong on the vty file.
While at it, split allocation code into its own function to quickly find
out where the allocation of the object happens.

Change-Id: Iaa4cd86c44957321c238d268ea62daafa1b79c69
2024-10-17 18:56:55 +02:00
Pau Espin Pedrol
ce691d8e64 Move apn allocation code out of vty file
The details of creation of data structures don't belong on the vty file.
While at it, split allocation code into its own function to quickly find
out where the allocation of the object happens.

Change-Id: If15a4158dd6599341017efd24dbf67ca565a7c34
2024-10-17 18:51:39 +02:00
Pau Espin Pedrol
8c015bd89a ggsn: kernel gtpu: Support updating pdp ctx remote IP address and TEID
Whenever the SGSN sends the GGSN a UpdatePDPCtxReq, it may offer a new
remote IP address or/and remote TEID, eg. because it wants to establish
a Direct Tunnel and point the GTPU towards the RNC, or to point it back
to itself.
If the gtpu kernel is used, osmo-ggsn lacked updating the kernel with
the new remote data.
The gtp kernel module doesn't provide an efficient/explicit way to
update a pdp context keeping the4 same local IP+TEID and changing only
the remote remote IP+TEID, hence first destroy the pdp ctx in the gtp
kernel module and then recreate it.

This fixes test GGSN_Tests_v4_only.TC_pdp4_act_update_teid in
ttcn3-ggsn-test-kernel-net-next.

Related: OS#6523
Change-Id: I1fc48be5c0f177ccf6fbe97c003b4df44809c0fe
2024-08-19 11:57:23 +02:00
Pau Espin Pedrol
5f8960b206 ggsn: Mark internal cb function static
Change-Id: Ic9fdd50ae098058cc69e5aa6a0085819d69e3599
2024-08-01 17:45:49 +02:00
Pau Espin Pedrol
0828b2afc3 gtp: Allow setting callback to receive update_context_ind
This will be used by:
* SGSN: Get to know that RNC has gonne down according to GGSN (re-attempt
  Direct Tunnel or go back to tun SGSN<->GGSN).
* GGSN: Maybe find out that Direct Flags are used (should be handled
  internally directly in the rx path probably)

Related: OS#6512
Change-Id: Ic80a9a928c55b6ff85be96014920bb42793cb943
2024-07-31 17:23:59 +02:00
Pau Espin Pedrol
70a1d64e55 gtp: Allow UpdatePDPContext initiated by GGSN
The Update PDP Context procedure can be initiated GGSN -> SGSN, as
described in TS 29.060 7.3.3.

Related: OS#6512
Change-Id: I1c3441c71b90c5bbf6f4545484586222e6180fe1
2024-07-31 17:20:58 +02:00
Pau Espin Pedrol
ea3cf1b8ae gtp: Store rx Direct Tunnel Flags in UpdatePDPCtx{Req,Resp}
In Update PDP Ctx Response, only SGSN is expected to transmit Direct
Tunnel Flags in the message.

Related: SYS#5435
Change-Id: Ia3e360a35d30858eab1e438dc2508fd756c2e22e
2024-07-31 17:20:58 +02:00
Pau Espin Pedrol
7ce14f5e93 gtp: Allow tx Direct Tunnel Flags in UpdatePDPCtx{Req,Resp}
In Update PDP Ctx Response, only SGSN is expected to transmit Direct
Tunnel Flags in the message.

Related: SYS#5435
Change-Id: I36d93619e2fe9cafd3092515df18b82d29099d2d
2024-07-31 17:20:54 +02:00
Oliver Smith
561a9bc77c Bump version: 1.11.0.31-7ae1-dirty → 1.12.0
Change-Id: Idf8b178290cbb14e5199590feb584c82dc076dd0
2024-07-24 16:22:26 +02:00
Vadim Yanitskiy
7ae1177e04 README.md: cosmetic: fix a typo
Change-Id: Ia04c62a682cfeca2b054c8fb1ff9fff2aab2860d
2024-06-05 18:34:07 +07:00
Oliver Smith
57585767dc {contrib,debian}/osmo-ggsn.init: remove
Remove SysV init scripts. These are not really maintained anymore and
this makes it consistent with other Osmocom projects.

Avoids synchronizing with SysV scripts on debian:
  # systemctl enable osmo-ggsn
  Synchronizing state of osmo-ggsn.service with SysV service script with /lib/systemd/systemd-sysv-install.
  Executing: /lib/systemd/systemd-sysv-install enable osmo-ggsn

Change-Id: I11bfb5122344b54970b7f742470cb74b95fa37e0
2024-05-16 09:00:21 +00:00
Oliver Smith
488972b442 Use uniform log format for default config files
Related: OS#6272
Change-Id: I272767e029e95b64f2525d4f19efdfa1f0e29ca2
2024-05-16 09:00:21 +00:00
Oliver Smith
08239ccac3 contrib/systemd: run as osmocom user
Related: OS#4107
Change-Id: I915f2fc12d0bd905d24636aacb2760a6b72a55e3
2024-05-16 09:00:21 +00:00
Oliver Smith
fbc56063c5 doc: set state-dir to /var/lib/osmocom/osmo-ggsn
Prepare to run osmo-ggsn as user with the systemd service. As with other
Osmocom service files, we will set StateDirectory= and WorkingDirectory=
options. This results in osmo-ggsn only being able to write to
/var/lib/osmocom, therefore let's change the state-dir from /tmp to
/var/lib/osmocom/osmo-ggsn to avoid:

  gsn.c:411 fopen(path=/tmp/gsn_restart, mode=w) failed: Error = Permission denied

Having the state in /var/lib/osmocom also makes more sense, because then
it doesn't get deleted on reboot.

Change-Id: I5b51529b4f8bd2462e54f58a1ce2e2d7c76ff46a
2024-05-16 09:00:21 +00:00
Oliver Smith
c1598e0eb4 ggsn/ggsn_vty: create state-dir
Prepare to change the state-dir in the default config in a follow-up
commit. Create the directory if it does not exist.

Change-Id: I91349fb284336a9de6af41475f1b824eb0e021b0
2024-05-16 09:00:21 +00:00
Oliver Smith
519a2e401d gtp/gtp_internal.h: new file
While at it, move internal functions of gtp.h to a separate file too.
Make all functions that are only used inside gtp.c static.

The following APIs are unexpectedly public:
* imsi_gtp2str gets used by pdp.h
* gsna2in_addr, gtp_echo_req gets used by osmo-sgsn

Change-Id: I72c40cbdec33449ca8104fb3cad8df1a9e07dfd7
2024-05-14 11:13:45 +02:00
Oliver Smith
fbef527222 gtp: move conversion functions up
Move the conversion functions above the first user, so we can make
in_addr2gsna static in a follow-up commit and remove the extra
declaration.

Change-Id: I51e6a7c1161320fc54b0e8197ae57e4327976eb1
2024-05-14 11:13:45 +02:00
Oliver Smith
68f5b086ad gtp: remove unused conversion functions
Remove ipv42eua and eua2ipv4, which are in the "internal functions"
section of gtp.h, but are not used anywhere in the code anymore. This is
in preparation of moving the internal functions that are used in
multiple .c files into a separate header file, and to make the other
internal functions static. (Compiler complains about unused static
functions.)

Change-Id: I90e2750f6a6e3e6122e9c562103fda77d7326932
2024-05-14 11:13:41 +02:00
Oliver Smith
3cb3423a59 gtp/gtp.c: move gtp_create_context_resp down
Move gtp_create_context_resp below gtp_create_pdp_resp, which it calls.
In a follow-up commit, we can make gtp_create_pdp_resp static and
remove the additional declaration.

Change-Id: I34efe7592013a8423f4f280758272d81f24b65fa
2024-05-13 15:16:58 +02:00
Oliver Smith
bad5eeba0f gtp/gsn_internal.h: new file
Change-Id: I999462e39411fc4ec7e50bd0212e870006fbc4f1
2024-05-13 15:16:58 +02:00
Oliver Smith
1dd16fa12f libgtp: move includes to osmocom/include/gtp
Move all includes from /usr/include/….h to
/usr/include/osmocom/gtp/….h to be more consistent with other Osmocom
projects, and to not "pollute" the top include directory if we add more
header files.

Also the new directory structure makes more obvious, which headers are
public and which ones aren't.

Adjust libgtp.pc.in so both #include <gtp.h> (legacy)
and #include <osmocom/gtp/gtp.h> can be used.

Related: OS#6373
Change-Id: If7e01c61168819bf7120667344e40c857da5490b
2024-05-13 15:16:58 +02:00
Oliver Smith
3372625ad9 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I7cfe55fa2fda43da4eaa1e1b8d40b31d1c8aaf30
2024-05-08 14:40:58 +02:00
Harald Welte
1f9cc2674f README.md: Major overhaul
A lot of the existing contents was still from the old openggsn days, and
predated osmo-ggsn.

Change-Id: I6ec92260da0f55e9493a15db2e8178e5436143a0
2024-03-23 16:32:45 +01:00
Harald Welte
4abe361f33 README.md: Add Forum + Issue Tracker sections
Change-Id: I7b03a78178de77ebf733587ed178fd48c019663c
2024-03-23 16:15:42 +01:00
Harald Welte
bb0655d5aa README.md: Improve markdown formatting
Change-Id: I4e5d99384978d22e6ba0310e97e93d87b610a174
2024-03-23 16:11:50 +01:00
Harald Welte
4e6fe42731 Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: I75ba8c4f8635d5a2d36d5bb97566d737e27bcec9
2024-03-23 16:06:28 +01:00
Oliver Smith
19a506b705 Add clear error for kernel not supporting IPv6
Make it clear to the user, that if adding a tunnel fails with kernel GTP
and IPv6: the reason is that the kernel doesn't support IPv6 yet.

Related: OS#6096
Change-Id: I1d3c8cbb51212c91136292347dad9529a5c58a31
2024-03-04 11:15:07 +01:00
Oliver Smith
ea6c02ac1f doc: fix typo ndoe -> node
Change-Id: Ib78b5de45b93a7534163de2cd91211e9be75445d
2024-03-01 08:08:43 +00:00
Oliver Smith
ec357c5377 lib/gtp-kernel.c: check rc of in46a_from_eua
Fixes: b17fe7bf ("kernel-gtp: support IPv6 on inner layer")
Change-Id: I40e4de1517de8871224a45c173208810b42312ff
2024-02-27 16:04:42 +01:00
Oliver Smith
768d6d5be9 lib/gtp-kernel.c: initialize ret with 0
Fix -Werror=maybe-uninitialize found in Pau's build env:

/home/pespin/dev/sysmocom/git/osmo-ggsn/lib/gtp-kernel.c: In function ‘gtp_kernel_tunnel_add’:
/home/pespin/dev/sysmocom/git/osmo-ggsn/lib/gtp-kernel.c:111:13: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialize]
  111 |         int ret;
      |             ^~~
/home/pespin/dev/sysmocom/git/osmo-ggsn/lib/gtp-kernel.c: In function ‘gtp_kernel_tunnel_del’:
/home/pespin/dev/sysmocom/git/osmo-ggsn/lib/gtp-kernel.c:167:13: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialize]
  167 |         int ret;
      |             ^~~

Fixes: b17fe7bf ("kernel-gtp: support IPv6 on inner layer")
Change-Id: I19067ebe561d4c067b9ace7f5b201e15af6b342e
2024-02-27 15:00:59 +01:00
Oliver Smith
fa91a10498 Cosmetic: {lib,gtp}/Makefile.am: diff friendly
Change-Id: Ib1956794edc6e82cfa6c5419b2609565674d98a4
2024-02-23 13:25:32 +01:00
Oliver Smith
6929391ecf Cosmetic: AM_CFLAGS: make diff friendly
Change-Id: I3303cd8ba8c8b21ff267608833d9fb4833ffc471
2024-02-23 12:08:48 +01:00
Oliver Smith
9baac03927 Cosmetic: Makefile.am: make SUBDIRS diff friendly
Change-Id: I49e1b08e48dc324f313228e8c69679c37aa774f4
2024-02-23 12:08:48 +01:00
Oliver Smith
9bd2711f39 Revert "kernel-gtp: support IPv6 on outer layer"
This reverts commit 0917ce4e22,
as it breaks building osmo-sgsn. This needs to be reworked to be
backwards compatible.

Related: OS#6373
Change-Id: I2c2b2ff0875217e041d94c8e2cef030d2a86c2d8
2024-02-22 16:06:33 +01:00
Oliver Smith
b17fe7bfe9 kernel-gtp: support IPv6 on inner layer
Related: OS#6096
Change-Id: I3df47b6c209f1e2f8254ba139581d6e622c6b35f
2024-02-21 16:22:24 +01:00
Oliver Smith
0917ce4e22 kernel-gtp: support IPv6 on outer layer
Related: OS#1953, OS#6096
Change-Id: I257fff1dcd9d030a7f9ea936b2693a3f13208230
2024-02-21 16:22:21 +01:00
Oliver Smith
2a0d37cb1d gtp_new: deduplicate create_and_bind_socket code
Change-Id: Iff3cfdfb0c08033d869c51499754b3416c71732b
2024-02-21 13:06:04 +01:00
Oliver Smith
f3d541e353 Fix a typo
Change-Id: I508274a1a466651025c488ad897aeed739e4b799
2024-02-21 13:05:56 +01:00
Pau Espin Pedrol
8d976444b8 pco: Improve IPCP spec reference documentation
Change-Id: I1dd4a41bae491c61197e8e307efcfc8c63945a71
2024-01-26 16:12:08 +01:00
Daniel Willmann
77734ac81b libgtp: Remove defines for reserved causes in gtp.h
As discussed in Gerrit change I9c3bf64537ef2223e29f8082861fa32fde26bf68
remove defines that don't serve any purpose. These are defines for
reserved values and changing them later if a newer spec defined them
would break API.

Keep the comments to explain the missing values.

Change-Id: I8db0aa0ade59785443a407b51dea326144406dcf
2023-11-29 16:40:49 +01:00
Oliver Smith
848ec697e2 Bump version: 1.10.2.1-a625 → 1.11.0
Change-Id: I1f116e1cded135f231f22ebc9b817aebf3736fc2
2023-11-28 13:07:17 +00:00
Daniel Willmann
6a2e82542d libgtp: Use gtp_cause_successful() instead of GTPCAUSE_ACC_REQ
In some cases the phone requests a PDP context type that isn't available
no the PGW/GGSN, e.g. phone requests a combined IPv4/v6 context, but
only IPv4 is supported.

In that case the GGSN can send a Create PDP Context Response with cause
"New PDP type due to network preference" or "New PDP type due to single
address bearer only". libgtp should continue handling these cause values
like the "Request Accepted" cause. Use the new gtp_cause_successful()
function for that.

Related: OS#6268
Change-Id: I7dd1e0aa185530e1e2d0402742df833c61a787a7
2023-11-24 09:48:22 +01:00
66 changed files with 1783 additions and 1855 deletions

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

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

View File

@@ -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)

162
README.md
View File

@@ -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
@@ -242,22 +224,22 @@ external network routing needs to be set up. If private addresses are
used you need to configure network address translation. See the Linux
Networking HOWTO for details.
Remember to enable routing:
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
```

View File

@@ -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
#library what description / commit summary line

View File

@@ -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

View File

@@ -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]")

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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
View File

@@ -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
View 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
View File

@@ -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 -- \

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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)

View File

@@ -9,7 +9,7 @@ GGSN (Gateway GPRS support node) element in side the packet switched
core network of 2G and 3G cellular networks.
The GGSN function is the tunnel endpoint on the core network side,
from where the external (IP) packet data network
from where the external (IP) packet data network
=== Software Components
@@ -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

View File

@@ -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.

View File

@@ -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[]

View File

@@ -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)

View File

@@ -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;
}
/* 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);
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, 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);

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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));

View File

@@ -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 {

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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
View File

@@ -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
View File

@@ -0,0 +1,3 @@
#pragma once
void gtp_queue_timer_start(struct gsn_t *gsn);

462
gtp/gtp.c
View File

@@ -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,
union gtp_packet *packet, int len,
struct in_addr *inetaddr, void *cbp)
static int gtp_req_transmit(struct gsn_t *gsn, uint8_t version, const struct in_addr *inetaddr,
union gtp_packet *packet, int len,
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,20 +680,20 @@ 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,
struct sockaddr_in *peer, int fd, void *pack, unsigned len)
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,
int fd, void *pack, unsigned len)
static int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
int fd, void *pack, unsigned len)
{
/* Check if it was a duplicate request */
@@ -663,8 +705,8 @@ 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,
void *pack, unsigned len)
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];
unsigned char recovery;
@@ -716,8 +758,8 @@ 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,
int fd, void *pack, unsigned len)
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,8 +769,8 @@ 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,
void *pack, unsigned len)
static int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
void *pack, unsigned len)
{
if (gsn->cb_unsup_ind)
@@ -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,22 +1064,9 @@ 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,
uint8_t cause)
static int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
uint8_t cause)
{
union gtp_packet packet;
unsigned int length =
@@ -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,14 +1123,27 @@ 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,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
static int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
struct pdp_t *pdp, *pdp_old;
struct pdp_t pdp_buf;
@@ -1390,8 +1466,8 @@ recover_ret:
}
/* Handle Create PDP Context Response */
int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
struct sockaddr_in *peer, void *pack, unsigned len)
static int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
struct sockaddr_in *peer, void *pack, unsigned len)
{
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
@@ -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,11 +2288,11 @@ 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,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, struct pdp_t *linked_pdp,
uint8_t cause, int teardown)
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,
uint8_t cause, int teardown)
{
union gtp_packet packet;
unsigned int length =
@@ -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,14 +2329,13 @@ 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,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
static int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
struct pdp_t *pdp = NULL;
struct pdp_t *linked_pdp = NULL;
@@ -2302,8 +2432,8 @@ 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,
struct sockaddr_in *peer, void *pack, unsigned len)
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];
uint8_t cause;
@@ -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
View 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;

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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 */

View File

@@ -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
View File

@@ -0,0 +1,3 @@
SUBDIRS = \
osmocom \
$(NULL)

View File

@@ -0,0 +1,3 @@
SUBDIRS = \
gtp \
$(NULL)

View File

@@ -0,0 +1,8 @@
libgtp_HEADERS = \
gsn.h \
gtp.h \
gtpie.h \
pdp.h \
$(NULL)
libgtpdir = $(includedir)/osmocom/gtp

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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,61 +105,97 @@ 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);
t = gtp_tunnel_alloc();
if (t == NULL)
in46a_from_gsna(&pdp->gsnrc, &sgsn);
ms_addr_count = in46a_from_eua(&pdp->eua, ms);
if (ms_addr_count < 0)
return -1;
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
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_version(t, pdp->version);
gtp_tunnel_set_ms_ip4(t, &ms);
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
/* use the TEI advertised by SGSN when sending packets
* towards the SGSN */
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
gtp_tunnel_set_version(t, pdp->version);
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);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
/* use the TEI advertised by SGSN when sending packets
* towards the SGSN */
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
}
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
if (ret != 0)
break;
}
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
return ret;
}
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
{
int ms_addr_count;
struct in46_addr ms[2];
struct gtp_tunnel *t;
int ret;
int ret = 0;
pdp_debug(__func__, devname, pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
ms_addr_count = in46a_from_eua(&pdp->eua, ms);
if (ms_addr_count < 0)
return -1;
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
gtp_tunnel_set_version(t, pdp->version);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
}
for (int i = 0; i < ms_addr_count; i++) {
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
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));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
}
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
if (ret != 0)
break;
}
return ret;
}

View File

@@ -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;
}

View File

@@ -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];

View File

@@ -10,7 +10,7 @@
*/
#include "../lib/in46_addr.h"
#include "../gtp/pdp.h"
#include <osmocom/gtp/pdp.h>
#include <osmocom/core/utils.h>

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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;

View File

@@ -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
View File

@@ -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");
goto err_free;
}
OSMO_STRLCPY_ARRAY(tun->devname, devname);
/* 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;
}
return tun;
}
strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
struct tun_t *tun_alloc_tundev(const char *devname)
{
struct tun_t *tun;
int rc;
/* Disable checksums */
if (ioctl((*tun)->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;
tun = tun_alloc_common(devname);
if (!tun)
return NULL;
if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
strerror(errno));
return -1;
}
LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
return 0;
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;
/* 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->tundev.fd, TUNSETNOCSUM, 1) < 0) {
SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", tun->devname);
}
#elif defined(__FreeBSD__) || defined (__APPLE__)
LOGP(DTUN, LOGL_NOTICE, "tun %s configured\n", tun->devname);
return tun;
if (use_kernel) {
LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
return -1;
}
err_free_tundev:
osmo_tundev_free(tun->tundev.tundev);
err_free:
talloc_free(tun);
return NULL;
}
/* 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");
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));
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->fd >= 0) {
if (close(tun->fd)) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
if (tun->tundev.tundev) {
if (osmo_tundev_close(tun->tundev.tundev) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "osmo_tundev_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;
}
gtp_kernel_stop(tun->devname);
/* 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;
}

View File

@@ -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,

View File

@@ -9,7 +9,7 @@
*
*/
#include "../gtp/pdp.h"
#include <osmocom/gtp/pdp.h>
#include "ippool.h"
#include "in46_addr.h"

View File

@@ -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}/

View File

@@ -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

View File

@@ -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))

View File

@@ -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 = \

View File

@@ -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];

View File

@@ -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 \