Compare commits

...

179 Commits

Author SHA1 Message Date
Daniel Willmann
24d216c683 New version for On-Waves 2018-08-31 18:03:46 +02:00
Daniel Willmann
8e97fccc34 Merge remote-tracking branch 'origin/master' into daniel/onwaves 2018-08-31 18:03:16 +02:00
Harald Welte
ee44b82b96 debian/rules: Don't overwrite .tarball-version
The .tarball-version file should contain the *source version* uniquely
identifying the git commit, and not the Debian package name.

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

Change-Id: I1466936033c2f60edd1078eb41f3508d87da4402
Related: OS#3449
2018-08-06 11:15:00 +02:00
Pau Espin Pedrol
b5f93346df gtp: Add new replacement cb_recovery2 for cb_recovery
Sometimes the originating pdp ctx causing the Recovery Procedure is
required, in order to drop all pdp ctx but this one, which specs specify
should be handled as valid:
"""
The SGSN receiving the Recovery information element shall handle it as when an
Echo Response message is received but shall consider the PDP context being created as active if the response indicates
successful context activation at the GGSN.
"""

Change-Id: I53e92298f2f6b84d662a3300d922e8c2ccb178bc
2018-07-23 11:25:53 +02:00
Pau Espin Pedrol
8e8c7ef3c7 gtp: Add new API to avoid freeing pdp contexts during DEL CTX REQ
With this API, user is expectd to free the PDP ctx when the confirmation
for the release has been received (cb_conf time). This way user can
maintain the pdp ctx alive during all this time. Extra code is added to
gtp_delete_pdp_resp() since it's now possible to match it and push it up
to the user cb_conf.

This way, cb_conf() can be used for locally-initiated DEL CTX REQ, while
delete_context() cb is left for remotely-initiated DEL CTX REQ. In this
later case, when the DEL CTX RESP is sent the ctx is deleted and the
delete_context() is called, where the user can do related actions or
trigger consequence events (in the case of SGSN, it will drop all
related GGSN bits for that PDP ctx and forward the DEACT PDP CTX to the
MS).

Change-Id: I29d366253bb98dcba328c7ce8aa3e4daf8f75e6c
2018-07-21 17:22:54 +00:00
Stefan Sperling
57238889eb fix support for multiple IPCP in PDP protocol configuration options
Parse multiple IPCP IEs embedded in Protocol Configuration Options,
and return IPCP responses for all of them. Makes the associated
TTCN3 GGSN test pass.

Depends: Ia1410abb216831864042f95679330f4508e1af3d
Change-Id: I51ecab4e35f3ee638e68ca773b0da90cc0294ab0
Related: OS#3319
2018-07-19 19:45:01 +02:00
Stefan Sperling
d70ab97fa4 fix unaligned access in build_ipcp_pco()
IPCP data can begin at any byte location in the pco_req->v array.
Casting to a 'struct ipcp_hdr' pointer could lead to unaligned access.
Parse IPCP data with u_int8_t pointers instead to avoid this problem.

Add some length checks while here.
pco_contains_proto() and ipcp_contains_option() now receive the minimum
size of the data the caller is looking for, and only return pointers
to items of sufficient size.

Also fix an inifinite loop in ipcp_contains_option() by refusing
IPCP options with length small than 2. Previously, a zero length
option would trigger an infinite loop in the parser.

Change-Id: Ia1410abb216831864042f95679330f4508e1af3d
Related: OS#3194
2018-07-19 19:37:41 +02:00
Pau Espin Pedrol
d1bd6fce9c gtp: Log ignore CTX DEL REQ due to no teardown and only 1 ctx active
Change-Id: Ic950c04d309d5686bfbeab332f79c48678e743ae
2018-07-13 19:11:59 +02:00
Pau Espin Pedrol
a32e4c4fb8 gtp: Allow recv DEL CTX REQ in sgsn and DEL CTX RSP in ggsn
According to 3GPP TS 29.060 section "7.3.5
Delete PDP Context Request", both directions are valid in both GSNs.

This allows osmo-sgsn receive delete ctx indication (cb_delete_context)
in order to implement GGSN initiated requests.

Change-Id: I6927c07be4ddf74defe338d01d947056e15cd14d
2018-07-13 19:05:03 +02:00
Pau Espin Pedrol
3b84e92ab3 gtp: Log type name of unexpected signalling message
Change-Id: Iae0f045e4128cf97aa7824d7d774b59bf966cbe8
2018-07-13 18:32:38 +02:00
Daniel Willmann
835496d7dd Release new version 2018-06-26 19:12:29 +02:00
Pau Espin Pedrol
3e0baa6146 ggsn: ctrl iface: listen on IP configured by VTY
Previosuly, the CTRL iface of osmo-ggsn was always bound to 127.0.0.1

Fixes: OS#3287
Change-Id: I9b2c1b310c7dc94ef09642f7f256ae259b41619d
2018-06-19 11:52:00 +02:00
Pau Espin Pedrol
b673d1c438 Bump version: 1.2.1.3-6a28 → 1.2.2
Change-Id: Idbc183ca37196082e95a107901bea53d37aa2ff3
2018-05-31 12:44:54 +02:00
Philipp Maier
6a2856bab5 ggsn: make sure ipcp_option_hdr and and ipcp_hdr are packed
struct ipcp_option_hdr and struct ipcp_hdr are not declared as
packed explicitly, but they are used to parse memory blobs by
casting pointers.  Add __attribute__((packed)) to ensure that
those structs are stored packed.

Change-Id: I14e10bb3ce482347b3f0c4d3a75168a55df15f20
Related: OS#3288
2018-05-28 17:50:09 +02:00
Philipp Maier
0d95ca59f9 ggsn: fix misinterpreted length field in ipcp_contains_option()
The abort condition of the while loop in ipcp_contains_option()
is accessing ipcp->len directly. Unfortunately this field is an
uint16_t which as to be interpreted as little endian value. If
it is used without prior conversion the value may appear larger
than actually intended and the loop will then not stop at the
end of end of the buffer.

This can cause unpredictable results when the value given with
the parameter enum ipcp_options opt is not found.

The loop will then eventually cause a segmentation fauld or
is likely to hang as soon as cur_opt->len points to a zero
byte in memory.

- Make sure that ipcp->len interpreted correctly by accessing
  it through ntohs()

Change-Id: Icffde89f9bc5d8fcadf6e2dd6c0b4de03440edd5
Related: OS#3288
2018-05-28 17:48:19 +02:00
Vadim Yanitskiy
906c2099da ggsn_vty.c: fix: use CONFIG_NODE as parent by default
There are some configuration nodes, which are handled by extenral
libraries, such as libosmoctrl. So, when switching back to the
parent node, this should be kept in mind.

Change-Id: I65be7910dc46166caa34a0984a6763e1477dec99
2018-05-09 23:13:09 +07:00
Pau Espin Pedrol
ac07625086 Bump version: 1.2.0.1-36c4 → 1.2.1
Change-Id: I4a8bdcbee300296496f039b90795ff981018e17d
2018-05-04 12:19:58 +02:00
Pau Espin Pedrol
36c4fac9c9 debian/rules: Fix debian packaging after 1.2.0 release
The 1.2.0 release bumped lib version to 3 and updated the debian package
file accordingly, but forgot to increase dh_strip line in debian/rules.

Change-Id: Ib54f231943348c06acecd6f413b2c96b24f6db28
2018-05-04 11:28:24 +02:00
Pau Espin Pedrol
a06b2d3877 Bump version: 1.1.0.90-5468-dirty → 1.2.0
Change-Id: I2af8c8ff75d5153456b814b9dfe4fbddafe5af7a
2018-05-03 16:05:28 +02:00
Harald Welte
546884d9a1 ggsn: don't use gtp_kernel_tunnel_{add,del}() for userspace tun
Change-Id: I00cc8eb8c4d44532f975f78783ff4e12814b3416
2018-04-25 21:44:50 +02:00
Harald Welte
f2286395e9 Move kernel GTP support from ggsn/ to lib/
This way, the IP address / route handling between TUN devices and kernel
GTP can be shared, which will provide not only a unified codebase but
also a more consistent behavior.

This also paves the road for to use kernel GTP from sgsnemu in the future.

Related: OS#3214
Change-Id: Ic53a971136edd0d8871fbd6746d7b0090ce3a188
2018-04-25 21:44:46 +02:00
Harald Welte
9eebe15cd1 lib/tun: Remove tun_setaddr() API, as everyone is using tun_addaddr() now
Change-Id: I02e057d30b6773c17ea6bc31094e53587971e9e7
2018-04-25 21:41:43 +02:00
Harald Welte
31e1dab2c0 sgsnemu: Convert from tun_setaddr() to tun_addaddr()
This converts the last caller of tun_setaddr() outside of lib/tun.c to
use tun_addaddr().

Change-Id: Ia301d6a4ee3d02c1af1c85f2fe1041d3013268b0
2018-04-25 21:41:43 +02:00
Harald Welte
db0366c9e4 ggsn: Don't explicitly use tun_setaddr() API anymore
tun_addaddr() internally contains a fallback to tun_setaddr() for the
first address, so we can unify the API usage a bit and use tun_addaddr()
from all call sites

Change-Id: I34de003a1a040254bd38b29e48caea34cb0c88d2
2018-04-25 21:41:43 +02:00
Harald Welte
47adad0817 lib/netdev.c: Cosmetic changes (coding style / cleanups)
Change-Id: I60cbca616a4f727e2374c52715f9286a0f4c5e4b
2018-04-25 21:41:43 +02:00
Harald Welte
c5efb5bccb lib/tun: split generic network device related stuff to lib/netdev
Change-Id: Ib021e392637a43d5cf1b40e0d50621fe7e854ba5
2018-04-25 21:41:41 +02:00
Harald Welte
9a6da455b9 lib/tun.c: Generalize tun_{set,add}addr*() functions
There's nothing really tun-specific about the adding and removing of
addresses to network devices.  Let's generalize the related code.

Change-Id: I139a950dd81a4b1199953be1608cd109a060f562
2018-04-25 21:40:30 +02:00
Harald Welte
b4c0828039 lib/tun.c: generalize tun_*route() to netdev_*route()
There's nothing specific to tun devices in adding a route to the kernel.

Change-Id: Ib077934aa5f3c9bed06e2cf16a980c965a7a046d
2018-04-25 20:46:05 +02:00
Harald Welte
df3dcac439 lib/tun.c: Generalize tun_sifflags() to netdev_sifflags
There's nothing "tun" specific about that function, let's clarify that.

Change-Id: Iae7ced700245d6c1ac7e9807ab80d12fde8da116
2018-04-25 20:46:05 +02:00
Harald Welte
0757504a86 fix segfault in case of kernel gtp-u
There's a problem during the initial start-up of osmo-ggsn in case
of kernel gtp-u: apn->ggsn->gsn is not yet set while parsing the
'apn' nodes from the config file.  This member is only set after
the last 'apn' node has been parsed at the end of the 'ggsn' node.

Closes: OS#3217
Change-Id: I022a5e5ebc1f155e8f94938856d310462f79bbe8
2018-04-25 20:46:05 +02:00
Pau Espin Pedrol
042a445cf3 use osmo_init_logging2
Change-Id: Ic38fff17cc985238b91999c8acdd92d22fd28c72
2018-04-17 14:31:42 +02:00
Stefan Sperling
a16c7501a4 remove the -f option from osmo-ggsn.service
This option was removed in dda21ed7d4
and the behaviour previously implied by -f has since been the default.

Change-Id: Iba13df713af03771739a4feff4b222a0c3352394
Related: OS#3044
2018-03-20 14:05:34 +01:00
Neels Hofmeyr
9f98822255 jenkins.sh: use --enable-werror configure flag, not CFLAGS
Change-Id: I64e542ad4da34a7ac3bc1b599a122ecff47e892d
2018-03-05 20:53:20 +01:00
Neels Hofmeyr
fc8357a2db configure: add --enable-werror
Provide a sane means of adding the -Werror compiler flag.

Currently, some of our jenkins.sh add -Werror by passing 'CFLAGS="-Werror"',
but that actually *overwrites* all the other CFLAGS we might want to have set.

Maintain these exceptions from -Werror:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking
   builds)

As a last configure step before generating the output files, print the complete
CFLAGS and CPPFLAGS by means of AC_MSG_RESULT.

Change-Id: Ifcde5a110cbed0eaa250dd946927e3b0f4f9bd13
2018-03-05 20:42:45 +01:00
Harald Welte
3e443ca502 Add talloc context introspection via VTY
This requires libosmocore with Change-Id
I43fc42880b22294d83c565ae600ac65e4f38b30d or later.

Change-Id: I460efff3a3dfa2c7d955871aca78b37552a29aff
2018-02-14 00:54:32 +00:00
Martin Hauke
2c10211d60 build: Remove AC_PROG_CXX, C++ is never used
Change-Id: Ifda126ab2e5fdd98317e723aa6b10d964b4519c2
2018-02-14 00:46:41 +00:00
Pau Espin Pedrol
5fdda13f89 sgsnemu: listen param is a host, not an interface
This param is parsed by gethostbyname() and it's confusing to document
it as an interface, because users will then attempt to pass "lo" to it,
which fails.

Change-Id: Id8ef0e12ddcaf8bfd199a44de0ba4280f05d4431
2018-02-14 00:43:22 +00:00
Pau Espin Pedrol
dbeaa044f8 sgsnemu: Fix bad ptr during context deallocation
Older commit switched pdp_t to have an array of 2 peers instead of
only one in order to accomodate for ipv4v6 contexts, which can have 2
addresses assigned. The usage of peer field was not updated in sgsnemu
accordingly, which means the wrong memory portion was being accessed.

Fixes: 2d6a69e69a ("Add support for IPv4v6 End User Addresses")

Change-Id: I9e563522173a82b265e93b1ef9dc93ced40fefa2
2018-02-12 19:11:33 +01:00
Viktor Tsymbalyuk
7ad4d5e8cb sgsnemu: fix: no outgoing GTP-U in "createif" mode
in "createif" mode uplink traffic not forwarding
from tun interface into Gn, inside GTP-U.
create_pdp_conf get iphash (ipm) with pdp ==  0x0
Fix - in create_pdp_conf - instead of casting using already
definned iphash in ipset function.

Change-Id: Icd58450548b3a47cb933d70a2e3166c067552b2c
2018-02-12 14:00:45 +02:00
Viktor Tsymbalyuk
ab4db10750 sgsnemu: created "pinghost" and "createif" modes for mutual exclusion
No warnings when used options from "pinghost" and "createif" groups
in a same time. sgsnemu created tun0 interface and send pings inside
G-PDU, but didn't calculate replys. Added options modes to avoid
mutual exclusion options.

Change-Id: I196df7838212dcddecd64a64a6cba613b6cfced0
2018-02-09 11:17:39 +00:00
Pau Espin Pedrol
dddbbaaee1 ggsn.c: cb_tun_ind: Don't drop packets targeting pdp ctx ll addr
Change-Id: I72602a78baa2a7e3412d8b69c1bf1b3ac0efa434
2018-01-30 22:50:14 +01:00
Pau Espin Pedrol
134855c45e ggsn.c: cb_tun_ind: log dst addr of packet without pdp ctx
Change-Id: Ib3a87ec2b2d0014376295028a648750fa3124485
2018-01-30 22:50:13 +01:00
Pau Espin Pedrol
a4942e6566 ggsn.c: cb_tun_ind: Convert ifelse to switch statement
Change-Id: I4fc97f367ae615cdf0a9501f89419990c2fd4599
2018-01-30 22:49:26 +01:00
Pau Espin Pedrol
4e43ef5ab0 ggsn: Print all addresses on successful pdp ctx creation
Change-Id: I0c57df17d91bade127027e03633494adb6f818c5
2018-01-26 18:20:22 +00:00
Pau Espin Pedrol
4ae8d8232d ggsn: Parse PCO_IPCP for IPv4v6 pdp ctx
pdp_has_v4 only take into account IPv4 EUAs.

Change-Id: I1cf8d6548951e5732075beeea1412d12fb6bdec3
2018-01-26 18:20:22 +00:00
Pau Espin Pedrol
0bdd8bf5bc ggsn: Parse PCO_IPCP
Improvements include:
- Use Identifier received from request instead of using hardcoded id=0.
- Don't add DNS to response if they were not included in request.

Change-Id: Ic8aa5d634e526683b2ad8ed5d14088e171c41c98
2018-01-26 18:20:22 +00:00
Pau Espin Pedrol
5b1ef9589c ggsn: Validate packet src addr from MS
Closes: OS#2422

Change-Id: Ie658a7f161103bb6f631ab0508e45e55fb42a442
2018-01-26 18:20:22 +00:00
Pau Espin Pedrol
7d54ed48e7 ggsn: encaps_tun: Avoid forwarding packet if EUA is unassigned, fix crash
Check (before forwarding received GTP packets into the tun) if the pdp ctx
associated with the packet requested was assigned an EUA of the given IP version.
This way we avoid for instance forwarding an IPv6 packet (or sending
back a response to a Router Solicitation packet) in case the APN was
configured without IPv6 support or if the MS/SGSN didn't ask for an IPv6
while requesting an EUA.

As a side effect, this commit fixes an OSMO_ASSERT hit introduced in handle_router_mcast
in 2d6a69e69a due to a deffective MS
sending an icmpv6 Router Solicitation over IPv6 after having been
requesting and assigned an IPv4 EUA (so no IPv6 packets expected).
Before that commit, there was no crash but the message was being wrongly
answered and used an uninitialized .v6 addr field from the peer struct.

Fixes: OS#2843

Change-Id: Ib6d18a64c2b71f3bcf6cb7e3a978d2d3f9c7a79b
2018-01-26 18:20:22 +00:00
Pau Espin Pedrol
07730bb9cc gtp/gtp.c: Use uint8_t for version param in static functions
Change-Id: I9afc36e2304f1060615219e88dd28821fb74e300
2018-01-26 18:20:21 +00:00
Pau Espin Pedrol
7b38af5cd3 gtp/gtp.c: Mark non exported functions as static
Functions not exported in gtp.h should be static.
There's no need to mark functions as extern in the .c file.

Change-Id: Ie61d5c6e0ae45ef3885911cedf71c826ed1705d0
2018-01-26 18:20:21 +00:00
Pau Espin Pedrol
85ef5833cb gtp/gtp.c: Remove unused function char2ul_t
Change-Id: I0d7493404ea05ce2e795958041bbb6cb75a04d31
2018-01-26 18:20:21 +00:00
Viktor Tsymbalyuk
a2a08f7602 sgsnemu: sgsnemu stopped after recieving "Request accepted" from ggsn
"sgsnemu" stopped with the message "Received create PDP context response. Cause value: 128",
but normaly at that poit it should continue working and create "user plane".
Reason: Funtion "create_pdp_conf" checking result of "in46a_from_eua" and mistakenly
returned EOF when more than 1 IP address provided by GGSN.
Now function "create_pdp_conf" stopped with error when 0 IP provided or error code comes from "in46a_from_eua".
Fixes: 2d6a69e69a ("Add support for IPv4v6 End User Addresses")

Change-Id: I7881b8e1f27c432007cb6e5ff665a2ce55f103b5
2018-01-26 13:30:51 +00:00
Pau Espin Pedrol
282d4e3dda gtp.c: gtp_gpdu_ind: Early return to avoid use of uninitialized var
If the version received is not known, pdp is then uninitalized so we
should not be using it. Let's return an error to inform the caller.

Change-Id: Ib3e23b61a3521bd3c9002d3165ca8eff4361a35e
2018-01-25 18:20:55 +01:00
Pau Espin Pedrol
42d3250d17 gtp.c: gtp_gpdu_ind: Convert ifelse to switch statement
Change-Id: I99b73f7a6d4100789fa92021c6ec9117869c881b
2018-01-25 18:18:07 +01:00
Pau Espin Pedrol
5aed8de11d gtp/pdp: Remove unused APIs pdp_ntoeua pdp_euaton
Change-Id: I00db99ba8de3f3d90e85bf593ed31555eedb439b
2018-01-25 18:09:51 +01:00
Pau Espin Pedrol
5f5fcff5f3 gtp/pdp: Fix trailing whitespace
Change-Id: I1bc65ef9af1144779ee91a2c9b9887233ad15671
2018-01-25 18:09:02 +01:00
Pau Espin Pedrol
a884a95a7b gtp.c: Log unsupported GTP version number
Change-Id: Idbc6e4c912b958bde5916b87ec53c3c4db70bee0
2018-01-25 17:28:26 +01:00
Pau Espin Pedrol
a4aada0b5f gtp.c: Determine GTP version from header
Change-Id: I843071a090e877fd529e1e0b799df3585bf836d8
2018-01-25 17:24:38 +01:00
Pau Espin Pedrol
732131d4d0 gtp.c: Fix trailing whitespace
Change-Id: I636c81d0c0ff53c97e6aedbc00f90c1325a3d607
2018-01-25 17:23:09 +01:00
Harald Welte
36b940d1fe README.md: Remove misleading sentence on sgsnemu
As reported by Viktor Tsymbalyuk, "Use the same LAN switch as the one
your SGSN is connected to." is of course completely bogus.  As long as
you have IP routing in place, it doesn't matter at all which switch you
are using.

Change-Id: I748752337b863b317d2899017b1dc255ced2515d
2018-01-19 15:09:15 +01:00
Max
e661277b48 Add GTP message names
Change-Id: I65eb80db4bcdc6da4d267bef3b907d3f98942a2e
2018-01-17 09:42:40 +00:00
Max
6f539aa259 Fix stow-enabled jenkins build failure
The error is:
CC       gtp-kernel.o
gtp-kernel.c:19:26: fatal error: libgtpnl/gtp.h: No such file or directory
 #include <libgtpnl/gtp.h>
                          ^
compilation terminated.

Fix it by using proper CFLAGS/LIBS for libgtpnl.

Change-Id: I5a24076778ea3ce263ac27211a6f45f935155b33
2018-01-16 16:52:04 +01:00
Max
1c8c62667f Enable sanitize for CI tests
Change-Id: I7559807d54bec5da45ea5c41b10d396d992cb1b7
2017-12-21 17:12:46 +01:00
Pau Espin Pedrol
e5a082d64a ggsn_vty.c: Print ipv6 link-local cmd when writing config to file
Previous commit added the ipv6 link-local vty cmd but forgot to add code
to print its value in config_write_apn.

Fixes: 37c45e3998

Change-Id: I08aeaa98d6dc318b7e9740d837ba4ac48cd7051c
2017-12-15 15:55:32 +01:00
Pau Espin Pedrol
37c45e3998 ggsn: Add 'ipv6 link-local' vty cmd
This vty cmd let's you set up a new link-local IP for a specific APN to
be used during ICMPv6 Router Advertisement procedure.

osmo-ggsn hence requires a link-local IPv6 address to be added to the
tun interface, otherwise the apn will not be configured correctly and it
won't be able to allocate addresses from the ipv6 pool later on.

This feature is useful in case your OS doesn't support autoconfiguring
link-local IPs when the interface is brought up (some linux versions are
known to fail at this) or in case you configured your OS specifically to
avoid automatic set up (sysctl net.ipv6.conf.*.autoconf).

If "no ipv6 link-local" is provided (default), osmo-ggsn will rely on the
OS or the ipup-script setting up the link-local IP for the tun
interface at creation time, then fetching it after ipup-script time and
using the first link-local ip found. On the other hand, if the "ipv6
link-local" cmd is provided, osmo-ggsn will add the link-local IP to the
interface manually and use that one for later Router Advertisement
procedures.

Change-Id: I09ef27f54940d4c47150e5f9016d1cd4298c16b5
2017-12-14 16:01:35 +00:00
Pau Espin Pedrol
f5e40b7011 Set tun_addaddr ipv agnostic and add support for ipv6
sgsnemu (the only user of this API so far) has been modified to use the
new API with in46_addr.

FreeBSD code for IPv6 has not been tested.

Change-Id: Ie36afe6eaf393855a4a708000ef4ad0192bf4767
2017-12-14 14:49:12 +00:00
Pau Espin Pedrol
02e21af657 tun.c: tun_addaddr: Fix segfault and wrong usage of tun_nlattr
First of all, dstaddr can be NULL, avoid copying it in that case.
Second, we want to copy the addr data, not the pointer. I tested it and
the IP was not added (not shown in ip addr) until I copied the content
instead of the address.

Change-Id: I8da637b155f0e913cab6c5b0dde355c9f33375b5
2017-12-14 14:49:12 +00:00
Pau Espin Pedrol
bffc3f9012 ggsn.c: Improve logging info on link-local ipv6 addr not found
Change-Id: I18fb952514712ff30d18c7626f84309055d3efa1
2017-12-14 14:49:11 +00:00
Pau Espin Pedrol
7c4de0776b cosmetic: sgsnemu.c: Fix trailing whitespace
Change-Id: Ic392ed35946e076a39aa5f7bf80a8c2ffe73562c
2017-12-14 14:49:11 +00:00
Pau Espin Pedrol
077b903e11 contrib: jenkins.sh: Build libgtpnl as dep when building with gtp kernel support
Change-Id: I7ee741d4940e3c10a4944b676c9765d6808afba9
2017-12-14 15:29:24 +01:00
Pau Espin Pedrol
2d6a69e69a Add support for IPv4v6 End User Addresses
Before this commit, when an MS requested an ipv4v6 context osmo-ggsn
returned an error stating the type was unknown, and this text was
printed in the log:
Processing create PDP context request for APN 'ims'
Cannot decode EUA from MS/SGSN: f1 8d

This patch has been tested with an MS running the 3 types of addresses:
- IPv4 and IPv6: no regressions observed, the context is activated and
packets are sent to the ggsn.
- IPv4v6: Wireshark correctly parses request and reponse, and then
ICMPv6 traffic from both sides. Finally I see the MS using the IPv4 and
IPv6 DNS addresses advertised and TCP traffic over IPv4 (because
probably my IPv6 network setup is not correct). I also checked I can
disable/enable data (pdp ctx delete and activate) several times without
any issue.

Change-Id: Ic820759167fd3bdf329cb11d4b942e903fe50af5
2017-12-11 11:39:18 +01:00
Harald Welte
4f0343233b ggsn: Ignore PCO with length 0, don't abort processing
The existing code would abort iterating over the list of PCO TLVs
if a TLV of length zero was encountered.  However, there's nothing
in the spec that would make a zero-length PCO invalid, so we should
continue to iterate over any PCO TLVs after the zero-length one.

This issue was discovered while writing test cases in
osmo-ttcn3-hacks.git

Change-Id: I36660566a8ee2ca80ae6ee99c86e167e7c208df2
2017-12-05 17:29:24 +00:00
Harald Welte
bcab7fb4af ggsn.c: Fix byte order of IPCP IPv4 DNS servers
... this probably didn't show up as 8.8.8.8 is dual-endian. doh!

The address was already in network byte order, but msgb_put_u32 "of
course" expects host byte order, ending up the wrong way in the actual
packets :/

Change-Id: Ia4bcac5fcebfc24760432eb66be258a01d78f65f
Closes: OS#2685
2017-12-05 17:29:24 +00:00
Max
427699e6eb Log APN and tun names for packets
Change-Id: I6f7ce33f6585b2b78e2b8a5c0f7111f0316d6ddd
2017-12-05 17:42:09 +01:00
Pau Espin Pedrol
9c0f4f49e9 tests: Split ipv6 specific tests into a new test group
This way they can be easily disabled later on when IPv6 support is made
optional.

Change-Id: I3906dbf55ccf1650083398e08ac870add0bbdcef
2017-12-04 13:25:02 +01:00
Pau Espin Pedrol
ac51c7e68e Remove unused empty src/Makefile.in
Change-Id: I207362e055dbfafc42fad2cfdd0cf1da9dcad88b
2017-12-04 13:25:02 +01:00
Pau Espin Pedrol
55d639f0fb ggsn.c: Print version of unhandled ip packet
Change-Id: I7e226a12b074c96c572f90e3aaf62716d0cd47c5
2017-12-04 13:25:02 +01:00
Pau Espin Pedrol
b9ace14717 cosmetic: Reorder tun_addaddr to get rid of decl of tun_setaddr4
In any case, if we add support for ipv6 in tun_addaddr we will need
tun_setaddr (and also change the API of tun_addaddr to use in46_addr).

Change-Id: Iadf51379455174a642b477040ec96f28022c24c7
2017-12-01 15:40:32 +01:00
Pau Espin Pedrol
d9fff0c543 tun_setaddr6: Fix log typo
Change-Id: Id7f7d33a33730d57c5ecf7ebf5612f4744cf5163
2017-12-01 15:39:28 +01:00
Neels Hofmeyr
1d85bea152 sanitize build: ensure uint16/32 alignment in gtpie_test and in46a_test
Fixes sanitize build failures:

  Testing gtpie_tlv()
  ../../../../src/osmo-ggsn/tests/gtp/gtpie_test.c:30:2: runtime error: load of misaligned address 0x55c0a0830f21 for type 'uint16_t', which requires 2 byte alignment
  0x55c0a0830f21: note: pointer points here
   00 00 00  17 00 06 01 02 03 04 05  06 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00
                ^
  Testing gtpie_tv0()
  Testing gtpie_tv1()
  Testing gtpie_tv2()
  ../../../../src/osmo-ggsn/tests/gtp/gtpie_test.c:76:2: runtime error: load of misaligned address 0x55c0a0830f21 for type 'uint16_t', which requires 2 byte alignment
  0x55c0a0830f21: note: pointer points here
   00 00 00  2a ab cd 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00
                ^
  Testing gtpie_tv4()
  ../../../../src/osmo-ggsn/tests/gtp/gtpie_test.c:90:2: runtime error: load of misaligned address 0x55c0a0830f21 for type 'uint32_t', which requires 4 byte alignment
  0x55c0a0830f21: note: pointer points here
   00 00 00  2a ab cd 01 23 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00
                ^
  Testing gtpie_tv8()
  ../../../../src/osmo-ggsn/tests/gtp/gtpie_test.c:104:2: runtime error: load of misaligned address 0x55c0a0830f21 for type 'uint32_t', which requires 4 byte alignment
  0x55c0a0830f21: note: pointer points here
   00 00 00  2a 00 01 02 03 04 05 06  07 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00
                ^
  ../../../../src/osmo-ggsn/tests/gtp/gtpie_test.c:105:2: runtime error: load of misaligned address 0x55c0a0830f25 for type 'uint32_t', which requires 4 byte alignment
  0x55c0a0830f25: note: pointer points here
   00 01 02 03 04 05 06  07 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00
               ^

Change-Id: I9eb16450af942d6464211e190f6a4d5a1d814842
2017-11-29 16:20:57 +00:00
Pau Espin Pedrol
f1e44c5493 examples: Add secondary ipv6 google DNS to osmo-ggsn.cfg
Change-Id: I5efbd1848a7974cb8dc614c4567de2658b9a7269
2017-11-28 11:13:55 +01:00
Harald Welte
bebd75c2d1 Merge changes Id4724fe0,I5c619712
* changes:
  contrib/jenkins.sh: Enable Werror in C(PP)FLAGS
  ggsn_vty: Stop using deprecated API vty_install_default
2017-11-18 10:32:37 +00:00
Neels Hofmeyr
878ece768b add --enable-sanitize config option
Change-Id: I439ff2b2cb36a5c29347a914c0f2e21bed598b06
2017-11-17 02:05:34 +01:00
Pau Espin Pedrol
a00e79242b contrib/jenkins.sh: Enable Werror in C(PP)FLAGS
Change-Id: Id4724fe07f6641e82c3bd9cde2d8d759aa492288
2017-11-16 17:09:06 +01:00
Pau Espin Pedrol
840ce8a0a8 ggsn_vty: Stop using deprecated API vty_install_default
Changes made as requested by the deprecation text.

Fixes warning below:
 warning: ‘vty_install_default’ is deprecated: Now happens implicitly with install_node() [-Wdeprecat
ed-declarations]
  vty_install_default(GGSN_NODE);
  ^~~~~~~~~~~~~~~~~~~

Change-Id: I5c6197129e0c251a4e8dd174027b011c8f6476c6
2017-11-16 17:01:51 +01:00
Harald Welte
afd76a731f contrib/jenkins.sh: Allow jenkins job to specify if kernel GTP is used
Change-Id: I83319aa6e5e7dde229ae5a036b5a1800879cbf81
2017-11-14 14:48:32 +09:00
Harald Welte
fd30bd1032 gtp-kernel: Add device nime in pdp_debug() log statements
Change-Id: Iad0e7a9fa48fcddc31b8d555244581efdbd61b4e
2017-11-14 00:08:51 +09:00
Harald Welte
227034c88e gtp-kernel: Add function name to pdp_debug() function calls
This allows us to distinguish "add" from "del" operatons in the log

Change-Id: Ibe2e76a6eecc7b5fa5f44ab2c1578597138e30b9
2017-11-14 00:08:51 +09:00
Harald Welte
3dad951171 gtp-kernel: Get rid of SYS_ERR where not applicable
SYS_ERR is for logging an error from the (operating) system including
the errno value.  For general logging, we have DEBUGP/LOGP.  Let's
convert the gtp-kernel logging over.  This also fixes the related line
ending mess-up as SYS_ERR adds a LF while LOGP/DEBUGP don't.

Change-Id: Idb4069a28227b770e20d62bf306cd294f47146ae
2017-11-14 00:08:51 +09:00
Harald Welte
318795635e gtp-kernel: proper cleanup in error path
When genl_socket_open() succeeds but genl_lookup_family() fails,
we have to clean up the socket that we just opened.

This requires a new version of libgtpnl :/

Change-Id: I31df046530347f88cb7b16c37a899b456ed1b080
2017-11-14 00:08:51 +09:00
Harald Welte
22e1573831 gtp-kernel: Make sure repeated calls to gtp_kernel_init() are safe
We have to factor out the "run once" code and make sure to really
only run that once, while the per-device code remains in the
gtp_kernel_init() function.

Change-Id: Iba5bd71e4b725eef59fe4f233fbb965e396a06c3
2017-11-14 00:08:51 +09:00
Harald Welte
c85e89961a gtp-kernel: Avoid global state variable
Whether or not GTP kernel support is enabled is the property of a
given APN, and not a global state variable.

Change-Id: Iff3bd8a52bd6c20f9811ee41ff700486d08591f3
2017-11-14 00:08:51 +09:00
Harald Welte
2fc2bc6bc4 gtp-kernel: Align logging for APN start in kernel-gtp case with that of TUN
Change-Id: Ie53d37f151e8b6448636a8cde5777b3841989d05
2017-11-14 00:08:51 +09:00
Harald Welte
0d0e242685 gtp-kernel: shut down kernel GTP device in apn_down()
When we take the APN down, we should also take the GTP device down.

Change-Id: Idd250dd454a1603834d388884a24a63e044fdd7b
2017-11-14 00:08:51 +09:00
Harald Welte
698a2339eb gtp-kernel: Get rid of hard-coded kernel GTP device name
The existing kernel GTP support code inherited from OpenGGSN was overly
simplistic and didn't support multiple GTP devices or user-defined GTP
device names.  Let's remove that restriction in this patch

Change-Id: I51df223788fd5b7cf8099463b8aa0ca4a4fd1c96
2017-11-14 00:08:48 +09:00
Harald Welte
490782d18e gtp-kernel: Re-add support for kernel GTP-U acceleration
When we branched off osmo-ggsn from the old openggsn code base, the
support for kernel-gtp got temporarily removed.  This patch
re-introduces support for handling the GTP-U plane in the Linux kernel
by means of libgtpnl + the kernel GTP-U driver.

This only works for IPv4 at the moment, until the kernel GTP-U code
gains IPv6 support.

Kernel GTP currently also is restricted to a single APN per GSN.

Change-Id: Ieb1bc1bd0d51d41947f0abd6ebbc2e5d102592d6
2017-11-13 23:59:40 +09:00
Harald Welte
e3c5918aee gtp_kernel: Change gtp_kernel_init() function signature
Rather than taking an explicit in_addr, prefix_length and a
string-formatted prefix, let's pass in an in46_prefix and derive
the other representations from it.

Also, don't refer to a no-longer-existing global 'ipup' variable but
add it as a function argument.

Change-Id: Ife87142c86589b4fa4062d62afe3670467548589
2017-11-13 23:57:58 +09:00
Harald Welte
e2a1de5ca5 Properly NULL-out blacklist in alloc_ippool_blacklist()
This ensures that in case of error, any caller can still safely
call talloc_free() on the blacklist pointerm as free on NULL
is well-defined.  With the code prior to this patch we fear
a double-free.

Change-Id: Idc511cb3f0dfb922920aba8f88ea77df1722ecdc
2017-11-13 23:57:58 +09:00
Harald Welte
4c7d29107f factor out netdev_ip_local_get() from tun_ip_local_get()
netdev_ip_local_get() is a generalized version of tun_ip_local_get()
which supports the net device as argument, rather than a tun_t.

Change-Id: I072aa1a55e7bf110706e9207021b776d9b977fb6
2017-11-13 23:57:58 +09:00
Harald Welte
f55a039048 remove unused argument to alloc_ippool_blacklist()
Change-Id: I4d3ea077ba46446e537ec9a6de6f4080fcaa428c
2017-11-13 23:57:58 +09:00
Harald Welte
fc6676c4a0 ippool: Correctly compute size of static pool
* we have to use stataddr, not addr (dynamic)
* we have to multiply the length of the address by 8 to get its bit length
* we can simplify the -1 +1 logic (like dynamic)

Change-Id: I174102051bef95f7df34b7d7c480a00ae408be7d
Fixes: Coverity CID#174189
2017-11-06 03:38:54 +09:00
Harald Welte
1af543f44c tun: Don't copy 16byte IPv6 address to 'struct in_addr'
The 'struct tun' curently only has an in_addr (v4-only) member to
store the address of the tun device, so let's not attempt to store
an IPv6 address in it.

FIXME: This entire code needs an overhaul. The assumption that there's
only one address, and only either v6 or v4 is broken to begin with.

Change-Id: If0b626d688841d6e0a3867834f4cb1b70084050e
Fixes: Coverity CID#174278
2017-11-06 03:32:52 +09:00
Harald Welte
bd228244da gtp: Explicit OSMO_ASSERT to ensure pdp variable is set
Change-Id: I09e37e25fd118ac0a54ab788304d3f5083463050
Fixes: Coverity CID#174335
2017-11-06 03:16:49 +09:00
Harald Welte
a06120de77 gtp: Fix buffer overflow in imsi_gtp2str()
The string buffer allocated for the IMSI must be sized for a length
twice the number of input bytes (each byte has two nibbles) plus 1
byte for NUL.  We missed the "twice" part :/

Change-Id: I1ecaa811815ae522af71feabc5d0c1ea8b4edde9
Fixes: Coverity CID#174336
2017-11-06 03:12:54 +09:00
Harald Welte
b589e78f13 sgsnemu: Free strings in error path
In create_pdp_conf(), we have to free() any strings both in the
success and in the error case.

Change-Id: If59cc8d6d151c123f46c1d029091209fd82b3c8e
Fixes: Coverity CID#187636, CID#187633
2017-11-06 03:10:31 +09:00
Harald Welte
b11ed0f132 sgsnemu: Make sure buffer has space for terminating-NUL
In proc_ipv6_conf_read() we allocatea buffer on the stack but
forgot the terminating NUL byte.

Change-Id: I54126d8bc08c137859f2de4b47ef23fc0714fdd7
Fixes: Coverity CID#178641
2017-11-06 03:07:26 +09:00
Harald Welte
9c332104eb sgsnemu: Fix format string in printing tun-device name
Change-Id: Ie05050a78a135a1a76473337a341fd723bbe4976
Fixes: Coverity CID#178654
2017-11-06 02:44:42 +09:00
Harald Welte
51127ea962 sgsnemu: Don't leak FILE handle in proc_read()
Change-Id: Ie22e6a9bc172427e867e7a4001b6c710477a232b
Fixes: Coverity CID#178660
2017-11-06 02:42:59 +09:00
Neels Hofmeyr
dabb8b4860 fix compiler warnings: return 0 in main(), in 3 tests
Change-Id: I9e49ceba6b0a8dffd331e5707667d9bb007f3ec7
2017-10-31 01:02:00 +01:00
Harald Welte
ff069172ce Tag/Release Version 1.1.0
Change-Id: I30a9e72fa9c3d6fc755c5531844b663c08c3ac06
2017-10-28 19:01:30 +02:00
Harald Welte
7bee06e1cc Debian: include the systemd service file for osmo-ggsn
Change-Id: I5ebee0135e638b7327a16218bdce466ada9aee56
2017-10-28 18:58:38 +02:00
Harald Welte
6c10aa0e6f Debian: package libgtp debug symbols as libgtp-dbg
... and some more Description for debian/control

Change-Id: I099735f8550134441a77c29e8c4d4d3c2490e379
2017-10-28 18:58:38 +02:00
Harald Welte
a4e24f5546 Debian: libgtp is libgtp2 for some time, not libgtp1 anymore
In Change-Id Ie631880155513b1b78d1e9dd473dc5dc50e05943 we changed
LIBVERSION but didn't update debian/control.

Change-Id: I7a1349e9609cb455c0fe9d63d085e7c44dff96ef
2017-10-28 18:34:08 +02:00
Neels Hofmeyr
29caaab817 jenkins: use osmo-clean-workspace.sh before and after build
See osmo-ci change I2409b2928b4d7ebbd6c005097d4ad7337307dd93 for rationale.

Depends: I2409b2928b4d7ebbd6c005097d4ad7337307dd93
Change-Id: I1424dff06c7d4f695af0936671ad6faa504aaf16
2017-10-27 22:54:29 +02:00
Pau Espin Pedrol
859f9b0752 ippool: Implement and use blacklist instead of blindly using IPPOOL_NOGATEWAY
Commit dda21ed7d4 modified previous calls
to ippool_new() removing the pass of flags to avoid allocating certain
problematic IPs from the pool to MS, such as the network, gateway and
broadcast IPs.

Today I did some unsucessful tests with osmo-ggsn with a pool "ip prefix
dynamic 176.16.222.0/24", and thus IP 176.16.222.0 was being assigned to
the MS. De-capsulated DNS packets were received in the tun interface,
but the Linux system in there was unable to correctly forward the
packets to the gateway interface connected to the Internet. However,
adding a second MS which got 176.16.222.1 had its packets forwarded
correctly.

However, previous implementation relies on flag IPPOOL_NOGATEWAY flag to
blindly blacklist first IP after the network ip (ie, .0 and .1 are
removed), which limits the IP reserved for the tun device to be .1. If a
different IP in the range is assigned, it may cause issues. As a result,
a blacklist is introduced in this commit to dynamically fetch the tun IP
address and exlucde it from the pool of available IPs.

Change-Id: I8e91f7280d60490c858a769dd578c1c8e54e9243
2017-10-17 19:10:24 +02:00
Pau Espin Pedrol
a037e5908a tun: Convert tun_ipv6_linklocal_get to be more generic
Add support for IPv4 and IPv6 global IPs. Also return the prefix length
of the IP address by using a in46_prefix.

Change-Id: I277af191dc611b6bbcb83479f4ae338083740322
2017-10-16 17:45:48 +02:00
Pau Espin Pedrol
2e7b9ff891 lib/in46a: Introduce in46a_netmasklen API
Change-Id: I06e3e038afd8f7afaec2a3fa67b1616500c8db80
2017-10-16 17:45:40 +02:00
Pau Espin Pedrol
361cb9e910 lib/ippool: Fix listsize calculated 1 elem too small
Take the chance this commit is changing test output to also remove use
of IPPOOL_NOGATEWAY which is going to be removed soon, and instead test
IPPOOL_NOBROADCAST.

Change-Id: I95c24bc690490155bec9e3933d678e4668d7745f
2017-10-16 11:59:43 +02:00
Harald Welte
5bacb59a6c Merge "sgsnemu: Use getprotobynumber() in print_ipprot()" 2017-10-15 16:01:08 +00:00
Harald Welte
5b0096a236 Merge "sgsnemu: Remove dead code: encaps_printf()" 2017-10-15 16:01:08 +00:00
Harald Welte
df6a105024 Merge "sgsnemu: Mark local functions 'static'" 2017-10-15 16:01:08 +00:00
Harald Welte
226e95af1f Merge "sgsnemu: Make use of "dependon" feature in gengetopt" 2017-10-15 16:01:08 +00:00
Harald Welte
e37f48eaf9 sgsnemu: Use getprotobynumber() in print_ipprot()
There's no point in sgsnemu doing a poor mans reimplementation
of what the C library provides already by means of getprotobynumber()

Change-Id: I8cdc460e4fa5d86d80addf6e5f341d2d80093a35
2017-10-14 16:39:07 +02:00
Harald Welte
8a55263a1b sgsnemu: Remove dead code: encaps_printf()
This function is never used/called, remove it.

Change-Id: I37a447e4d5387e3fc5f4433ab20ceba7c446684b
2017-10-14 16:39:07 +02:00
Harald Welte
fed3389112 sgsnemu: Mark local functions 'static'
We don't need to export those to the global name space as they're
not called from code in other files.

Change-Id: I454249335ba46abdb3afbc669c4a06a06f39ae72
2017-10-14 16:39:07 +02:00
Harald Welte
081f30cba4 sgsnemu: Print warnings on broken IPv6 configuration (acept_ra)
When sgsnemu is used for an IPv6 pdp context, we rely on the router
discovery procedure and SLAAC to set the correct IPv6 address/prefix
on the tun device.  This requires the system to be configure to accept
router-advertisements on the tun device.  Let's print a warning
if accept_ra for the specific tun device is set to a wrong value.

We're leaving it up to the user to either set a system-wide
/proc/sys/net/ipv6/conf/default/accept_ra or to configure this in an
ip-up script used together with sgsnemu.

Change-Id: I563092ca35bc74f035a5023e11256779aac46e11
2017-10-14 16:39:07 +02:00
Harald Welte
ea0c26a436 sgsnemu: Make use of "dependon" feature in gengetopt
The gengetopt syntax can specify that a particular command line argument
depends on some other argument/option present.  We can use this to
provide useful feedback to the user at the command line parsing state,
like --pingrate making no sense without --pinghost being specified.

Change-Id: Ief27275e90e6bce23aed1e83874dbac98dd0926b
2017-10-14 16:39:07 +02:00
Harald Welte
9d9d91b8e8 ggsn: Avoid crash on wrong EUA type
If the EUA in the Create PDP Context Request was not supported by
the given APN (e.g. IPv6 request for a v4-only APN), we crashed.

Avoid this and add proper handling of this error case.

Change-Id: I8d1f7ec727c5d2d4427232015f81ed57d3440dff
2017-10-14 16:39:07 +02:00
Harald Welte
73abc38dc5 sgsnemu: Add '--tun-device' option to specify TUN device name
This way, multiple sgsnemu instances can be runnig in parallel, each
of them creating a different tun device for their respective PDP context

Change-Id: Id12fbadf924a60db255b6d51b9f647aa51dd2e16
2017-10-14 08:17:07 +02:00
Harald Welte
be4baa6d97 Merge "Add unit tests for libgtp gtpie.[ch] functions" 2017-10-14 06:16:36 +00:00
Harald Welte
d369013250 Merge "gtpie_{encaps,encaps2}(): RAI is a fixe 6-byte length field, not 8 bytes" 2017-10-14 06:16:36 +00:00
Harald Welte
8afec5f86d Merge "gtpie.h: Add IE identifier definitions up to 29.60 v11.8.0 Release 11" 2017-10-14 06:16:36 +00:00
Harald Welte
5943cbb73f Add unit tests for libgtp gtpie.[ch] functions
This doesn't yet cover all the functions in gtpie.[ch], but testing half
of them is better than not testing any of them, so let's merge this
current state with a couple of TDOO's on what we still need to test.

Change-Id: I30a6dd8a01b7a074ef2d3936d186dfff6c79e6c0
2017-10-14 08:11:13 +02:00
Harald Welte
f6c5f9524f gtpie_{encaps,encaps2}(): RAI is a fixe 6-byte length field, not 8 bytes
gtpie_decaps() always had this right, but the encapsulation functions
treated it as 8-byte fixed length IE.

I hope we had a chance to convert all of this to the normal libosmogsm
tlv_parser one day.  This would have one description table for all TLV
types which then is used from encoder and decoder.

Change-Id: I48471f2735511806ac424b5ffc1929e85bb156f3
2017-10-14 07:59:07 +02:00
Harald Welte
bc41c8d581 gtpie.h: Add IE identifier definitions up to 29.60 v11.8.0 Release 11
Change-Id: I6ab7bfb31f93f52f9f6b1b5880dcb2c232bba794
2017-10-14 07:49:15 +02:00
Harald Welte
89e1abcb18 Allow Common flags 0x94 and ignore them in PDP activation
extended from https://github.com/osmocom/openggsn/pull/2

Change-Id: I31b3e4b378e74bb5a0a2f54af6d2a15b629876cf
2017-10-14 07:43:58 +02:00
Harald Welte
8376972050 gtpie: Add missing #include of <arpa/inet.h> for htonl() and friends
Change-Id: Id69d957d0860ee49e7f0db5c64ab8ba976f8c8d6
2017-10-13 16:36:43 +02:00
Harald Welte
a964027344 gtpie_decaps: const-ify pointer to input packet data
Change-Id: Ia048abcd80f29581c6ac02cd7f534f2617005671
2017-10-13 16:36:43 +02:00
Harald Welte
c5150cecc5 gtpie: Add doxygen API documentation
libgtp should have proper API documentation, let's start with the
gtpie.[ch] parts.

Change-Id: I97df5bd3c8dcc5c97a114c8c6abadabf33147b05
2017-10-13 16:36:43 +02:00
Harald Welte
02af9b3ca2 gtpie_tlv() gtpie_tv0(): const-ify read-only input argument
Change-Id: Ide487e34884c8356694246f43c3f1f562357304c
2017-10-13 16:36:43 +02:00
Harald Welte
db924d3908 Add unit tests for lib/in46_addr.c code
Change-Id: Id032c330405e5dca8ecfc0970d128341ed75c675
2017-10-13 16:36:39 +02:00
Harald Welte
34a7416ec0 in46a_to_sas(): Return AF_INET6 in case of IPv6 address
Change-Id: Ia2f9ac60f08823d5f7c1a76c0b7cbd65ac615e26
2017-10-13 16:28:01 +02:00
Pau Espin Pedrol
45ce2725ac tests: Remove Makefile.in
Change-Id: Ie21e8bfab7cda52aa696dd26280efc05c4b51bba
2017-10-13 16:28:01 +02:00
Harald Welte
a2eb5eb760 update .gitignore
Change-Id: I030bd616dd27d49e55b5bdcc7c0b4fa3eb523da4
2017-10-13 16:28:01 +02:00
Pau Espin Pedrol
fdd732b130 Remove trailing whitespace
Change-Id: I8e24f95a88bef3a59006a89c219871e6156963d7
2017-10-13 16:28:01 +02:00
Harald Welte
1d8ffc6b23 Add ippool unit-test for 'make check' runs
This test creates a variety of v4 (and one v6) pool and performs
allocations until the pool is full, then frees a random number of
randomly distributed addresses, re-allocates them and again checks that
they're all available and finally once the pool is full allocations
fail.

Change-Id: Ibf4588e8f3ae71684e5262c0caaa2689aee73a94
2017-10-13 16:28:01 +02:00
Pau Espin Pedrol
58c0da7833 lib/tun.c: tun_ipv6_linklocal_get(): fix memory leak with getifaddrs()
From getifaddrs(3) man:
"The data returned by getifaddrs() is dynamically allocated and should
be freed using freeifaddrs() when no longer needed"

Change-Id: If6300d1c8d36fcafef294a4c11bbda31a158bb9c
2017-10-12 18:00:16 +02:00
Pau Espin Pedrol
958256f5cf create_context_ind(): Fix crash on apn not found
Program terminated with signal SIGSEGV, Segmentation fault.
0  create_context_ind (pdp=0xb6b391b0 <pdpa>)
    at /usr/src/debug/osmo-ggsn/1.0.0+gitrAUTOINC+ab5e160937-r0/git/ggsn/ggsn.c:453

453             if (!apn->started)

(gdb) bt
0  create_context_ind (pdp=0xb6b391b0 <pdpa>)
    at /usr/src/debug/osmo-ggsn/1.0.0+gitrAUTOINC+ab5e160937-r0/git/ggsn/ggsn.c:453
1  0xb6b225e0 in gtp_create_pdp_ind (gsn=gsn@entry=0x74f28, version=version@entry=1, peer=0x0,
    peer@entry=0xbee6ead4, fd=-1092167056, fd@entry=8, pack=pack@entry=0xbee6eae4, len=len@entry=179)
    at /usr/src/debug/osmo-ggsn/1.0.0+gitrAUTOINC+ab5e160937-r0/git/gtp/gtp.c:1591
2  0xb6b245e4 in gtp_decaps1c (gsn=0x74f28)
    at /usr/src/debug/osmo-ggsn/1.0.0+gitrAUTOINC+ab5e160937-r0/git/gtp/gtp.c:2986
3  0x41d770c0 in osmo_select_main () from /usr/lib/libosmocore.so.8
4  0x000121b8 in main (argc=4, argv=0xbee70e54)
    at /usr/src/debug/osmo-ggsn/1.0.0+gitrAUTOINC+ab5e160937-r0/git/ggsn/ggsn.c:897

Fixes: dd266066c7, b16c46b4c3

Change-Id: Ie4ec74e87aaf1d067dd1717d986673be56c4d6ed
2017-10-11 20:37:24 +02:00
Harald Welte
6748dc90b8 sgsnemu: Add IPv6 support via tun device and "-t v6 --createif"
The idea is to only implement the GTP-C plane and configure the right
link-local source address on the tun-device and let the regular (Linux)
kernel take care of sending router solicitations and
accepting/processing the related router advertisement.  This avoids a
lot of complexity in sgsnemu.

For this to work, you must have /proc/sys/net/ipv6/conf/$tun/accept_ra
set to either 1 (works only if no IPv6 forwarding/routing configured on
your sgsnemu-running system) or 2 (works even if forwarding/routing is
configured).

Change-Id: I57e4c53ee648e1efecfba3eea592d1129849557c
Closes: OS#2518
2017-10-10 08:56:09 +08:00
Harald Welte
7bd7b6815a Merge "create_context_ind(): ignore a non-started default APN" 2017-10-01 10:31:09 +00:00
Harald Welte
b16c46b4c3 create_context_ind(): ignore a non-started default APN
If the default APN has not been started, it is not eligible to be
used in starting of new PDP contexts.

Change-Id: I93b5c205c033f275824ee8bc8cdcf1428fb086df
2017-10-01 18:29:41 +08:00
Harald Welte
840a8e9713 sgsnemu: Allow specification of PDP (EUA) Type IPv4 or IPv6
This just adds the capability to sgsnemu to request a certain PDP
EUA type.  It doesn't mean it actually handles anything beyond the
existing IPv4 yet.

Change-Id: I157f9157a7ff2ea56c37a4a902d4706de4c7d35d
2017-10-01 18:19:07 +08:00
Harald Welte
cee7546f15 Replace EUA magic numbers for IETF, IPv4 and IPv6 with #defines
Change-Id: I33f65e404217e717bd795e5229c8d9456a7b3739
2017-10-01 18:19:07 +08:00
Harald Welte
ed1ba2c902 apn_start(): Extend error message when setting IPv6 address fails
Tell the user about possible causes of failure to set the IPv6
address of the tun device, such as general lack of IPv6 support in
the kernel/OS, or the use of /proc/sys/net/ipv6/conf/default/disable_ipv6

Change-Id: I5ff812425ee12b8386bb66521e05c93e825a4506
2017-10-01 18:19:07 +08:00
Harald Welte
ed08eb1c5a apn_stop(): Print tun device name when closing tun device
Change-Id: If981cc0696122cb69c01ceac6f54ae01bcbf4a2d
2017-10-01 18:19:07 +08:00
Harald Welte
2e84d2c29a create_context_ind(): ignore any non-started APNs
If we receive a GTP-C CREATE PDP CONTEXT for an APN that we were
unable (or not configured) to start, ignore that APN.

Change-Id: I8011a9ccc1d5effd3779f184c9055af46838ccaf
2017-10-01 18:19:07 +08:00
Harald Welte
dd266066c7 apn_start(): fix clean-up after errors bringing up APN
When there's an interim error (e.g. in resolving the link-local address
or setting up the tun device), apn_start() simply calls apn_stop()
on the not-yet-fully-started apn_ctx.

This only works if apn_stop() doesn't bail out early in case of
a not-started apn_ctx, so let's remove the related check at the
start of the function.

Change-Id: I2917a6258cb73cc12fd9d81296ff0eaa616890b9
2017-10-01 18:19:07 +08:00
Pau Espin Pedrol
b5624c3d48 contrib: osmo-ggsn.service: Use expected suffix for cfg file
files in doc/examples/ dir end with .cfg, as well as all configuration
files present in all projects.

Change-Id: I361c67809d095dd08b0f400de2a6f84f981411c5
2017-09-27 20:57:06 +02:00
Max
6a21527a2d Move extended PDP logging macro to header
It might be useful for any user of libgtp who uses libosmocore so let's
make generalized version of it available as part of installable header.

Change-Id: I79aba10ef989384a28f059c30899e65c771ae5e1
Related: SYS#3610
2017-09-25 10:35:34 +02:00
Harald Welte
1a8bc9839a Merge "sgsnemu: Add --no-tx-gpdu-seq option to suppress transmission of G-PDU sequence numbers" 2017-09-24 15:20:23 +00:00
Harald Welte
79aa4bd837 Merge "ggsn: Add per-APN VTY configuration option on G-PDU sequence numbers" 2017-09-24 15:20:23 +00:00
Harald Welte
fbb9c7f59a sgsnemu: Add --no-tx-gpdu-seq option to suppress transmission of G-PDU sequence numbers
Related: OS#2519
Change-Id: Idc650d896f0f72329090b56a37d9c16359294860
2017-09-24 23:10:05 +08:00
Harald Welte
3c1cce245e libgtp: Allow each PDP context to specify if it transmits G-PDU sequence numbers
GTP sequence numbers on GTP-U are optional for G-PDU type messages (i.e.
user-ip messages).  Let's allow the user to specify this behavior by
a new pdu_t.tx_gpdu_seq flag.  The flag is enabled by default to stay
compatible with the prior behaviour.

Related: OS#2519
Change-Id: Icf22a2ddd5c4a968ef5bda7c202b921d93fb49e6
2017-09-24 23:10:01 +08:00
Harald Welte
93fed3bc51 ggsn: Add per-APN VTY configuration option on G-PDU sequence numbers
This per-APN vty option determines if we are transmitting GTP sequence
numbers in downlink G-PDU messages.  This behavior is optional as per
GTP spec.  The default behavior is "true", like before this change.

Related: OS#2519
Change-Id: Ibf0de261f83951309b01b4feae998b6656c77664
2017-09-24 23:10:01 +08:00
Harald Welte
00d346092b Merge "ggsn: Fix double whitespace in writing "ifconfig" lines" 2017-09-24 14:58:50 +00:00
Harald Welte
3ca419a2ef ggsn: Fix config file writing of IPv6 DNS settings
There was a copy+paste mistake that created syntax errors during the
write of a config file that contained IPv6 DNS server settings.

Change-Id: Ida40c32c72dba8155f8294b93484e46e8bd27739
2017-09-24 22:49:21 +08:00
Harald Welte
ff438174aa ggsn: Fix double whitespace in writing "ifconfig" lines
This is merely a cosmetic issue, no functional change.

Change-Id: I8663ee633524eedeed5ddd45ddb65a06825052ac
2017-09-24 22:49:21 +08:00
Harald Welte
f85fe9720b ICMPv6: Send router advertisement from own link-local address
I'm not quite sure how I ended up doing this, but for some strange
reason the code before this commit is sending the ICMPv6 Router
Advertisements from some weird non-standard source address.  This is
a violation of RFC4861 which clearly states that the source address
of router advertisements "MUST be the link-local address assigned to the
interface from which this message is sent."

Change-Id: Ib444af70fc8f0b433d371281601fd5a37b29039e
2017-09-24 20:51:47 +08:00
Harald Welte
fed598f41d gtp: Avoid magic numbers when operating on GTP header flags
Let's introduce a couple of #defines that make the code much more
readable.

Change-Id: I3635d679fd54507274b46e99a02bdbbe41d7684e
2017-09-24 16:53:16 +08:00
Harald Welte
471e349ecc libgtp: Avoid extra memcpy() in gtp_data_req() by using sendmsg()
Adresses two "TODO Should be avoided" comments about an extra memcpy()
before sendto() that can be replaced by a single sendmsg() call with an
iovec array: 1 record for the GTP header + 1 record for the user payload.

Change-Id: Ie332a6b15972330fcf540753898eb84ecb84fe24
2017-09-24 16:12:39 +08:00
Harald Welte
7e1175f6d8 sgsnemu: Fix gengetopt package name
gengetopt by default picks the program name from PACKAGE (autotools),
which is osmo-ggsn and is obviously wrong in case of sgsnemu.

After this patch, "sgsnemu --help" no longer shows "osmo-ggsn" but
"sgsnemu" at the top of the help text.

Change-Id: Ifabc2435a503ef71aa5a002ca46833f329068b37
2017-09-24 10:53:53 +08:00
Harald Welte
f621498129 sgsnemu: Re-generate cmdline.[ch] using gengetopt
This will replace the manual additions to cmdline.[ch] with
auto-generated code from gengetopt.  We need to fix-up the RAT Type in
sgsnemu.c as the manually-added code diverged from what gengetopt
generates.

Change-Id: Ia687e13d5cec1655a57078a767d2123aa022842c
2017-09-24 10:27:07 +08:00
Harald Welte
7c20148e39 sgsnemu: Fix up gengetopt file for --norecovery
In commit 3a4c67b4bf we introduced the
--norecovery command line option, but this was apparently done by
manually editing the C source code rather than adding it to the .ggo
and letting gengetopt do its magic.  Let's fix this up.

Change-Id: I1698280a699b17cea65651c3736ef149aba7e432
2017-09-24 08:56:14 +08:00
Harald Welte
b6fc227763 sgsnemu: Fix up gengetopt file for RAI
In commit 41af5691ef we introduced the
--rai command line options, but this was apparently done by
manually editing the C source code rather than adding it to the .ggo
and letting gengetopt do its magic.  Let's fix this up.

Change-Id: Iaab404c3bcfc0c3943764f6616763f4f407d5644
2017-09-24 08:53:58 +08:00
Harald Welte
1d94585f96 sgsnemu: Fix up gengetopt file for rattype, userloc, mstz, imeisv
In commit 944dce3e66 we introduced various
command line options, but this was apparently done by manually editing
the C source code rather than adding it to the .ggo and letting
gengetopt do its magic.  Let's fix this up.

Change-Id: Ib8e7ef1cad5fc4423a1a4af628950aa93a4e073a
2017-09-24 08:51:02 +08:00
Harald Welte
05ac095006 sgsnemu: Fix up gengetopt file for QoS Extensions of 24.008
In commit 11a398fbc3 we introduced the
--qose{1,2,3,4} command line options, but this was apparently done by
manually editing the C source code rather than adding it to the .ggo
and letting gengetopt do its magic.  Let's fix this up.

Change-Id: I4cd827a96ac17f6eb9f824342f195727426d0e20
2017-09-24 08:41:14 +08:00
Harald Welte
73d28c9dda sgsnemu/cmdline.ggo: Remove 'unsigned' which is no longer supported
It seems like modern gengetopt no longer supports 'unsigned int'
argument types, and we need to use 'int' instead.  tested with 2.22.6

Change-Id: I34ca86cb3cc482400a7c4b3bf77c8668aaef562e
2017-09-24 08:34:36 +08:00
Max
ea70f3619a Fix leftovers after rename
* Use proper name in jenkins test
* Fix naming in systemd service
* Fix git-review config

Change-Id: I934f897002215d7d4e610cbd312383181bbe97c9
2017-09-15 12:19:23 +02:00
Harald Welte
98146776dd ggsn: Add ability to specify local IP addresses for GTP-C and GTP-U
In case the GGSN is behind some kind of DNAT, the public GTP-C and
GTP-U IP addresses as exposed inside the GTP payload information
elements are different from the (internal, behind-nat) IP address
to which it listens/binds.

Change-Id: I548c9011c9abd66d46f963b1def61575f3dabb89
2017-09-06 12:22:31 +02:00
60 changed files with 72619 additions and 2934 deletions

48
.gitignore vendored
View File

@@ -1,3 +1,4 @@
# autotools
Makefile
Makefile.in
aclocal.m4
@@ -17,31 +18,54 @@ ltmain.sh
missing
osmo-ggsn.spec
stamp-h1
doc/Makefile.in
ggsn/Makefile.in
gtp/Makefile.in
sgsnemu/Makefile.in
INSTALL
m4/
Makefile
osmo-ggsn-*.tar*
.version
.tarball-version
# debian
debian/osmo-ggsn/
debian/*.debhelper
debian/libgtp/
debian/libgtp1
debian/osmo-ggsn-dbg
debian/*.log
INSTALL
debian/autoreconf.*
debian/*.substvars
debian/tmp/
sgsnemu/sgsnemu
debian/files
debian/libgtp-dev/
# programs / libraries
sgsnemu/sgsnemu
libgtp.pc
ggsn/osmo-ggsn
m4/
*.swp
# compiler results
*.o
*.a
*.la
*.lo
*.pyc
.deps
.libs
# misc
*.swp
.dirstamp
.deps
*.orig
*.new
*.rej
*/.deps
*/.libs
*/Makefile
*~
osmo-ggsn.cfg
sgsnemu.pid
gsn_restart
# testsuite
tests/atconfig
tests/*/*_test
tests/testsuite
tests/testsuite.log
tests/package.m4

View File

@@ -1,3 +1,3 @@
[gerrit]
host=gerrit.osmocom.org
project=openggsn
project=osmo-ggsn

View File

@@ -1,5 +1,5 @@
## Process this file with automake to produce Makefile.in
SUBDIRS = lib gtp ggsn sgsnemu doc
SUBDIRS = lib gtp ggsn sgsnemu doc tests
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgtp.pc

View File

@@ -263,8 +263,7 @@ following:
1. Install sgsnemu on a Linux Box. See under installation above.
2. Connect your Linux box with sgsnemu installed to the GPRS core
network. Use the same LAN switch as the one your SGSN is connected
to. You also need a free IP address that can be used by sgsnemu.
network. You also need a free IP address that can be used by sgsnemu.
3. You need to configure networking in terms of interface address,
subnet mask and default route. See the Linux Networking HOWTO for
details.

View File

@@ -7,6 +7,7 @@ AM_CONFIG_HEADER([config.h])
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_TESTDIR(tests)
AC_CANONICAL_SYSTEM
dnl kernel style compile messages
@@ -17,7 +18,6 @@ AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_AWK
AC_PROG_CPP
AC_PROG_CXX
LT_INIT
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
@@ -65,7 +65,7 @@ AC_ARG_ENABLE([gtp-linux],
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
AS_IF([test "x$enable_gtp_linux" = "xyes"], [
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0])
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.2.0])
])
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
@@ -135,9 +135,42 @@ adl_FUNC_GETOPT_LONG
AM_INIT_AUTOMAKE([foreign])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
[Compile with address sanitizer enabled],
)],
[sanitize=$enableval], [sanitize="no"])
if test x"$sanitize" = x"yes"
then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
AC_ARG_ENABLE(werror,
[AS_HELP_STRING(
[--enable-werror],
[Turn all compiler warnings into errors, with exceptions:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
]
)],
[werror=$enableval], [werror="no"])
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_CONFIG_FILES([Makefile
doc/Makefile
@@ -149,6 +182,8 @@ AC_CONFIG_FILES([Makefile
po/Makefile
sgsnemu/Makefile
tests/Makefile
tests/lib/Makefile
tests/gtp/Makefile
libgtp.pc
osmo-ggsn.spec])
AC_OUTPUT

View File

@@ -14,9 +14,13 @@ deps="$base/deps"
inst="$deps/install"
export deps inst
mkdir "$deps" || true
rm -rf "$inst"
osmo-clean-workspace.sh
mkdir "$deps" || true
if [ "x$GTP" == "x--enable-gtp-linux" ]; then
osmo-build-dep.sh libgtpnl
fi
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -28,12 +32,14 @@ set +x
echo
echo
echo
echo " =============================== openggsn ==============================="
echo " =============================== OsmoGGSN ==============================="
echo
set -x
cd "$base"
autoreconf --install --force
./configure
./configure --enable-sanitize --enable-werror $GTP
$MAKE $PARALLEL_MAKE
$MAKE distcheck
osmo-clean-workspace.sh

View File

@@ -1,11 +1,11 @@
[Unit]
Description=OpenGGSN
Description=OsmoGGSN
After=networking.service
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/ggsn -c /etc/ggsn.conf -f
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
RestartSec=2
RestartPreventExitStatus=1

145
debian/changelog vendored
View File

@@ -1,3 +1,148 @@
osmo-ggsn (1.2.2+ow2) unstable; urgency=medium
* Release version for On-Waves
* From commit ee44b82b967929eaf8867d967a22428972b58d0a
-- Daniel Willmann <dwillmann@sysmocom.de> Fri, 31 Aug 2018 18:02:47 +0200
osmo-ggsn (1.2.2+ow1) unstable; urgency=medium
* Release new version for On-Waves
* commit b673d1c438488fb74abda344e563d733e5ce451a
-- Daniel Willmann <dwillmann@sysmocom.de> Fri, 31 Aug 2018 18:02:02 +0200
osmo-ggsn (1.2.2) unstable; urgency=medium
[ Vadim Yanitskiy ]
* ggsn_vty.c: fix: use CONFIG_NODE as parent by default
[ Philipp Maier ]
* ggsn: fix misinterpreted length field in ipcp_contains_option()
* ggsn: make sure ipcp_option_hdr and and ipcp_hdr are packed
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 31 May 2018 12:44:54 +0200
osmo-ggsn (1.2.1) unstable; urgency=medium
* debian/rules: Fix debian packaging after 1.2.0 release
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 04 May 2018 12:19:58 +0200
osmo-ggsn (1.2.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* fix compiler warnings: return 0 in main(), in 3 tests
* add --enable-sanitize config option
* sanitize build: ensure uint16/32 alignment in gtpie_test and in46a_test
* configure: add --enable-werror
* jenkins.sh: use --enable-werror configure flag, not CFLAGS
[ Harald Welte ]
* sgsnemu: Don't leak FILE handle in proc_read()
* sgsnemu: Fix format string in printing tun-device name
* sgsnemu: Make sure buffer has space for terminating-NUL
* sgsnemu: Free strings in error path
* gtp: Fix buffer overflow in imsi_gtp2str()
* gtp: Explicit OSMO_ASSERT to ensure pdp variable is set
* tun: Don't copy 16byte IPv6 address to 'struct in_addr'
* ippool: Correctly compute size of static pool
* remove unused argument to alloc_ippool_blacklist()
* factor out netdev_ip_local_get() from tun_ip_local_get()
* Properly NULL-out blacklist in alloc_ippool_blacklist()
* gtp_kernel: Change gtp_kernel_init() function signature
* gtp-kernel: Re-add support for kernel GTP-U acceleration
* gtp-kernel: Get rid of hard-coded kernel GTP device name
* gtp-kernel: shut down kernel GTP device in apn_down()
* gtp-kernel: Align logging for APN start in kernel-gtp case with that of TUN
* gtp-kernel: Avoid global state variable
* gtp-kernel: Make sure repeated calls to gtp_kernel_init() are safe
* gtp-kernel: proper cleanup in error path
* gtp-kernel: Get rid of SYS_ERR where not applicable
* gtp-kernel: Add function name to pdp_debug() function calls
* gtp-kernel: Add device nime in pdp_debug() log statements
* contrib/jenkins.sh: Allow jenkins job to specify if kernel GTP is used
* ggsn.c: Fix byte order of IPCP IPv4 DNS servers
* ggsn: Ignore PCO with length 0, don't abort processing
* README.md: Remove misleading sentence on sgsnemu
* Add talloc context introspection via VTY
* fix segfault in case of kernel gtp-u
* lib/tun.c: Generalize tun_sifflags() to netdev_sifflags
* lib/tun.c: generalize tun_*route() to netdev_*route()
* lib/tun.c: Generalize tun_{set,add}addr*() functions
* lib/tun: split generic network device related stuff to lib/netdev
* lib/netdev.c: Cosmetic changes (coding style / cleanups)
* ggsn: Don't explicitly use tun_setaddr() API anymore
* sgsnemu: Convert from tun_setaddr() to tun_addaddr()
* lib/tun: Remove tun_setaddr() API, as everyone is using tun_addaddr() now
* Move kernel GTP support from ggsn/ to lib/
* ggsn: don't use gtp_kernel_tunnel_{add,del}() for userspace tun
[ Pau Espin Pedrol ]
* ggsn_vty: Stop using deprecated API vty_install_default
* contrib/jenkins.sh: Enable Werror in C(PP)FLAGS
* examples: Add secondary ipv6 google DNS to osmo-ggsn.cfg
* tun_setaddr6: Fix log typo
* cosmetic: Reorder tun_addaddr to get rid of decl of tun_setaddr4
* ggsn.c: Print version of unhandled ip packet
* Remove unused empty src/Makefile.in
* tests: Split ipv6 specific tests into a new test group
* Add support for IPv4v6 End User Addresses
* contrib: jenkins.sh: Build libgtpnl as dep when building with gtp kernel support
* cosmetic: sgsnemu.c: Fix trailing whitespace
* ggsn.c: Improve logging info on link-local ipv6 addr not found
* tun.c: tun_addaddr: Fix segfault and wrong usage of tun_nlattr
* Set tun_addaddr ipv agnostic and add support for ipv6
* ggsn: Add 'ipv6 link-local' vty cmd
* ggsn_vty.c: Print ipv6 link-local cmd when writing config to file
* gtp.c: Fix trailing whitespace
* gtp.c: Determine GTP version from header
* gtp.c: Log unsupported GTP version number
* gtp/pdp: Fix trailing whitespace
* gtp/pdp: Remove unused APIs pdp_ntoeua pdp_euaton
* gtp.c: gtp_gpdu_ind: Convert ifelse to switch statement
* gtp.c: gtp_gpdu_ind: Early return to avoid use of uninitialized var
* gtp/gtp.c: Remove unused function char2ul_t
* gtp/gtp.c: Mark non exported functions as static
* gtp/gtp.c: Use uint8_t for version param in static functions
* ggsn: encaps_tun: Avoid forwarding packet if EUA is unassigned, fix crash
* ggsn: Validate packet src addr from MS
* ggsn: Parse PCO_IPCP
* ggsn: Parse PCO_IPCP for IPv4v6 pdp ctx
* ggsn: Print all addresses on successful pdp ctx creation
* ggsn.c: cb_tun_ind: Convert ifelse to switch statement
* ggsn.c: cb_tun_ind: log dst addr of packet without pdp ctx
* ggsn.c: cb_tun_ind: Don't drop packets targeting pdp ctx ll addr
* sgsnemu: Fix bad ptr during context deallocation
* sgsnemu: listen param is a host, not an interface
* use osmo_init_logging2
[ Max ]
* Log APN and tun names for packets
* Enable sanitize for CI tests
* Fix stow-enabled jenkins build failure
* Add GTP message names
[ Viktor Tsymbalyuk ]
* sgsnemu: sgsnemu stopped after recieving "Request accepted" from ggsn
* sgsnemu: created "pinghost" and "createif" modes for mutual exclusion
* sgsnemu: fix: no outgoing GTP-U in "createif" mode
[ Martin Hauke ]
* build: Remove AC_PROG_CXX, C++ is never used
[ Stefan Sperling ]
* remove the -f option from osmo-ggsn.service
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:05:27 +0200
osmo-ggsn (1.1.0) unstable; urgency=medium
* libgtp: pdp.h: Addition of new tx_gpdu_seq struct member member
* libgtp: pdp.h: add LOGPDPX() helper to public API
-- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 19:00:23 +0200
osmo-ggsn (1.0.0) unstable; urgency=medium
* Transition to OsmoGGSN

23
debian/control vendored
View File

@@ -22,7 +22,7 @@ Description: Osmocom Gateway GPRS Support Node (GGSN)
operators as the interface between the Internet and the rest of the
mobile network infrastructure.
Package: libgtp1
Package: libgtp3
Architecture: any
Multi-Arch: same
Section: libs
@@ -41,7 +41,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
libgtp1 (= ${binary:Version})
libgtp3 (= ${binary:Version})
Description: Development files for libgtp
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
operators as the interface between the Internet and the rest of the
@@ -54,6 +54,23 @@ Package: osmo-ggsn-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp1 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp3 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
Multi-Arch: same
Description: Debug symbols for OsmoGGSN
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
operators as the interface between the Internet and the rest of the
mobile network infrastructure.
Package: libgtp-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp3 (= ${binary:Version})
Multi-Arch: same
Description: Debug symbols for OsmoGGSN
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
operators as the interface between the Internet and the rest of the
mobile network infrastructure.
.
The library libgtp implements the GTP protocol between SGSN and GGSN
and this package contains the development files for this library.

1
debian/osmo-ggsn.service vendored Symbolic link
View File

@@ -0,0 +1 @@
../contrib/osmo-ggsn.service

7
debian/rules vendored
View File

@@ -15,8 +15,5 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
dh $@ --with autoreconf
override_dh_strip:
dh_strip --dbg-package=osmo-ggsn-dbg
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
dh_strip -plibgtp3 --dbg-package=libgtp-dbg

View File

@@ -53,6 +53,7 @@ ggsn ggsn0
type-support v6
ipv6 prefix dynamic 2001:780:44:2000:0:0:0:0/56
ipv6 dns 0 2001:4860:4860::8888
ipv6 dns 1 2001:4860:4860::8844
ipv6 ifconfig 2001:780:44:2000:0:0:0:0/56
no shutdown
apn inet46
@@ -65,6 +66,7 @@ ggsn ggsn0
ip ifconfig 176.16.46.0/24
ipv6 prefix dynamic 2001:780:44:2100:0:0:0:0/56
ipv6 dns 0 2001:4860:4860::8888
ipv6 dns 1 2001:4860:4860::8844
ipv6 ifconfig 2001:780:44:2100:0:0:0:0/56
no shutdown
default-apn internet

View File

@@ -7,13 +7,9 @@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb
osmo_ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
if ENABLE_GTP_KERNEL
AM_CFLAGS += -DGTP_KERNEL
osmo_ggsn_LDADD += -lgtpnl
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
endif
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
if ENABLE_GTP_KERNEL
osmo_ggsn_SOURCES += gtp-kernel.c
endif
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h

View File

@@ -56,15 +56,16 @@
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/gsm/apn.h>
#include "../lib/tun.h"
#include "../lib/ippool.h"
#include "../lib/syserr.h"
#include "../lib/in46_addr.h"
#include "../lib/gtp-kernel.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
#include "gtp-kernel.h"
#include "icmpv6.h"
#include "ggsn.h"
@@ -83,8 +84,7 @@ struct ul255_t apn;
#define LOGPGGSN(level, ggsn, fmt, args...) \
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
#define LOGPPDP(level, pdp, fmt, args...) \
LOGP(DGGSN, level, "PDP(%s:%u): " fmt, imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi, ## args)
#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
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);
@@ -113,9 +113,6 @@ static void pool_close_all_pdp(struct ippool_t *pool)
int apn_stop(struct apn_ctx *apn, bool force)
{
if (!apn->started)
return 0;
LOGPAPN(LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
/* check if pools have any active PDP contexts and bail out */
pool_close_all_pdp(apn->v4.pool);
@@ -128,9 +125,11 @@ int apn_stop(struct apn_ctx *apn, bool force)
LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
}
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
/* release tun device */
LOGPAPN(LOGL_INFO, apn, "Closing TUN device\n");
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;
}
@@ -150,9 +149,56 @@ int apn_stop(struct apn_ctx *apn, bool force)
return 0;
}
static int alloc_ippool_blacklist(struct apn_ctx *apn, struct in46_prefix **blacklist, bool ipv6)
{
int flags, len, len2, i;
*blacklist = NULL;
if (ipv6)
flags = IP_TYPE_IPv6_NONLINK;
else
flags = IP_TYPE_IPv4;
while (1) {
len = netdev_ip_local_get(apn->tun.cfg.dev_name, NULL, 0, flags);
if (len < 1)
return len;
*blacklist = talloc_zero_size(apn, len * sizeof(struct in46_prefix));
len2 = netdev_ip_local_get(apn->tun.cfg.dev_name, *blacklist, len, flags);
if (len2 < 1) {
talloc_free(*blacklist);
*blacklist = NULL;
return len2;
}
if (len2 > len) { /* iface was added between 2 calls, repeat operation */
talloc_free(*blacklist);
*blacklist = NULL;
} else
break;
}
for (i = 0; i < len2; i++)
LOGPAPN(LOGL_INFO, apn, "Blacklist tun IP %s\n",
in46p_ntoa(&(*blacklist)[i]));
return len2;
}
/* actually start the APN with its current config */
int apn_start(struct apn_ctx *apn)
{
int ippool_flags = IPPOOL_NONETWORK | IPPOOL_NOBROADCAST;
struct in46_prefix ipv6_tun_linklocal_ip;
struct in46_prefix *blacklist;
int blacklist_size;
struct gsn_t *gsn = apn->ggsn->gsn;
int rc;
if (apn->started)
return 0;
@@ -160,7 +206,7 @@ int apn_start(struct apn_ctx *apn)
switch (apn->cfg.gtpu_mode) {
case APN_GTPU_MODE_TUN:
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name)) {
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
return -1;
}
@@ -172,11 +218,42 @@ int apn_start(struct apn_ctx *apn)
/* 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, false);
return -1;
}
if (gsn == NULL) {
/* skip bringing up the APN now if the GSN is not initialized yet.
* This happens during initial load of the config file, as the
* "no shutdown" in the ggsn node only happens after the "apn" nodes
* are brought up */
LOGPAPN(LOGL_NOTICE, apn, "Skipping APN start\n");
return 0;
}
/* use GTP kernel module for data packet encapsulation */
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
return -1;
}
break;
default:
LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
return -1;
}
/* common initialization below */
/* set back-pointer from TUN device to APN */
apn->tun.tun->priv = apn;
if (apn->v4.cfg.ifconfig_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
if (tun_setaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
@@ -188,58 +265,80 @@ 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_setaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s\n",
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
apn_stop(apn, false);
return -1;
}
}
if (apn->v6.cfg.ll_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 link-local address %s\n",
in46p_ntoa(&apn->v6.cfg.ll_prefix));
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr, NULL,
apn->v6.cfg.ll_prefix.prefixlen)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
apn_stop(apn, false);
return -1;
}
apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
}
if (apn->tun.cfg.ipup_script) {
LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
apn->tun.cfg.ipup_script);
tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
}
/* set back-pointer from TUN device to APN */
apn->tun.tun->priv = apn;
break;
case APN_GTPU_MODE_KERNEL_GTP:
LOGPAPN(LOGL_ERROR, apn, "FIXME: Kernel GTP\n");
#if 0
/* use GTP kernel module for data packet encapsulation */
if (gtp_kernel_init(gsn, &net.v4, prefixlen, net_arg) < 0)
goto err;
#endif
break;
default:
LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
apn->v6.cfg.ll_prefix.addr.len == 0) {
rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
if (rc < 1) {
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
rc ? strerror(errno) : "tun interface has no link-local IP assigned");
apn_stop(apn, false);
return -1;
}
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
}
/* Create IPv4 pool */
if (apn->v4.cfg.dynamic_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
in46p_ntoa(&apn->v4.cfg.dynamic_prefix));
if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, false)) < 0)
LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv4 tun IPs\n");
if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix,
&apn->v4.cfg.static_prefix, 0)) {
&apn->v4.cfg.static_prefix, ippool_flags,
blacklist, blacklist_size)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
talloc_free(blacklist);
apn_stop(apn, false);
return -1;
}
talloc_free(blacklist);
}
/* Create IPv6 pool */
if (apn->v6.cfg.dynamic_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n",
in46p_ntoa(&apn->v6.cfg.dynamic_prefix));
if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, true)) < 0)
LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv6 tun IPs\n");
if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix,
&apn->v6.cfg.static_prefix, 0)) {
&apn->v6.cfg.static_prefix, ippool_flags,
blacklist, blacklist_size)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
talloc_free(blacklist);
apn_stop(apn, false);
return -1;
}
talloc_free(blacklist);
}
LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
@@ -266,27 +365,71 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
static int delete_context(struct pdp_t *pdp)
{
struct gsn_t *gsn = pdp->gsn;
struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
struct apn_ctx *apn = pdp->priv;
struct ippoolm_t *member;
int i;
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
struct ippoolm_t *member = pdp->peer;
if (pdp->peer) {
for (i = 0; i < 2; i++) {
if (pdp->peer[i]) {
member = pdp->peer[i];
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
ippool_freeip(ipp->pool, ipp);
} else
ippool_freeip(member->pool, member);
} else if(i == 0)
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
}
if (gtp_kernel_tunnel_del(pdp)) {
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
strerror(errno));
}
}
return 0;
}
#include <osmocom/gsm/tlv.h>
/* RFC 1332 */
enum ipcp_options {
IPCP_OPT_IPADDR = 3,
IPCP_OPT_PRIMARY_DNS = 129,
IPCP_OPT_SECONDARY_DNS = 131,
};
struct ipcp_option_hdr {
uint8_t type;
uint8_t len;
uint8_t data[0];
} __attribute__ ((packed));
struct ipcp_hdr {
uint8_t code;
uint8_t id;
uint16_t len;
uint8_t options[0];
} __attribute__ ((packed));
/* determine if IPCP contains given option */
static uint8_t *ipcp_contains_option(uint8_t *ipcp, size_t ipcp_len, enum ipcp_options opt, size_t opt_minlen)
{
uint8_t *cur_opt = ipcp + sizeof(struct ipcp_hdr);
/* iterate over Options and check if protocol contained */
while (cur_opt + 2 <= ipcp + ipcp_len) {
uint8_t type = cur_opt[0];
uint8_t len = cur_opt[1]; /* length value includes 2 bytes type/length */
if (len < 2)
return NULL;
if (type == opt && len >= 2 + opt_minlen)
return cur_opt;
cur_opt += len;
}
return NULL;
}
/* 3GPP TS 24.008 10.6.5.3 */
enum pco_protocols {
PCO_P_LCP = 0xC021,
@@ -320,59 +463,88 @@ enum pco_protocols {
};
/* determine if PCO contains given protocol */
static bool pco_contains_proto(struct ul255_t *pco, uint16_t prot)
static uint8_t *pco_contains_proto(struct ul255_t *pco, size_t offset, uint16_t prot, size_t prot_minlen)
{
uint8_t *cur = pco->v + 1;
uint8_t *cur = pco->v + 1 + offset;
/* iterate over PCO and check if protocol contained */
while (cur + 3 <= pco->v + pco->l) {
uint16_t cur_prot = osmo_load16be(cur);
uint8_t cur_len = cur[2];
if (cur_prot == prot)
return true;
if (cur_len == 0)
break;
if (cur_prot == prot && cur_len >= prot_minlen)
return cur;
cur += cur_len + 3;
}
return false;
return NULL;
}
/* determine if PDP context has IPv6 support */
static bool pdp_has_v4(struct pdp_t *pdp)
{
if (pdp->eua.l == 4+2)
return true;
else
return false;
/*! Get the peer of pdp based on IP version used.
* \param[in] pdp PDP context to select the peer from.
* \param[in] v4v6 IP version to select. Valid values are 4 and 6.
* \returns The selected peer matching the given IP version. NULL if not present.
*/
static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
uint8_t len1, len2, i;
if (is_ipv6) {
len1 = 8;
len2 = 16;
} else {
len1 = sizeof(struct in_addr);
len2 = len1;
}
/* construct an IPCP PCO from up to two given DNS addreses */
static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *dns1,
const struct in46_addr *dns2)
for (i = 0; i < 2; i++) {
struct ippoolm_t * ippool = pdp->peer[i];
if (ippool && (ippool->addr.len == len1 || ippool->addr.len == len2))
return ippool;
}
return NULL;
}
/* construct an IPCP PCO response from request*/
static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg)
{
uint8_t *len1, *len2;
uint8_t *start = msg->tail;
const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
uint8_t *ipcp;
uint16_t ipcp_len;
uint8_t *len1, *len2, *pco_ipcp;
unsigned int len_appended;
ptrdiff_t consumed;
size_t remain, offset = 0;
/* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
while (pco_ipcp) {
uint8_t *start = msg->tail;
ipcp = (pco_ipcp + 3); /* 2=type + 1=len */
consumed = (ipcp - &pdp->pco_req.v[0]);
remain = sizeof(pdp->pco_req.v) - consumed;
ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */
if (remain < 0 || remain < ipcp_len)
return;
/* Three byte T16L header */
msgb_put_u16(msg, 0x8021); /* IPCP */
len1 = msgb_put(msg, 1); /* Length of contents: delay */
msgb_put_u8(msg, 0x02); /* ACK */
msgb_put_u8(msg, id); /* ID: Needs to match request */
msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */
msgb_put_u8(msg, 0x00); /* Length MSB */
len2 = msgb_put(msg, 1); /* Length LSB: delay */
if (dns1 && dns1->len == 4) {
if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
msgb_put_u8(msg, 0x81); /* DNS1 Tag */
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
msgb_put_u32(msg, dns1->v4.s_addr);
msgb_put_u32(msg, ntohl(dns1->v4.s_addr));
}
if (dns2 && dns2->len == 4) {
if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
msgb_put_u8(msg, 0x83); /* DNS2 Tag */
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
msgb_put_u32(msg, dns2->v4.s_addr);
msgb_put_u32(msg, ntohl(dns2->v4.s_addr));
}
/* patch in length values */
@@ -380,25 +552,26 @@ static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *
*len1 = len_appended - 3;
*len2 = len_appended - 3;
return 0;
offset += 3 + ipcp_len;
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
}
}
/* process one PCO request from a MS/UE, putting together the proper responses */
static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
{
struct msgb *msg = msgb_alloc(256, "PCO");
struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
unsigned int i;
OSMO_ASSERT(msg);
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
/* FIXME: also check if primary / secondary DNS was requested */
if (pdp_has_v4(pdp) && pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)) {
/* FIXME: properly implement this for IPCP */
build_ipcp_pco(msg, 0, &apn->v4.cfg.dns[0], &apn->v4.cfg.dns[1]);
}
if (peer_v4)
build_ipcp_pco(apn, pdp, msg);
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR)) {
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv6_ADDR, 0)) {
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
struct in46_addr *i46a = &apn->v6.cfg.dns[i];
if (i46a->len != 16)
@@ -407,7 +580,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
}
}
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv4_ADDR)) {
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv4_ADDR, 0)) {
for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
struct in46_addr *i46a = &apn->v4.cfg.dns[i];
if (i46a->len != 4)
@@ -425,15 +598,30 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
msgb_free(msg);
}
static bool apn_supports_ipv4(const struct apn_ctx *apn)
{
if (apn->v4.cfg.static_prefix.addr.len || apn->v4.cfg.dynamic_prefix.addr.len)
return true;
return false;
}
static bool apn_supports_ipv6(const struct apn_ctx *apn)
{
if (apn->v6.cfg.static_prefix.addr.len || apn->v6.cfg.dynamic_prefix.addr.len)
return true;
return false;
}
int create_context_ind(struct pdp_t *pdp)
{
static char name_buf[256];
struct gsn_t *gsn = pdp->gsn;
struct ggsn_ctx *ggsn = gsn->priv;
struct in46_addr addr;
struct ippoolm_t *member;
struct in46_addr addr[2];
struct ippoolm_t *member = NULL, *addrv4 = NULL, *addrv6 = NULL;
char straddrv4[INET_ADDRSTRLEN], straddrv6[INET6_ADDRSTRLEN];
struct apn_ctx *apn;
int rc;
int rc, num_addr, i;
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
@@ -441,9 +629,17 @@ int create_context_ind(struct pdp_t *pdp)
/* First find an exact APN name match */
apn = ggsn_find_apn(ggsn, name_buf);
/* ignore if the APN has not been started */
if (apn && !apn->started)
apn = NULL;
/* then try default (if any) */
if (!apn)
apn = ggsn->cfg.default_apn;
/* ignore if the APN has not been started */
if (apn && !apn->started)
apn = NULL;
if (!apn) {
/* no APN found for what user requested */
LOGPPDP(LOGL_NOTICE, pdp, "Unknown APN '%s', rejecting\n", name_buf);
@@ -460,46 +656,69 @@ int create_context_ind(struct pdp_t *pdp)
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
pdp->qos_neg.l = pdp->qos_req.l;
if (in46a_from_eua(&pdp->eua, &addr)) {
memset(addr, 0, sizeof(addr));
if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
osmo_hexdump(pdp->eua.v, pdp->eua.l));
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
return 0;
}
if (addr.len == sizeof(struct in_addr)) {
rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
/* Allocate dynamic addresses from the pool */
for (i = 0; i < num_addr; i++) {
if (addr[i].len == sizeof(struct in_addr)) {
/* does this APN actually have an IPv4 pool? */
if (!apn_supports_ipv4(apn))
goto err_wrong_af;
rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
if (rc < 0)
goto err_pool_full;
in46a_to_eua(&member->addr, &pdp->eua);
/* copy back */
memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
if (gtp_kernel_tunnel_add(pdp) < 0) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
return 0;
}
} else if (addr.len == sizeof(struct in6_addr)) {
struct in46_addr tmp;
rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
addrv4 = member;
} else if (addr[i].len == sizeof(struct in6_addr)) {
/* does this APN actually have an IPv6 pool? */
if (!apn_supports_ipv6(apn))
goto err_wrong_af;
rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
if (rc < 0)
goto err_pool_full;
/* IPv6 doesn't really send the real/allocated address at this point, but just
* the link-identifier which the MS shall use for router solicitation */
tmp.len = addr.len;
/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
in46a_to_eua(&tmp, &pdp->eua);
memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
addrv6 = member;
} else
OSMO_ASSERT(0);
pdp->peer = member;
pdp->ipif = apn->tun.tun; /* TODO */
pdp->peer[i] = member;
member->peer = pdp;
}
in46a_to_eua(addr, num_addr, &pdp->eua);
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP && apn_supports_ipv4(apn)) {
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
return 0;
}
}
pdp->ipif = apn->tun.tun; /* TODO */
pdp->priv = apn;
/* TODO: change trap to send 2 IPs */
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
return 0;
@@ -507,8 +726,13 @@ int create_context_ind(struct pdp_t *pdp)
process_pco(apn, pdp);
LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IP=%s\n",
name_buf, apn->cfg.name, pdp->teic_own, in46a_ntoa(&member->addr));
/* Transmit G-PDU sequence numbers (only) if configured in APN */
pdp->tx_gpdu_seq = apn->cfg.tx_gpdu_seq;
LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IPv4=%s, IPv6=%s\n",
name_buf, apn->cfg.name, pdp->teic_own,
addrv4 ? inet_ntop(AF_INET, &addrv4->addr.v4, straddrv4, sizeof(straddrv4)) : "none",
addrv6 ? inet_ntop(AF_INET6, &addrv6->addr.v6, straddrv6, sizeof(straddrv6)) : "none");
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
return 0; /* Success */
@@ -516,6 +740,11 @@ err_pool_full:
LOGPPDP(LOGL_ERROR, pdp, "Cannot allocate IP address from pool (full!)\n");
gtp_create_context_resp(gsn, pdp, -rc);
return 0; /* Already in use, or no more available */
err_wrong_af:
LOGPPDP(LOGL_ERROR, pdp, "APN doesn't support requested EUA / AF type\n");
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
return 0;
}
/* Internet-originated IP packet, needs to be sent via GTP towards MS */
@@ -527,23 +756,31 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
struct ippool_t *pool;
char straddr[INET6_ADDRSTRLEN];
uint8_t pref_offset;
if (iph->version == 4) {
switch (iph->version) {
case 4:
if (len < sizeof(*iph) || len < 4*iph->ihl)
return -1;
dst.len = 4;
dst.v4.s_addr = iph->daddr;
pool = apn->v4.pool;
} else if (iph->version == 6) {
break;
case 6:
/* Due to the fact that 3GPP requires an allocation of a
* /64 prefix to each MS, we must instruct
* ippool_getip() below to match only the leading /64
* prefix, i.e. the first 8 bytes of the address */
* prefix, i.e. the first 8 bytes of the address. If the ll addr
* is used, then the match should be done on the trailing 64
* bits. */
dst.len = 8;
dst.v6 = ip6h->ip6_dst;
pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
memcpy(&dst.v6, ((uint8_t*)&ip6h->ip6_dst) + pref_offset, 8);
pool = apn->v6.pool;
} else {
LOGP(DTUN, LOGL_NOTICE, "non-IPv packet received from tun\n");
break;
default:
LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
return -1;
}
@@ -551,12 +788,15 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
if (!pool)
return 0;
DEBUGP(DTUN, "Received packet from tun!\n");
DEBUGP(DTUN, "Received packet for APN(%s) from tun %s", apn->cfg.name, tun->devname);
if (ippool_getip(pool, &ipm, &dst)) {
DEBUGP(DTUN, "Received packet with no PDP contex!!\n");
DEBUGPC(DTUN, " with no PDP contex! (%s)\n", iph->version == 4 ?
inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)) :
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
return 0;
}
DEBUGPC(DTUN, "\n");
if (ipm->peer) /* Check if a peer protocol is defined */
gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
@@ -573,16 +813,55 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
{
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
struct tun_t *tun = (struct tun_t *)pdp->ipif;
struct apn_ctx *apn = tun->priv;
char straddr[INET6_ADDRSTRLEN];
struct ippoolm_t *peer;
uint8_t pref_offset;
LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n");
OSMO_ASSERT(tun);
OSMO_ASSERT(apn);
LOGPPDP(LOGL_DEBUG, pdp, "Packet received on APN(%s): forwarding to tun %s\n", apn->cfg.name, tun->devname);
switch (iph->version) {
case 6:
peer = pdp_get_peer_ipv(pdp, true);
if (!peer) {
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv6 with unassigned EUA: %s\n",
osmo_hexdump(pack, len));
return -1;
}
/* Validate packet comes from IPaddr assigned to the pdp ctx.
If packet is a LL addr, then EUA is in the lower 64 bits,
otherwise it's used as the 64 prefix */
pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ? 8 : 0;
if (memcmp(((uint8_t*)&ip6h->ip6_src) + pref_offset, &peer->addr.v6, 8)) {
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv6: %s\n",
inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
return -1;
}
/* daddr: all-routers multicast addr */
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
return handle_router_mcast(pdp->gsn, pdp, pack, len);
return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
&apn->v6_lladdr, pack, len);
break;
case 4:
peer = pdp_get_peer_ipv(pdp, false);
if (!peer) {
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv4 with unassigned EUA: %s\n",
osmo_hexdump(pack, len));
return -1;
}
/* Validate packet comes from IPaddr assigned to the pdp ctx */
if (memcmp(&iph->saddr, &peer->addr.v4, sizeof(peer->addr.v4))) {
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv4: %s\n",
inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)));
return -1;
}
break;
default:
LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
@@ -693,6 +972,14 @@ int ggsn_start(struct ggsn_ctx *ggsn)
}
ggsn->gsn->priv = ggsn;
/* patch in different addresses to use (in case we're behind NAT, the listen
* address is different from what we advertise externally) */
if (ggsn->cfg.gtpc_addr.v4.s_addr)
ggsn->gsn->gsnc = ggsn->cfg.gtpc_addr.v4;
if (ggsn->cfg.gtpu_addr.v4.s_addr)
ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4;
/* Register File Descriptors */
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
rc = osmo_fd_register(&ggsn->gtp_fd0);
@@ -806,6 +1093,7 @@ int main(int argc, char **argv)
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
g_vty_info.tall_ctx = tall_ggsn_ctx;
/* Handle keyboard interrupt SIGINT */
signal(SIGINT, &signal_handler);
@@ -815,11 +1103,12 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&log_info);
osmo_init_logging2(tall_ggsn_ctx, &log_info);
osmo_stats_init(tall_ggsn_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds(NULL);
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds(&log_info);
ggsn_vty_init();
ctrl_vty_init(tall_ggsn_ctx);
@@ -838,7 +1127,8 @@ int main(int argc, char **argv)
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(),
OSMO_CTRL_PORT_GGSN, NULL);
if (!g_ctrlh) {
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
exit(1);

View File

@@ -22,6 +22,7 @@ struct ggsn_ctx;
struct apn_ctx_ip {
struct {
struct in46_prefix ifconfig_prefix;
struct in46_prefix ll_prefix;
struct in46_prefix static_prefix;
struct in46_prefix dynamic_prefix;
/* v4 DNS server names */
@@ -63,6 +64,8 @@ struct apn_ctx {
enum apn_gtpu_mode gtpu_mode;
/* administratively shut-down (true) or not (false) */
bool shutdown;
/* transmit G-PDU sequeence numbers (true) or not (false) */
bool tx_gpdu_seq;
} cfg;
/* corresponding tun device */
@@ -78,6 +81,9 @@ struct apn_ctx {
struct osmo_fd fd;
} tun;
/* ipv6 link-local address */
struct in6_addr v6_lladdr;
struct apn_ctx_ip v4;
struct apn_ctx_ip v6;
};
@@ -99,6 +105,10 @@ struct ggsn_ctx {
struct apn_ctx *default_apn;
/* ADdress to which we listen for GTP */
struct in46_addr listen_addr;
/* Local GTP-C address advertised in GTP */
struct in46_addr gtpc_addr;
/* Local GTP-U address advertised in GTP */
struct in46_addr gtpu_addr;
/* directory for state file */
char *state_dir;
/* administratively shut-down (true) or not (false) */

View File

@@ -103,6 +103,7 @@ struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
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);
@@ -165,8 +166,8 @@ DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
"gtp local-ip A.B.C.D",
DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
"gtp bind-ip A.B.C.D",
"GTP Parameters\n"
"Set the IP address for the local GTP bind\n"
"IPv4 Address\n")
@@ -179,6 +180,34 @@ DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
"gtp control-ip A.B.C.D",
"GTP Parameters\n"
"Set the IP address states as local IP in GTP-C messages\n"
"IPv4 Address\n")
{
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
size_t t;
ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
return CMD_SUCCESS;
}
DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
"gtp user-ip A.B.C.D",
"GTP Parameters\n"
"Set the IP address states as local IP in GTP-U messages\n"
"IPv4 Address\n")
{
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
size_t t;
ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
return CMD_SUCCESS;
}
DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
"gtp state-dir PATH",
"GTP Parameters\n"
@@ -484,6 +513,24 @@ DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
"ipv6 link-local X:X::X:X/M",
IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
{
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
"no ipv6 link-local",
NO_STR IP6_STR IFCONFIG_STR)
{
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
return CMD_SUCCESS;
}
#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
@@ -530,6 +577,24 @@ DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
"g-pdu tx-sequence-numbers",
"G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
{
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
apn->cfg.tx_gpdu_seq = true;
return CMD_SUCCESS;
}
DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
"no g-pdu tx-sequence-numbers",
NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
{
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
apn->cfg.tx_gpdu_seq = false;
return CMD_SUCCESS;
}
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
"shutdown",
"Put the APN in administrative shut-down\n")
@@ -593,6 +658,9 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
VTY_NEWLINE);
}
if (!apn->cfg.tx_gpdu_seq)
vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
/* IPv4 prefixes + DNS */
if (apn->v4.cfg.static_prefix.addr.len)
vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
@@ -614,10 +682,12 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
if (!apn->v6.cfg.dns[i].len)
continue;
vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
vty_out(vty, " ipv6 dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
}
if (apn->v6.cfg.ifconfig_prefix.addr.len)
vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
if (apn->v6.cfg.ll_prefix.addr.len)
vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
/* must be last */
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
@@ -633,7 +703,11 @@ static int config_write_ggsn(struct vty *vty)
if (ggsn->cfg.description)
vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
vty_out(vty, " gtp local-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
if (ggsn->cfg.gtpc_addr.v4.s_addr)
vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
if (ggsn->cfg.gtpu_addr.v4.s_addr)
vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
llist_for_each_entry(apn, &ggsn->apn_list, list)
config_write_apn(vty, apn);
if (ggsn->cfg.default_apn)
@@ -671,6 +745,8 @@ static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
in46a_from_eua(&pdp->eua, &eua46);
vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
}
DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
@@ -800,13 +876,15 @@ int ggsn_vty_init(void)
install_element(CONFIG_NODE, &cfg_ggsn_cmd);
install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
install_node(&ggsn_node, config_write_ggsn);
vty_install_default(GGSN_NODE);
install_element(GGSN_NODE, &cfg_description_cmd);
install_element(GGSN_NODE, &cfg_no_description_cmd);
install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
install_element(GGSN_NODE, &cfg_ggsn_local_ip_cmd);
install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
@@ -814,7 +892,6 @@ int ggsn_vty_init(void)
install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
install_node(&apn_node, NULL);
vty_install_default(APN_NODE);
install_element(APN_NODE, &cfg_description_cmd);
install_element(APN_NODE, &cfg_no_description_cmd);
install_element(APN_NODE, &cfg_apn_shutdown_cmd);
@@ -836,6 +913,10 @@ int ggsn_vty_init(void)
install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
return 0;
}
@@ -867,6 +948,10 @@ static int ggsn_vty_go_parent(struct vty *vty)
vty->index_sub = &apn->ggsn->cfg.description;
}
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
}
return vty->node;

View File

@@ -1,223 +0,0 @@
#ifdef __linux__
#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
#endif
#include "../config.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <libgtpnl/gtp.h>
#include <libgtpnl/gtpnl.h>
#include <libmnl/libmnl.h>
#include <errno.h>
#include <time.h>
#include "../lib/tun.h"
#include "../lib/syserr.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
#include <libgtpnl/gtp.h>
#include <libgtpnl/gtpnl.h>
#include <libmnl/libmnl.h>
#include "gtp-kernel.h"
static void pdp_debug(struct pdp_t *pdp)
{
int i;
uint64_t teid;
if (!debug)
return;
printf("version %u\n", pdp->version);
if (pdp->version == 0) {
teid = pdp_gettid(pdp->imsi, pdp->nsapi);
printf("flowid %u\n", pdp->flru);
} else {
teid = pdp->teid_gn; /* GTPIE_TEI_DI */
}
printf("teid %llx\n", teid);
printf("address (%u)\n", pdp->eua.l);
/* Byte 0: 0xf1 == IETF */
/* Byte 1: 0x21 == IPv4 */
/* Byte 2-6: IPv4 address */
for (i = 0; i < 6; i++)
printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
printf("\n");
printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
for (i = 0; i < 4; i++)
printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
printf("\n");
}
static struct {
int genl_id;
struct mnl_socket *nl;
bool enabled;
} gtp_nl;
/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
#define GTP_DEVNAME "gtp0"
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
size_t prefixlen, const char *net_arg)
{
if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot create GTP tunnel device: %s\n",
strerror(errno));
return -1;
}
gtp_nl.enabled = true;
gtp_nl.nl = genl_socket_open();
if (gtp_nl.nl == NULL) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot create genetlink socket\n");
return -1;
}
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
if (gtp_nl.genl_id < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot lookup GTP genetlink ID\n");
return -1;
}
if (debug) {
SYS_ERR(DGGSN, LOGL_NOTICE, 0,
"Using the GTP kernel mode (genl ID is %d)\n",
gtp_nl.genl_id);
}
DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
net_arg, GTP_DEVNAME);
if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Cannot add route to reach network %s\n",
net_arg);
}
/* launch script if it is set to bring up the route to reach
* the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
* using native rtnetlink interface given that we know the
* MS network mask, later.
*/
if (ipup) {
char cmd[1024];
int err;
/* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
snprintf(cmd, sizeof(cmd), "%s %s %s",
ipup, GTP_DEVNAME, net_arg);
cmd[sizeof(cmd)-1] = '\0';
err = system(cmd);
if (err < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Failed to launch script `%s'", ipup);
return -1;
}
}
SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
return 0;
}
void gtp_kernel_stop(void)
{
if (!gtp_nl.enabled)
return;
gtp_dev_destroy(GTP_DEVNAME);
}
int gtp_kernel_tunnel_add(struct pdp_t *pdp)
{
struct in_addr ms, sgsn;
struct gtp_tunnel *t;
int ret;
if (!gtp_nl.enabled)
return 0;
pdp_debug(pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
gtp_tunnel_set_version(t, pdp->version);
gtp_tunnel_set_ms_ip4(t, &ms);
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
/* use the TEI advertised by SGSN when sending packets
* towards the SGSN */
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
}
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
return ret;
}
int gtp_kernel_tunnel_del(struct pdp_t *pdp)
{
struct gtp_tunnel *t;
int ret;
if (!gtp_nl.enabled)
return 0;
pdp_debug(pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
gtp_tunnel_set_version(t, pdp->version);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
}
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
return ret;
}
int gtp_kernel_enabled(void)
{
return gtp_nl.enabled;
}

View File

@@ -1,45 +0,0 @@
#ifndef _GTP_KERNEL_H_
#define _GTP_KERNEL_H_
struct gengetopt_args_info;
extern int debug;
extern char *ipup;
#ifdef GTP_KERNEL
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
size_t prefixlen, const char *net_arg);
void gtp_kernel_stop(void);
int gtp_kernel_tunnel_add(struct pdp_t *pdp);
int gtp_kernel_tunnel_del(struct pdp_t *pdp);
int gtp_kernel_enabled(void);
#else
static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
size_t prefixlen, const char *net_arg)
{
SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n");
return -1;
}
static inline void gtp_kernel_stop(void) {}
static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp)
{
return 0;
}
static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp)
{
return 0;
}
static inline int gtp_kernel_enabled(void)
{
return 0;
}
#endif
#endif /* _GTP_KERNEL_H_ */

View File

@@ -179,22 +179,16 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
return true;
}
/* RFC3307 link-local scope multicast address */
static const struct in6_addr my_local_addr = {
.s6_addr = { 0x01,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0xff }
};
/* handle incoming packets to the all-routers multicast address */
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len)
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
const struct in6_addr *pdp_prefix,
const struct in6_addr *own_ll_addr,
const uint8_t *pack, unsigned len)
{
struct ippoolm_t *member = pdp->peer;
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
struct msgb *msg;
OSMO_ASSERT(pdp);
OSMO_ASSERT(member);
if (len < sizeof(*ip6h)) {
LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
return -1;
@@ -222,10 +216,10 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pac
osmo_hexdump(pack, len));
return -1;
}
/* FIXME: Send router advertisement from GGSN link-local
/* Send router advertisement from GGSN link-local
* address to MS link-local address, including prefix
* allocated to this PDP context */
msg = icmpv6_construct_ra(&my_local_addr, &ip6h->ip6_src, &member->addr.v6);
msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, pdp_prefix);
/* Send the constructed RA to the MS */
gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
msgb_free(msg);

View File

@@ -3,4 +3,7 @@
#include "../gtp/gtp.h"
#include "../gtp/pdp.h"
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len);
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
const struct in6_addr *pdp_prefix,
const struct in6_addr *own_ll_addr,
const uint8_t *pack, unsigned len);

View File

@@ -1,7 +1,9 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
LIBVERSION=2:0:0
# If major=current-age is increased, remember to update the dh_strip line in debian/rules!
LIBVERSION=3:0:0
lib_LTLIBRARIES = libgtp.la
include_HEADERS = gtp.h pdp.h gtpie.h
@@ -11,7 +13,3 @@ AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_
libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)

447
gtp/gtp.c
View File

@@ -86,6 +86,51 @@ const char *gtp_version()
return VERSION;
}
const struct value_string gtp_type_names[] = {
{ GTP_ECHO_REQ, "Echo Request" },
{ GTP_ECHO_RSP, "Echo Response" },
{ GTP_NOT_SUPPORTED, "Version Not Supported" },
{ GTP_ALIVE_REQ, "Node Alive Request" },
{ GTP_ALIVE_RSP, "Node Alive Response" },
{ GTP_REDIR_REQ, "Redirection Request" },
{ GTP_REDIR_RSP, "Redirection Response" },
{ GTP_CREATE_PDP_REQ, "Create PDP Context Request" },
{ GTP_CREATE_PDP_RSP, "Create PDP Context Response" },
{ GTP_UPDATE_PDP_REQ, "Update PDP Context Request" },
{ GTP_UPDATE_PDP_RSP, "Update PDP Context Response" },
{ GTP_DELETE_PDP_REQ, "Delete PDP Context Request" },
{ GTP_DELETE_PDP_RSP, "Delete PDP Context Response" },
{ GTP_ERROR, "Error Indication" },
{ GTP_PDU_NOT_REQ, "PDU Notification Request" },
{ GTP_PDU_NOT_RSP, "PDU Notification Response" },
{ GTP_PDU_NOT_REJ_REQ, "PDU Notification Reject Request" },
{ GTP_PDU_NOT_REJ_RSP, "PDU Notification Reject Response" },
{ GTP_SUPP_EXT_HEADER, "Supported Extension Headers Notification" },
{ GTP_SND_ROUTE_REQ, "Send Routeing Information for GPRS Request" },
{ GTP_SND_ROUTE_RSP, "Send Routeing Information for GPRS Response" },
{ GTP_FAILURE_REQ, "Failure Report Request" },
{ GTP_FAILURE_RSP, "Failure Report Response" },
{ GTP_MS_PRESENT_REQ, "Note MS GPRS Present Request" },
{ GTP_MS_PRESENT_RSP, "Note MS GPRS Present Response" },
{ GTP_IDEN_REQ, "Identification Request" },
{ GTP_IDEN_RSP, "Identification Response" },
{ GTP_SGSN_CONTEXT_REQ,"SGSN Context Request" },
{ GTP_SGSN_CONTEXT_RSP,"SGSN Context Response" },
{ GTP_SGSN_CONTEXT_ACK,"SGSN Context Acknowledge" },
{ GTP_FWD_RELOC_REQ, "Forward Relocation Request" },
{ GTP_FWD_RELOC_RSP, "Forward Relocation Response" },
{ GTP_FWD_RELOC_COMPL, "Forward Relocation Complete" },
{ GTP_RELOC_CANCEL_REQ,"Relocation Cancel Request" },
{ GTP_RELOC_CANCEL_RSP,"Relocation Cancel Response" },
{ GTP_FWD_SRNS, "Forward SRNS Context" },
{ GTP_FWD_RELOC_ACK, "Forward Relocation Complete Acknowledge" },
{ GTP_FWD_SRNS_ACK, "Forward SRNS Context Acknowledge" },
{ GTP_DATA_TRAN_REQ, "Data Record Transfer Request" },
{ GTP_DATA_TRAN_RSP, "Data Record Transfer Response" },
{ GTP_GPDU, "G-PDU" },
{ 0, NULL }
};
/* gtp_new */
/* gtp_free */
@@ -145,6 +190,15 @@ int gtp_set_cb_conf(struct gsn_t *gsn,
return 0;
}
static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
struct pdp_t * pdp, uint8_t recovery)
{
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
if (gsn->cb_recovery2)
gsn->cb_recovery2(peer, pdp, recovery);
}
int gtp_set_cb_recovery(struct gsn_t *gsn,
int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
{
@@ -152,7 +206,21 @@ int gtp_set_cb_recovery(struct gsn_t *gsn,
return 0;
}
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
/* cb_recovery()
* pdp may be NULL if Recovery IE was received from a message independent
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
* local setup. In case pdp is known, caller may want to keep that pdp alive to
* handle subsequent msg cb as this specific pdp ctx is still valid according to
* specs.
*/
int gtp_set_cb_recovery2(struct gsn_t *gsn,
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery))
{
gsn->cb_recovery2 = cb_recovery2;
return 0;
}
int gtp_set_cb_data_ind(struct gsn_t *gsn,
int (*cb_data_ind) (struct pdp_t * pdp,
void *pack, unsigned len))
{
@@ -168,7 +236,7 @@ extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
* to hold the packet header.
* returns the length of the header. 0 on error.
**/
static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
static unsigned int get_default_gtp(uint8_t version, uint8_t type, void *packet)
{
struct gtp0_header *gtp0_default = (struct gtp0_header *)packet;
struct gtp1_header_long *gtp1_default =
@@ -192,7 +260,8 @@ static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
/* set to 1 */
/* Currently extension headers are not supported */
memset(gtp1_default, 0, sizeof(struct gtp1_header_long));
gtp1_default->flags = 0x32; /* No extension, enable sequence, no N-PDU */
/* No extension, enable sequence, no N-PDU */
gtp1_default->flags = GTPHDR_F_VER(1) | GTP1HDR_F_GTP1 | GTP1HDR_F_SEQ;
gtp1_default->type = hton8(type);
return GTP1_HEADER_SIZE_LONG;
default:
@@ -210,10 +279,11 @@ static unsigned int get_default_gtp(int version, uint8_t type, void *packet)
static uint16_t get_seq(void *pack)
{
union gtp_packet *packet = (union gtp_packet *)pack;
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (ver == 0) {
return ntoh16(packet->gtp0.h.seq);
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
return ntoh16(packet->gtp1l.h.seq);
} else {
return 0;
@@ -229,7 +299,7 @@ static uint64_t get_tid(void *pack)
{
union gtp_packet *packet = (union gtp_packet *)pack;
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (GTPHDR_F_GET_VER(packet->flags) == 0) { /* Version 0 */
return be64toh(packet->gtp0.h.tid);
}
return 0;
@@ -243,13 +313,14 @@ static uint64_t get_tid(void *pack)
static uint16_t get_hlen(void *pack)
{
union gtp_packet *packet = (union gtp_packet *)pack;
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (ver == 0) { /* Version 0 */
return GTP0_HEADER_SIZE;
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
return GTP1_HEADER_SIZE_LONG;
} else if ((packet->flags & 0xe7) == 0x20) { /* Short version 1 */
} else if (ver == 1 && (packet->flags & 0x07) == 0) { /* Short version 1 */
return GTP1_HEADER_SIZE_SHORT;
} else if (ver == 1) { /* Version 1 with seq/n-pdu/ext */
return GTP1_HEADER_SIZE_LONG;
} else {
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
return 0;
@@ -264,10 +335,11 @@ static uint16_t get_hlen(void *pack)
static uint32_t get_tei(void *pack)
{
union gtp_packet *packet = (union gtp_packet *)pack;
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (ver == 0) { /* Version 0 */
return ntoh16(packet->gtp0.h.flow);
} else if ((packet->flags & 0xe0) == 0x20) { /* Version 1 */
} else if (ver == 1) { /* Version 1 */
return ntoh32(packet->gtp1l.h.tei);
} else {
LOGP(DLGTP, LOGL_ERROR, "Unknown packet flags: 0x%02x\n", packet->flags);
@@ -353,10 +425,11 @@ static uint32_t get_tei(void *pack)
* a predefined timeout.
*************************************************************/
int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
static int gtp_req(struct gsn_t *gsn, uint8_t version, struct pdp_t *pdp,
union gtp_packet *packet, int len,
struct in_addr *inetaddr, void *cbp)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
struct sockaddr_in addr;
struct qmsg_t *qmsg;
int fd;
@@ -368,7 +441,7 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
addr.sin_len = sizeof(addr);
#endif
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
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);
@@ -382,7 +455,7 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
else if (pdp)
packet->gtp0.h.flow = hton16(pdp->flrc);
fd = gsn->fd0;
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
} else if (ver == 1 && (packet->flags & GTP1HDR_F_SEQ)) { /* Version 1 with seq */
addr.sin_port = htons(GTP1C_PORT);
packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
packet->gtp1l.h.seq = hton16(gsn->seq_next);
@@ -427,15 +500,15 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
* Remove signalling packet from retransmission queue.
* return 0 on success, EOF if packet was not found */
int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
static int gtp_conf(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
union gtp_packet *packet, int len, uint8_t * type, void **cbp)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
uint16_t seq;
if ((packet->gtp0.h.flags & 0xe0) == 0x00)
if (ver == 0)
seq = ntoh16(packet->gtp0.h.seq);
else if ((packet->gtp1l.h.flags & 0xe2) == 0x22)
else if (ver == 1 && (packet->gtp1l.h.flags & GTP1HDR_F_SEQ))
seq = ntoh16(packet->gtp1l.h.seq);
else {
GTP_LOGPKG(LOGL_ERROR, peer, packet, len,
@@ -518,13 +591,14 @@ int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
return 0;
}
int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
struct qmsg_t *qmsg;
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (ver == 0) { /* Version 0 */
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
packet->gtp0.h.seq = hton16(seq);
packet->gtp0.h.tid = htobe64(tid);
@@ -533,7 +607,7 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
packet->gtp0.h.flow = hton16(pdp->flru);
else if (pdp)
packet->gtp0.h.flow = hton16(pdp->flrc);
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
} 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))
@@ -575,11 +649,12 @@ int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
return 0;
}
int gtp_notification(struct gsn_t *gsn, int version,
static int gtp_notification(struct gsn_t *gsn, uint8_t version,
union gtp_packet *packet, int len,
struct sockaddr_in *peer, int fd, uint16_t seq)
{
uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
struct sockaddr_in addr;
memcpy(&addr, peer, sizeof(addr));
@@ -592,10 +667,10 @@ int gtp_notification(struct gsn_t *gsn, int version,
else if (fd == gsn->fd1u)
addr.sin_port = htons(GTP1C_PORT);
if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
if (ver == 0) { /* Version 0 */
packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
packet->gtp0.h.seq = hton16(seq);
} else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
} 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);
} else {
@@ -619,7 +694,7 @@ int gtp_notification(struct gsn_t *gsn, int version,
return 0;
}
int gtp_dublicate(struct gsn_t *gsn, int version,
static int gtp_dublicate(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, uint16_t seq)
{
struct qmsg_t *qmsg;
@@ -925,8 +1000,7 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
if (gsn->cb_conf)
gsn->cb_conf(type, recovery, NULL, cbp);
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
emit_cb_recovery(gsn, peer, NULL, recovery);
return 0;
}
@@ -964,7 +1038,7 @@ int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
}
/* Send off an Supported Extension Headers Notification */
int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
static int gtp_extheader_req(struct gsn_t *gsn, uint8_t version, struct sockaddr_in *peer,
int fd, void *pack, unsigned len)
{
union gtp_packet packet;
@@ -985,7 +1059,7 @@ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
}
/* Handle a Supported Extension Headers Notification */
int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
static int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
void *pack, unsigned len)
{
@@ -1008,7 +1082,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
*************************************************************/
/* API: Send Create PDP Context Request (7.3.1) */
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp)
{
union gtp_packet packet;
@@ -1258,6 +1332,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
struct pdp_t pdp_buf;
union gtpie_member *ie[GTPIE_SIZE];
uint8_t recovery;
bool recovery_recvd = false;
int rc;
uint16_t seq = get_seq(pack);
int hlen = get_hlen(pack);
@@ -1358,8 +1434,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
/* Recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
/* we use recovery futher down after announcing new pdp ctx to user */
recovery_recvd = true;
}
/* Selection mode (conditional) */
@@ -1560,6 +1636,9 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
/* Switch to using the old pdp context */
pdp = pdp_old;
if (recovery_recvd)
emit_cb_recovery(gsn, peer, pdp, recovery);
/* Confirm to peer that things were "successful" */
return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_ACC_REQ);
@@ -1581,13 +1660,16 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
/* Callback function to validata login */
if (gsn->cb_create_context_ind != 0)
return gsn->cb_create_context_ind(pdp);
rc = gsn->cb_create_context_ind(pdp);
else {
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"No create_context_ind callback defined\n");
return gtp_create_pdp_resp(gsn, version, pdp,
rc = gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_NOT_SUPPORTED);
}
if (recovery_recvd)
emit_cb_recovery(gsn, peer, pdp, recovery);
return rc;
}
/* Handle Create PDP Context Response */
@@ -1644,8 +1726,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
/* Extract recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
emit_cb_recovery(gsn, peer, pdp, recovery);
}
/* Extract protocol configuration options (optional) */
@@ -1884,7 +1965,7 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
}
/* Send Update PDP Context Response */
int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
static int gtp_update_pdp_resp(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, uint8_t cause)
@@ -1946,7 +2027,7 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
}
/* Handle Update PDP Context Request */
int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
@@ -2054,8 +2135,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
/* Recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
emit_cb_recovery(gsn, peer, pdp, recovery);
}
if (version == 0) {
@@ -2174,7 +2254,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
}
/* Handle Update PDP Context Response */
int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, void *pack, unsigned len)
{
struct pdp_t *pdp;
@@ -2215,8 +2295,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
/* Extract recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
if (gsn->cb_recovery)
gsn->cb_recovery(peer, recovery);
emit_cb_recovery(gsn, peer, pdp, recovery);
}
/* Check all conditional information elements */
@@ -2285,16 +2364,66 @@ err_out:
return EOF;
}
/* API: Send Delete PDP Context Request */
/* API: Deprecated. Send Delete PDP Context Request And free pdp ctx. */
int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
int teardown)
{
struct pdp_t *linked_pdp;
struct pdp_t *secondary_pdp;
int n;
if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) {
LOGP(DLGTP, LOGL_ERROR,
"Unknown linked PDP context: %u\n", pdp->teic_own);
return EOF;
}
if (gtp_delete_context_req2(gsn, pdp, cbp, teardown) == EOF)
return EOF;
if (teardown) { /* Remove all contexts */
for (n = 0; n < PDP_MAXNSAPI; n++) {
if (linked_pdp->secondary_tei[n]) {
if (pdp_getgtp1
(&secondary_pdp,
linked_pdp->secondary_tei[n])) {
LOGP(DLGTP, LOGL_ERROR,
"Unknown secondary PDP context\n");
return EOF;
}
if (linked_pdp != secondary_pdp) {
if (gsn->cb_delete_context)
gsn->cb_delete_context
(secondary_pdp);
pdp_freepdp(secondary_pdp);
}
}
}
if (gsn->cb_delete_context)
gsn->cb_delete_context(linked_pdp);
pdp_freepdp(linked_pdp);
} else {
if (gsn->cb_delete_context)
gsn->cb_delete_context(pdp);
if (pdp == linked_pdp) {
linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0;
linked_pdp->nodata = 1;
} else
pdp_freepdp(pdp);
}
return 0;
}
/* API: Send Delete PDP Context Request. PDP CTX shall be free'd by user at cb_conf(GTP_DELETE_PDP_RSP) */
int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
int teardown)
{
union gtp_packet packet;
unsigned int length =
get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
struct in_addr addr;
struct pdp_t *linked_pdp;
struct pdp_t *secondary_pdp;
int n;
int count = 0;
@@ -2331,37 +2460,6 @@ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp);
if (teardown) { /* Remove all contexts */
for (n = 0; n < PDP_MAXNSAPI; n++) {
if (linked_pdp->secondary_tei[n]) {
if (pdp_getgtp1
(&secondary_pdp,
linked_pdp->secondary_tei[n])) {
LOGP(DLGTP, LOGL_ERROR,
"Unknown secondary PDP context\n");
return EOF;
}
if (linked_pdp != secondary_pdp) {
if (gsn->cb_delete_context)
gsn->cb_delete_context
(secondary_pdp);
pdp_freepdp(secondary_pdp);
}
}
}
if (gsn->cb_delete_context)
gsn->cb_delete_context(linked_pdp);
pdp_freepdp(linked_pdp);
} else {
if (gsn->cb_delete_context)
gsn->cb_delete_context(pdp);
if (pdp == linked_pdp) {
linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0;
linked_pdp->nodata = 1;
} else
pdp_freepdp(pdp);
}
return 0;
}
@@ -2501,6 +2599,9 @@ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
if (linked_pdp->secondary_tei[n])
count++;
if (count <= 1) {
GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
"Ignoring CTX DEL without teardown and count=%d\n",
count);
return 0; /* 29.060 7.3.5 Ignore message */
}
}
@@ -2518,19 +2619,32 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
uint8_t cause;
void *cbp = NULL;
uint8_t type = 0;
struct pdp_t *pdp = NULL;
int hlen = get_hlen(pack);
/* Remove packet from queue */
if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp))
return EOF;
/* Find the context in question. It may not be available if gtp_delete_context_req
* was used and as a result the PDP ctx was already freed */
if (pdp_getgtp1(&pdp, get_tei(pack))) {
gsn->err_unknownpdp++;
GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
"Unknown PDP context: %u (expected if gtp_delete_context_req is used)\n",
get_tei(pack));
if (gsn->cb_conf)
gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
/* Decode information elements */
if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
gsn->invalid++;
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Invalid message format\n");
if (gsn->cb_conf)
gsn->cb_conf(type, EOF, NULL, cbp);
gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -2540,7 +2654,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing mandatory information field\n");
if (gsn->cb_conf)
gsn->cb_conf(type, EOF, NULL, cbp);
gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -2550,19 +2664,19 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Unexpected cause value received: %d\n", cause);
if (gsn->cb_conf)
gsn->cb_conf(type, cause, NULL, cbp);
gsn->cb_conf(type, cause, pdp, cbp);
return EOF;
}
/* Callback function to notify application */
if (gsn->cb_conf)
gsn->cb_conf(type, cause, NULL, cbp);
gsn->cb_conf(type, cause, pdp, cbp);
return 0;
}
/* Send Error Indication (response to a GPDU message) - 3GPP TS 29.060 7.3.7 */
int gtp_error_ind_resp(struct gsn_t *gsn, int version,
static int gtp_error_ind_resp(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
@@ -2584,7 +2698,7 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version,
}
/* Handle Error Indication */
int gtp_error_ind_conf(struct gsn_t *gsn, int version,
static int gtp_error_ind_conf(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, void *pack, unsigned len)
{
union gtpie_member *ie[GTPIE_SIZE];
@@ -2628,22 +2742,28 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Received Error Indication\n");
/* This is obvious from above code, given the semantics of the
* functions above, but Coverity doesn't figure this out, so
* let's make it clear. It's good style anyway in case above
* code should ever change. */
OSMO_ASSERT(pdp);
if (gsn->cb_delete_context)
gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return 0;
}
int gtp_gpdu_ind(struct gsn_t *gsn, int version,
static int gtp_gpdu_ind(struct gsn_t *gsn, uint8_t version,
struct sockaddr_in *peer, int fd, void *pack, unsigned len)
{
int hlen = GTP1_HEADER_SIZE_SHORT;
int hlen;
/* Need to include code to verify packet src and dest addresses */
struct pdp_t *pdp;
if (version == 0) {
switch (version) {
case 0:
if (pdp_getgtp0
(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) {
gsn->err_unknownpdp++;
@@ -2653,7 +2773,8 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
len);
}
hlen = GTP0_HEADER_SIZE;
} else if (version == 1) {
break;
case 1:
if (pdp_getgtp1
(&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) {
gsn->err_unknownpdp++;
@@ -2668,9 +2789,11 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
hlen = GTP1_HEADER_SIZE_LONG;
else
hlen = GTP1_HEADER_SIZE_SHORT;
} else {
break;
default:
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Unknown version: %d\n", version);
return EOF;
}
/* If the GPDU was not from the peer GSN tell him to delete context */
@@ -2699,7 +2822,7 @@ int gtp_decaps0(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp0_header *pheader;
int version = 0; /* GTP version should be determined from header! */
uint8_t version;
int fd = gsn->fd0;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -2735,15 +2858,17 @@ int gtp_decaps0(struct gsn_t *gsn)
pheader = (struct gtp0_header *)(buffer);
version = GTPHDR_F_GET_VER(pheader->flags);
/* Version should be gtp0 (or earlier) */
/* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */
/* GTP 0 messages. If other version message is received we reply that we */
/* only support version 0, implying that this is the only version */
/* supported on this port */
if (((pheader->flags & 0xe0) > 0x00)) {
if (version > 0) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported GTP version\n");
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
"Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
continue;
}
@@ -2767,23 +2892,23 @@ int gtp_decaps0(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_DELETE_PDP_RSP))) {
(pheader->type == GTP_UPDATE_PDP_RSP))) {
gsn->unexpect++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status,
"Unexpected GTPv0 Signalling Message\n");
"Unexpected GTPv0 Signalling Message '%s'\n",
get_value_string(gtp_type_names, pheader->type));
continue; /* Silently discard 29.60: 11.1.4 */
}
if ((gsn->mode == GTP_MODE_SGSN) &&
((pheader->type == GTP_CREATE_PDP_REQ) ||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
(pheader->type == GTP_DELETE_PDP_REQ))) {
(pheader->type == GTP_UPDATE_PDP_REQ))) {
gsn->unexpect++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status,
"Unexpected GTPv0 Signalling Message\n");
"Unexpected GTPv0 Signalling Message '%s'\n",
get_value_string(gtp_type_names, pheader->type));
continue; /* Silently discard 29.60: 11.1.4 */
}
@@ -2844,7 +2969,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp1_header_short *pheader;
int version = 1; /* TODO GTP version should be determined from header! */
uint8_t version;
int fd = gsn->fd1c;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -2880,11 +3005,13 @@ int gtp_decaps1c(struct gsn_t *gsn)
pheader = (struct gtp1_header_short *)(buffer);
version = GTPHDR_F_GET_VER(pheader->flags);
/* Version must be no larger than GTP 1 */
if (((pheader->flags & 0xe0) > 0x20)) {
if (version > 1) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported GTP version\n");
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
"Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, version, &peer, fd, buffer, status);
/*29.60: 11.1.1 */
continue;
@@ -2894,10 +3021,10 @@ int gtp_decaps1c(struct gsn_t *gsn)
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
/* GTP 1 messages. If GTP 0 message is received we silently discard */
/* the message */
if (((pheader->flags & 0xe0) < 0x20)) {
if (version < 1) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported GTP version\n");
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
"Unsupported GTP version %"PRIu8"\n", version);
continue;
}
@@ -2930,7 +3057,7 @@ int gtp_decaps1c(struct gsn_t *gsn)
/* Check for extension headers */
/* TODO: We really should cycle through the headers and determine */
/* if any have the comprehension required flag set */
if (((pheader->flags & 0x04) != 0x00)) {
if (((pheader->flags & GTP1HDR_F_EXT) != 0x00)) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported extension header\n");
@@ -2942,23 +3069,23 @@ 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_DELETE_PDP_RSP))) {
(pheader->type == GTP_UPDATE_PDP_RSP))) {
gsn->unexpect++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status,
"Unexpected GTPv1 Signalling Message\n");
"Unexpected GTPv1 Signalling Message '%s'\n",
get_value_string(gtp_type_names, pheader->type));
continue; /* Silently discard 29.60: 11.1.4 */
}
if ((gsn->mode == GTP_MODE_SGSN) &&
((pheader->type == GTP_CREATE_PDP_REQ) ||
(pheader->type == GTP_UPDATE_PDP_REQ) ||
(pheader->type == GTP_DELETE_PDP_REQ))) {
(pheader->type == GTP_UPDATE_PDP_REQ))) {
gsn->unexpect++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status,
"Unexpected GTPv1 Signalling Message\n");
"Unexpected GTPv1 Signalling Message '%s'\n",
get_value_string(gtp_type_names, pheader->type));
continue; /* Silently discard 29.60: 11.1.4 */
}
@@ -3019,7 +3146,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
socklen_t peerlen;
int status;
struct gtp1_header_short *pheader;
int version = 1; /* GTP version should be determined from header! */
uint8_t version;
int fd = gsn->fd1u;
/* TODO: Need strategy of userspace buffering and blocking */
@@ -3056,11 +3183,13 @@ int gtp_decaps1u(struct gsn_t *gsn)
pheader = (struct gtp1_header_short *)(buffer);
version = GTPHDR_F_GET_VER(pheader->flags);
/* Version must be no larger than GTP 1 */
if (((pheader->flags & 0xe0) > 0x20)) {
if (version > 1) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported GTP version\n");
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
"Unsupported GTP version %"PRIu8"\n", version);
gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */
continue;
}
@@ -3069,10 +3198,10 @@ int gtp_decaps1u(struct gsn_t *gsn)
/* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
/* GTP 1 messages. If GTP 0 message is received we silently discard */
/* the message */
if (((pheader->flags & 0xe0) < 0x20)) {
if (version < 1) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported GTP version\n");
GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
"Unsupported GTP version %"PRIu8"\n", version);
continue;
}
@@ -3105,7 +3234,7 @@ int gtp_decaps1u(struct gsn_t *gsn)
/* Check for extension headers */
/* TODO: We really should cycle through the headers and determine */
/* if any have the comprehension required flag set */
if (((pheader->flags & 0x04) != 0x00)) {
if (((pheader->flags & GTP1HDR_F_EXT) != 0x00)) {
gsn->unsup++;
GTP_LOGPKG(LOGL_ERROR, &peer, buffer,
status, "Unsupported extension header\n");
@@ -3146,61 +3275,63 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
{
union gtp_packet packet;
struct sockaddr_in addr;
struct msghdr msgh;
struct iovec iov[2];
int fd;
int length;
/* prepare destination address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
#if defined(__FreeBSD__) || defined(__APPLE__)
addr.sin_len = sizeof(addr);
#endif
memcpy(&addr.sin_addr, pdp->gsnru.v, pdp->gsnru.l); /* TODO range check */
/* prepare msghdr */
memset(&msgh, 0, sizeof(msgh));
msgh.msg_name = &addr;
msgh.msg_namelen = sizeof(addr);
msgh.msg_iov = iov;
msgh.msg_iovlen = ARRAY_SIZE(iov);
/* prepare iovectors */
iov[0].iov_base = &packet;
/* iov[0].iov_len is not known here yet */
iov[1].iov_base = pack;
iov[1].iov_len = len;
if (pdp->version == 0) {
length = GTP0_HEADER_SIZE + len;
iov[0].iov_len = GTP0_HEADER_SIZE;
addr.sin_port = htons(GTP0_PORT);
fd = gsn->fd0;
get_default_gtp(0, GTP_GPDU, &packet);
packet.gtp0.h.length = hton16(len);
if (pdp->tx_gpdu_seq)
packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
else
packet.gtp0.h.seq = 0;
packet.gtp0.h.flow = hton16(pdp->flru);
packet.gtp0.h.tid = htobe64(pdp_gettid(pdp->imsi, pdp->nsapi));
if (len > sizeof(union gtp_packet) - sizeof(struct gtp0_header)) {
gsn->err_memcpy++;
LOGP(DLGTP, LOGL_ERROR,
"Memcpy failed: %u > %zu\n", len,
sizeof(union gtp_packet) -
sizeof(struct gtp0_header));
return EOF;
}
memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
} else if (pdp->version == 1) {
length = GTP1_HEADER_SIZE_LONG + len;
addr.sin_port = htons(GTP1U_PORT);
fd = gsn->fd1u;
get_default_gtp(1, GTP_GPDU, &packet);
if (pdp->tx_gpdu_seq) {
packet.gtp1l.h.seq = hton16(pdp->gtpsntx++);
packet.gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT +
GTP1_HEADER_SIZE_LONG);
packet.gtp1l.h.seq = hton16(pdp->gtpsntx++);
packet.gtp1l.h.tei = hton32(pdp->teid_gn);
if (len >
sizeof(union gtp_packet) -
sizeof(struct gtp1_header_long)) {
gsn->err_memcpy++;
LOGP(DLGTP, LOGL_ERROR,
"Memcpy failed: %u > %zu\n", len,
sizeof(union gtp_packet) -
sizeof(struct gtp0_header));
return EOF;
iov[0].iov_len = GTP1_HEADER_SIZE_LONG;
} else {
packet.gtp1s.h.flags &= ~GTP1HDR_F_SEQ;
packet.gtp1s.h.length = hton16(len);
packet.gtp1s.h.tei = hton32(pdp->teid_gn);
iov[0].iov_len = GTP1_HEADER_SIZE_SHORT;
}
memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */
} else {
LOGP(DLGTP, LOGL_ERROR, "Unknown version: %d\n", pdp->version);
return EOF;
@@ -3211,11 +3342,10 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
return -1;
}
if (sendto(fd, &packet, length, 0,
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (sendmsg(fd, &msgh, 0) < 0) {
gsn->err_sendto++;
LOGP(DLGTP, LOGL_ERROR,
"Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
"sendmsg(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd,
(unsigned long)&packet, GTP0_HEADER_SIZE + len,
strerror(errno));
return EOF;
@@ -3227,15 +3357,6 @@ int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len)
* Conversion functions
*************************************************************/
int char2ul_t(char *src, struct ul_t dst)
{
dst.l = strlen(src) + 1;
dst.v = malloc(dst.l);
dst.v[0] = dst.l - 1;
memcpy(&dst.v[1], src, dst.v[0]);
return 0;
}
/* ***********************************************************
* IP address conversion functions
* There exist several types of address representations:
@@ -3251,8 +3372,8 @@ int char2ul_t(char *src, struct ul_t dst)
int ipv42eua(struct ul66_t *eua, struct in_addr *src)
{
eua->v[0] = 0xf1; /* IETF */
eua->v[1] = 0x21; /* IPv4 */
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);
@@ -3264,7 +3385,7 @@ int ipv42eua(struct ul66_t *eua, struct in_addr *src)
int eua2ipv4(struct in_addr *dst, struct ul66_t *eua)
{
if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] = 0x21))
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;
@@ -3293,7 +3414,7 @@ int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src)
* _network byte order_ to contain BCD digits ?!? */
const char *imsi_gtp2str(const uint64_t *imsi)
{
static char buf[sizeof(*imsi)+1];
static char buf[sizeof(*imsi)*2+1];
const uint8_t *imsi8 = (const uint8_t *) imsi;
unsigned int i, j = 0;

View File

@@ -12,6 +12,9 @@
#ifndef _GTP_H
#define _GTP_H
#include <osmocom/core/utils.h>
#include <osmocom/core/defs.h>
#define GTP_MODE_GGSN 1
#define GTP_MODE_SGSN 2
@@ -85,6 +88,10 @@
/* 242-254 For future use. */
#define GTP_GPDU 255 /* G-PDU */
extern const struct value_string gtp_type_names[];
static inline const char *gtp_type_name(uint8_t val)
{ return get_value_string(gtp_type_names, val); }
/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
/* */
#define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */
@@ -162,6 +169,13 @@ struct gtp0_header { /* Descriptions from 3GPP 09.60 */
uint64_t tid; /* 13 Tunnel ID */
} __attribute__((packed)); /* 20 */
#define GTP1HDR_F_NPDU 0x01
#define GTP1HDR_F_SEQ 0x02
#define GTP1HDR_F_EXT 0x04
#define GTP1HDR_F_GTP1 0x10
#define GTPHDR_F_VER(n) ((n) << 5)
#define GTPHDR_F_GET_VER(flags) ((flags)>>5)
struct gtp1_header_short { /* Descriptions from 3GPP 29060 */
uint8_t flags; /* 01 bitfield, with typical values */
/* 001..... Version: 1 */
@@ -257,6 +271,7 @@ struct gsn_t {
int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
/* Counters */
@@ -310,6 +325,9 @@ extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, struct in_addr *inetaddr);
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");
extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, int teardown);
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
@@ -344,6 +362,11 @@ extern int gtp_set_cb_conf(struct gsn_t *gsn,
int gtp_set_cb_recovery(struct gsn_t *gsn,
int (*cb) (struct sockaddr_in * peer,
uint8_t recovery))
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
int gtp_set_cb_recovery2(struct gsn_t *gsn,
int (*cb) (struct sockaddr_in * peer,
struct pdp_t * pdp,
uint8_t recovery));
/* Internal functions (not part of the API */

View File

@@ -39,8 +39,16 @@
#include "gtpie.h"
/*! Encode a TLV type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] l Length of value \a v in bytes
* \param[in] v Pointer to input value
* \returns 0 on success, 1 on error */
int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
int l, void *v)
int l, const void *v)
{
if ((*length + 3 + l) >= size)
return 1;
@@ -51,8 +59,16 @@ int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Encode a TV0 (Tag + value) type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] l Length of value \a v in bytes
* \param[in] v Pointer to input value
* \returns 0 on success, 1 on error */
int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
int l, uint8_t * v)
int l, const uint8_t * v)
{
if ((*length + 1 + l) >= size)
return 1;
@@ -62,6 +78,13 @@ int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Encode a TV1 (Tag + 8bit value) type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] v Input value
* \returns 0 on success, 1 on error */
int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
uint8_t v)
{
@@ -73,6 +96,13 @@ int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Encode a TV2 (Tag + 16bit value) type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] v Input value
* \returns 0 on success, 1 on error */
int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
uint16_t v)
{
@@ -84,6 +114,13 @@ int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Encode a TV4 (Tag + 32bit value) type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] v Input value
* \returns 0 on success, 1 on error */
int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
uint32_t v)
{
@@ -95,6 +132,13 @@ int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Encode a TV8 (Tag + 64bit value) type Information Element.
* \param[inout] p Pointer to output packet to which IE is appended
* \param[inout] length Up to which byte length is \a p used/filled
* \param[in] size Total size of \a p in bytes
* \param[in] t Tag / Information Element Identifier
* \param[in] v Input value
* \returns 0 on success, 1 on error */
int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
uint64_t v)
{
@@ -106,6 +150,11 @@ int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t,
return 0;
}
/*! Obtain a GTP IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \returns index into \a ie on success; -1 if not found */
int gtpie_getie(union gtpie_member *ie[], int type, int instance)
{
int j;
@@ -118,6 +167,11 @@ int gtpie_getie(union gtpie_member *ie[], int type, int instance)
return -1;
}
/*! Determine if IE for a given tag/IEI exists in a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \returns 1 if IEI instance present in \a ie; 0 if not */
int gtpie_exist(union gtpie_member *ie[], int type, int instance)
{
int j;
@@ -130,6 +184,14 @@ int gtpie_exist(union gtpie_member *ie[], int type, int instance)
return 0;
}
/*! Obtain Value of TLV-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[out] length Length of IE
* \param[inout] dst Caller-allocated buffer where to store value
* \param[in] size Size of \a dst in bytes
* \returns 0 on sucess; EOF in case value is larger than \a size */
int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
unsigned int *length, void *dst, unsigned int size)
{
@@ -145,6 +207,13 @@ int gtpie_gettlv(union gtpie_member *ie[], int type, int instance,
return 0;
}
/*! Obtain Value of TV0-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[inout] dst Caller-allocated buffer where to store value
* \param[in] size Size of value in bytes
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
void *dst, unsigned int size)
{
@@ -157,6 +226,12 @@ int gtpie_gettv0(union gtpie_member *ie[], int type, int instance,
return 0;
}
/*! Obtain Value of TV1-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[inout] dst Caller-allocated buffer where to store value
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
uint8_t * dst)
{
@@ -169,6 +244,12 @@ int gtpie_gettv1(union gtpie_member *ie[], int type, int instance,
return 0;
}
/*! Obtain Value of TV2-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[inout] dst Caller-allocated buffer where to store value
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
uint16_t * dst)
{
@@ -181,6 +262,12 @@ int gtpie_gettv2(union gtpie_member *ie[], int type, int instance,
return 0;
}
/*! Obtain Value of TV4-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[inout] dst Caller-allocated buffer where to store value
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
uint32_t * dst)
{
@@ -193,6 +280,12 @@ int gtpie_gettv4(union gtpie_member *ie[], int type, int instance,
return 0;
}
/*! Obtain Value of TV8-type IE for a given tag/IEI from a list/array.
* \param[in] ie Array of GTPIE
* \param[in] type Tag/IEI for which we're looking
* \param[in] instance Instance (number of occurence) of this IEI
* \param[inout] dst Caller-allocated buffer where to store value
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
uint64_t * dst)
{
@@ -205,13 +298,19 @@ int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
return 0;
}
int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
/*! Parse an incoming GTP packet into its Information Elements.
* \param[out] ie Caller-allocated Array of GTPIE
* \param[in] version GTP protocol version
* \param[in] pack Pointer to raw GTP packet (payload part)
* \param[in] len Length of \a pack in bytes
* \returns 0 on sucess; EOF in case IE not found */
int gtpie_decaps(union gtpie_member *ie[], int version, const void *pack,
unsigned len)
{
int i;
int j = 0;
unsigned char *p;
unsigned char *end;
const unsigned char *p;
const unsigned char *end;
end = (unsigned char *)pack + len;
p = pack;
@@ -391,11 +490,76 @@ int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
case GTPIE_RAB_SETUP:
case GTPIE_TRIGGER_ID:
case GTPIE_OMC_ID:
case GTPIE_CHARGING_ADDR:
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:
if (j < GTPIE_SIZE) {
ie[j] = (union gtpie_member *)p;
@@ -430,6 +594,11 @@ int gtpie_decaps(union gtpie_member *ie[], int version, void *pack,
}
}
/*! Encode GTP packet payload from Array of Information Elements.
* \param[out] ie Input Array of GTPIE
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
* \param[out] len Encoded length of \a pack in bytes
* \returns 0 on sucess; 2 for out-of-space */
int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
{
int i;
@@ -483,11 +652,13 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
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 */
case GTPIE_RAI:
iesize = 9;
break;
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
@@ -511,6 +682,75 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
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[i]->tlv.l);
@@ -528,6 +768,12 @@ int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len)
return 0;
}
/*! Encode GTP packet payload from Array of Information Elements.
* \param[out] ie Input Array of GTPIE
* \param[in] size Size of ?
* \param[out] pack Pointer to caller-allocated buffer for raw GTP packet (GTPIE_MAX length)
* \param[out] len Encoded length of \a pack in bytes
* \returns 0 on sucess; 2 for out-of-space */
int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
void *pack, unsigned *len)
{
@@ -577,16 +823,19 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
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 */
case GTPIE_RAI:
iesize = 9;
break;
case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */
@@ -595,8 +844,7 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
case GTPIE_EXT_HEADER_T: /* GTP extension header */
iesize = 2 + hton8(ie[i].ext.l);
break;
case GTPIE_CHARGING_ID: /* TLV GTPIE types with length length 2 */
case GTPIE_EUA:
case GTPIE_EUA: /* TLV GTPIE types with length length 2 */
case GTPIE_MM_CONTEXT:
case GTPIE_PDP_CONTEXT:
case GTPIE_APN:
@@ -611,6 +859,75 @@ int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
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[i].tlv.l);

View File

@@ -12,6 +12,8 @@
#ifndef _GTPIE_H
#define _GTPIE_H
#include <arpa/inet.h>
/* Macroes for conversion between host and network byte order */
#define hton8(x) (x)
#define ntoh8(x) (x)
@@ -48,7 +50,7 @@ static __inline uint64_t hton64(uint64_t q)
#define GTPIE_DEBUG 0 /* Print debug information */
/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
/* GTP Information elements from 29.060 v11.8.0 7.7 Information Elements */
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
* by 135: QOS Profile in version 1 */
@@ -84,7 +86,7 @@ static __inline uint64_t hton64(uint64_t q)
#define GTPIE_TRACE_TYPE 28 /* Trace Type 2 */
#define GTPIE_MS_NOT_REACH 29 /* MS Not Reachable Reason 1 */
/* 30-116 UNUSED */
/* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
/* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
#define GTPIE_CHARGING_ID 127 /* Charging ID 4 */
#define GTPIE_EUA 128 /* End User Address */
#define GTPIE_MM_CONTEXT 129 /* MM Context */
@@ -102,14 +104,81 @@ static __inline uint64_t hton64(uint64_t q)
#define GTPIE_EXT_HEADER_T 141 /* Extension Header Type List */
#define GTPIE_TRIGGER_ID 142 /* Trigger Id */
#define GTPIE_OMC_ID 143 /* OMC Identity */
#define GTPIE_RAN_T_CONTAIN 144 /* RAN Transparent Container */
#define GTPIE_PDP_CTX_PRIO 145 /* PDP Context Prioritization */
#define GTPIE_ADDL_RAB_S_I 146 /* Additional RAB Setup Information */
#define GTPIE_SGSN_NUMBER 147 /* SGSN Number */
#define GTPIE_COMMON_FLAGS 148 /* Common Flags */
#define GTPIE_APN_RESTR 149 /* APN Restriction */
#define GTPIE_R_PRIO_LCS 150 /* Radio Priority LCS */
#define GTPIE_RAT_TYPE 151 /* Radio Access Technology Type */
#define GTPIE_USER_LOC 152 /* User Location Information */
#define GTPIE_MS_TZ 153 /* MS Time Zone */
#define GTPIE_IMEI_SV 154 /* IMEI Software Version */
#define GTPIE_CML_CHG_I_CT 155 /* CAMEL Charging Information Container */
#define GTPIE_MBMS_UE_CTX 156 /* MSMS UE Context */
#define GTPIE_TMGI 157 /* Temporary Mobile Group Identity (TMGI) */
#define GTPIE_RIM_ROUT_ADDR 158 /* RIM Routing Address */
#define GTPIE_MBMS_PCO 159 /* MBMS Protocol Configuratin Options */
#define GTPIE_MBMS_SA 160 /* MBMS Service Area */
#define GTPIE_SRNC_PDCP_CTX 161 /* Source RNC PDCP Context Info */
#define GTPIE_ADDL_TRACE 162 /* Additional Trace Info */
#define GTPIE_HOP_CTR 163 /* Hop Counter */
#define GTPIE_SEL_PLMN_ID 164 /* Selected PLMN ID */
#define GTPIE_MBMS_SESS_ID 165 /* MBMS Session Identifier */
#define GTPIE_MBMS_2_3G_IND 166 /* MBMS 2G/3G Indicator */
#define GTPIE_ENH_NSAPI 167 /* Enhanced NSAPI */
#define GTPIE_MBMS_SESS_DUR 168 /* MBMS Session Duration */
#define GTPIE_A_MBMS_TRAC_I 169 /* Additional MBMS Trace Info */
#define GTPIE_MBMS_S_REP_N 170 /* MBMS Session Repetition Number */
#define GTPIE_MBMS_TTDT 171 /* MBMS Time To Data Transfer */
#define GTPIE_PS_HO_REQ_CTX 172 /* PS Handover Request Context */
#define GTPIE_BSS_CONTAINER 173 /* BSS Container */
#define GTPIE_CELL_ID 174 /* Cell Identification */
#define GTPIE_PDU_NUMBERS 175 /* PDU Numbers */
#define GTPIE_BSSGP_CAUSE 176 /* BSSGP Cause */
#define GTPIE_RQD_MBMS_BCAP 177 /* Required MBMS Bearer Capabilities */
#define GTPIE_RIM_RA_DISCR 178 /* RIM Routing Address Discriminator */
#define GTPIE_L_SETUP_PFCS 179 /* List of set-up PFCs */
#define GTPIE_PS_HO_XID_PAR 180 /* PS Handover XID Parameters */
#define GTPIE_MS_CHG_REP_A 181 /* MS Info Change Reporting Action */
#define GTPIE_DIR_TUN_FLAGS 182 /* Direct Tunnel Flags */
#define GTPIE_CORREL_ID 183 /* Correlation-ID */
#define GTPIE_BCM 184 /* Bearer control mode */
/* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
#define GTPIE_MBMS_FLOWI 185 /* MBMS Flow Identifier */
#define GTPIE_MBMS_MC_DIST 186 /* MBMS IP Multicast Distribution */
#define GTPIE_MBMS_DIST_ACK 187 /* MBMS Distribution Acknowledgement */
#define GTPIE_R_IRAT_HO_INF 188 /* Reliable INTER RAT HANDOVER INFO */
#define GTPIE_RFSP_IDX 189 /* RFSP Index */
#define GTPIE_FQDN 190 /* FQDN */
#define GTPIE_E_ALL_PRIO_1 191 /* Evolvd Allocation/Retention Priority I */
#define GTPIE_E_ALL_PRIO_2 192 /* Evolvd Allocation/Retention Priority II */
#define GTPIE_E_CMN_FLAGS 193 /* Extended Common Flags */
#define GTPIE_U_CSG_INFO 194 /* User CSG Information (UCI) */
#define GTPIE_CSG_I_REP_ACT 195 /* CSG Information Reporting Action */
#define GTPIE_CSG_ID 196 /* CSG ID */
#define GTPIE_CSG_MEMB_IND 197 /* CSG Membership Indication (CMI) */
#define GTPIE_AMBR 198 /* Aggregate Maximum Bit Rate (AMBR) */
#define GTPIE_UE_NET_CAPA 199 /* UE Network Capability */
#define GTPIE_UE_AMBR 200 /* UE-AMBR */
#define GTPIE_APN_AMBR_NS 201 /* APN-AMBR with NSAPI */
#define GTPIE_GGSN_BACKOFF 202 /* GGSN Back-Off Time */
#define GTPIE_S_PRIO_IND 203 /* Signalling Priority Indication */
#define GTPIE_S_PRIO_IND_NS 204 /* Signalling Priority Indication with NSAPI */
#define GTPIE_H_BR_16MBPS_F 205 /* Higher Bitrates than 16 Mbps flag */
/* 206: Reserved */
#define GTPIE_A_MMCTX_SRVCC 207 /* Additional MM context for SRVCC */
#define GTPIE_A_FLAGS_SRVCC 208 /* Additional flags fro SRVC */
#define GTPIE_STN_SR 209 /* STN-SR */
#define GTPIE_C_MSISDN 210 /* C-MSISDN */
#define GTPIE_E_RANAP_CAUSE 211 /* Extended RANAP Cause */
#define GTPIE_ENODEB_ID 212 /* eNodeB ID */
#define GTPIE_SEL_MODE_NS 213 /* Selection Mode with NSAPI */
#define GTPIE_ULI_TIMESTAMP 214 /* ULI Timestamp */
/* 215-238 Spare. For future use */
/* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
#define GTPIE_CHARGING_ADDR 251 /* Charging Gateway Address */
/* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
/* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15 / 32.295) */
#define GTPIE_PRIVATE 255 /* Private Extension */
/* GTP information element structs in network order */
@@ -221,9 +290,9 @@ struct tlv2 {
} __attribute__ ((packed));
extern int gtpie_tlv(void *p, unsigned int *length, unsigned int size,
uint8_t t, int l, void *v);
uint8_t t, int l, const void *v);
extern int gtpie_tv0(void *p, unsigned int *length, unsigned int size,
uint8_t t, int l, uint8_t * v);
uint8_t t, int l, const uint8_t * v);
extern int gtpie_tv1(void *p, unsigned int *length, unsigned int size,
uint8_t t, uint8_t v);
extern int gtpie_tv2(void *p, unsigned int *length, unsigned int size,
@@ -248,7 +317,7 @@ extern int gtpie_gettv8(union gtpie_member *ie[], int type, int instance,
uint64_t * dst);
extern int gtpie_decaps(union gtpie_member *ie[], int version,
void *pack, unsigned len);
const void *pack, unsigned len);
extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len);
extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size,
void *pack, unsigned *len);

View File

@@ -149,6 +149,8 @@ int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
1].secondary_tei[(*pdp)->nsapi & 0x0f] =
(*pdp)->teid_own;
}
/* Default: Generate G-PDU sequence numbers on Tx */
(*pdp)->tx_gpdu_seq = true;
return 0;
}
@@ -362,24 +364,6 @@ int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
*/
/* Various conversion functions */
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
{
eua->l = 6;
eua->v[0] = 0xf1; /* IETF */
eua->v[1] = 0x21; /* IPv4 */
memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */
return 0;
}
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst)
{
if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] != 0x21)) {
return EOF;
}
memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
return 0;
}
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
{
return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);

View File

@@ -13,11 +13,21 @@
#ifndef _PDP_H
#define _PDP_H
#include <stdbool.h>
struct gsn_t;
#define LOGPDPX(ss, level, pdp, fmt, args...) \
LOGP(ss, level, "PDP(%s:%u): " fmt, imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi, ## args)
#define PDP_MAX 1024 /* Max number of PDP contexts */
#define PDP_MAXNSAPI 16 /* Max number of NSAPI */
#define PDP_EUA_ORG_IETF 0xF1
#define PDP_EUA_TYPE_v4 0x21
#define PDP_EUA_TYPE_v6 0x57
#define PDP_EUA_TYPE_v4v6 0x8D
/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
* by 135: QOS Profile in version 1 */
@@ -112,7 +122,7 @@ struct pdp_t {
/* Parameters shared by all PDP context belonging to the same MS */
void *ipif; /* IP network interface */
void *peer; /* Pointer to peer protocol */
void *peer[2]; /* Pointer to peer protocol */
void *asap; /* Application specific service access point */
uint64_t imsi; /* International Mobile Subscriber Identity. */
@@ -226,6 +236,8 @@ struct pdp_t {
void *priv;
struct gsn_t *gsn;
bool tx_gpdu_seq; /* Transmit (true) or suppress G-PDU sequence numbers */
};
/* functions related to pdp_t management */
@@ -255,8 +267,6 @@ int pdp_ipdel(struct pdp_t *pdp);
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua);
*/
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
#endif /* !_PDP_H */

View File

@@ -1,7 +1,12 @@
noinst_LIBRARIES = libmisc.a
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c
if ENABLE_GTP_KERNEL
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
libmisc_a_SOURCES += gtp-kernel.c
endif

160
lib/gtp-kernel.c Normal file
View File

@@ -0,0 +1,160 @@
#ifdef __linux__
#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
#endif
#include "../config.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <libgtpnl/gtp.h>
#include <libgtpnl/gtpnl.h>
#include <libmnl/libmnl.h>
#include <errno.h>
#include <time.h>
#include "../lib/tun.h"
#include "../lib/syserr.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
#include <libgtpnl/gtp.h>
#include <libgtpnl/gtpnl.h>
#include <libmnl/libmnl.h>
#include "gtp-kernel.h"
static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp)
{
struct in46_addr ia46;
struct in_addr ia;
in46a_from_eua(&pdp->eua, &ia46);
gsna2in_addr(&ia, &pdp->gsnrc);
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix,
devname, pdp->version,
pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
in46a_ntoa(&ia46), inet_ntoa(ia));
}
static struct {
int genl_id;
struct mnl_socket *nl;
} gtp_nl;
static int gtp_kernel_init_once(void)
{
/* only initialize once */
if (gtp_nl.nl)
return 0;
gtp_nl.nl = genl_socket_open();
if (gtp_nl.nl == NULL) {
LOGP(DGGSN, LOGL_ERROR, "cannot create genetlink socket\n");
return -1;
}
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
if (gtp_nl.genl_id < 0) {
LOGP(DGGSN, LOGL_ERROR, "cannot lookup GTP genetlink ID\n");
genl_socket_close(gtp_nl.nl);
gtp_nl.nl = NULL;
return -1;
}
LOGP(DGGSN, LOGL_NOTICE, "Initialized GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id);
return 0;
}
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
{
if (gtp_kernel_init_once() < 0)
return -1;
return gtp_dev_create(dest_ns, devname, fd0, fd1u);
}
int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u)
{
if (gtp_kernel_init_once() < 0)
return -1;
return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u);
}
void gtp_kernel_stop(const char *devname)
{
gtp_dev_destroy(devname);
}
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
{
struct in_addr ms, sgsn;
struct gtp_tunnel *t;
int ret;
pdp_debug(__func__, devname, pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
gtp_tunnel_set_version(t, pdp->version);
gtp_tunnel_set_ms_ip4(t, &ms);
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
/* use the TEI advertised by SGSN when sending packets
* towards the SGSN */
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
}
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
return ret;
}
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
{
struct gtp_tunnel *t;
int ret;
pdp_debug(__func__, devname, pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
gtp_tunnel_set_version(t, pdp->version);
if (pdp->version == 0) {
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
gtp_tunnel_set_flowid(t, pdp->flru);
} else {
gtp_tunnel_set_i_tei(t, pdp->teid_own);
}
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
gtp_tunnel_free(t);
return ret;
}

38
lib/gtp-kernel.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef _GTP_KERNEL_H_
#define _GTP_KERNEL_H_
struct gengetopt_args_info;
extern int debug;
extern char *ipup;
#ifdef GTP_KERNEL
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u);
int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u);
void gtp_kernel_stop(const char *devname);
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname);
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname);
#else
static inline int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
{
SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n");
return -1;
}
#define gtp_kernel_create_sgsn gtp_kernel_create
static inline void gtp_kernel_stop(const char *devname) {}
static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
{
return 0;
}
static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
{
return 0;
}
#endif
#endif /* _GTP_KERNEL_H_ */

View File

@@ -10,6 +10,7 @@
*/
#include "../lib/in46_addr.h"
#include "../gtp/pdp.h"
#include <osmocom/core/utils.h>
@@ -48,7 +49,7 @@ int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
sin->sin_addr = in->v4;
break;
case 16:
sin6->sin6_family = AF_INET;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = in->v6;
break;
default:
@@ -194,22 +195,87 @@ int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
}
}
/*! Convert given PDP End User Address to in46_addr
* \returns 0 on success; negative on error */
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
static unsigned int ipv4_netmasklen(const struct in_addr *netmask)
{
uint32_t bits = netmask->s_addr;
uint8_t *b = (uint8_t*) &bits;
unsigned int i, prefix = 0;
for (i = 0; i < 4; i++) {
while (b[i] & 0x80) {
prefix++;
b[i] = b[i] << 1;
}
}
return prefix;
}
static unsigned int ipv6_netmasklen(const struct in6_addr *netmask)
{
#if defined(__linux__)
#define ADDRFIELD(i) s6_addr32[i]
#else
#define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
#endif
unsigned int i, j, prefix = 0;
for (j = 0; j < 4; j++) {
uint32_t bits = netmask->ADDRFIELD(j);
uint8_t *b = (uint8_t*) &bits;
for (i = 0; i < 4; i++) {
while (b[i] & 0x80) {
prefix++;
b[i] = b[i] << 1;
}
}
}
#undef ADDRFIELD
return prefix;
}
/*! Convert netmask to prefix length representation
* \param[in] netmask in46_addr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit)
* \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask)
*/
unsigned int in46a_netmasklen(const struct in46_addr *netmask)
{
switch (netmask->len) {
case 4:
return ipv4_netmasklen(&netmask->v4);
case 16:
return ipv6_netmasklen(&netmask->v6);
default:
OSMO_ASSERT(0);
return 0;
}
}
/*! Convert given array of in46_addr to PDP End User Address
* \param[in] src Array containing 1 or 2 in46_addr
* \param[out] eua End User Address structure to fill
* \returns 0 on success; negative on error
*
* In case size is 2, this function expects to find exactly one IPv4 and one
* IPv6 addresses in src. */
int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua)
{
const struct in46_addr *src_v4, *src_v6;
if (size == 1) {
switch (src->len) {
case 4:
eua->l = 6;
eua->v[0] = 0xf1; /* IETF */
eua->v[1] = 0x21; /* IPv4 */
eua->v[0] = PDP_EUA_ORG_IETF;
eua->v[1] = PDP_EUA_TYPE_v4;
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
break;
case 8:
case 16:
eua->l = 18;
eua->v[0] = 0xf1; /* IETF */
eua->v[1] = 0x57; /* IPv6 */
eua->v[0] = PDP_EUA_ORG_IETF;
eua->v[1] = PDP_EUA_TYPE_v6;
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
break;
default:
@@ -219,8 +285,34 @@ int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
return 0;
}
/*! Convert given in46_addr to PDP End User Address
* \returns 0 on success; negative on error */
if (src[0].len == src[1].len)
return -1; /* we should have a v4 and a v6 address */
src_v4 = (src[0].len == 4) ? &src[0] : &src[1];
src_v6 = (src[0].len == 4) ? &src[1] : &src[0];
eua->l = 22;
eua->v[0] = PDP_EUA_ORG_IETF;
eua->v[1] = PDP_EUA_TYPE_v4v6;
memcpy(&eua->v[2], &src_v4->v4, 4);
memcpy(&eua->v[6], &src_v6->v6, 16);
return 0;
}
/*! Convert given PDP End User Address to an array of in46_addr
* \param[in] eua End User Address structure to parse
* \param[out] dst Array containing 2 in46_addr
* \returns number of parsed addresses (1 or 2) on success; negative on error
*
* This function expects to receive an End User Address struct together with an
* array of 2 zeroed in46_addr structs. The in46_addr structs are filled in
* order, hence if the function returns 1 the parsed address will be stored in
* the first struct and the second one will be left intact. If 2 is returned, it
* is guaranteed that one of them is an IPv4 and the other one is an IPv6, but
* the order in which they are presented is not specified and must be
* discovered for instance by checking the len field of each address.
*/
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
{
if (eua->l < 2)
@@ -230,28 +322,52 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
return -1;
switch (eua->v[1]) {
case 0x21:
case PDP_EUA_TYPE_v4:
dst->len = 4;
if (eua->l >= 6)
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
else
dst->v4.s_addr = 0;
break;
case 0x57:
return 1;
case PDP_EUA_TYPE_v6:
dst->len = 16;
if (eua->l >= 18)
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
else
memset(&dst->v6, 0, 16);
return 1;
case PDP_EUA_TYPE_v4v6:
/* 3GPP TS 29.060, section 7.7.27 */
switch (eua->l) {
case 2: /* v4 & v6 dynamic */
dst[0].v4.s_addr = 0;
memset(&dst[1].v6, 0, 16);
break;
case 6: /* v4 static, v6 dynamic */
memcpy(&dst[0].v4, &eua->v[2], 4);
memset(&dst[1].v6, 0, 16);
break;
case 18: /* v4 dynamic, v6 static */
dst[0].v4.s_addr = 0;
memcpy(&dst[1].v6, &eua->v[2], 16);
break;
case 22: /* v4 & v6 static */
memcpy(&dst[0].v4, &eua->v[2], 4);
memcpy(&dst[1].v6, &eua->v[6], 16);
break;
default:
return -1;
}
return 0;
dst[0].len = 4;
dst[1].len = 16;
return 2;
default:
return -1;
}
default_to_dyn_v4:
/* assume dynamic IPv4 by default */
dst->len = 4;
dst->v4.s_addr = 0;
return 0;
return 1;
}

View File

@@ -27,6 +27,7 @@ extern const char *in46p_ntoa(const struct in46_prefix *in46p);
extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b);
extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
unsigned int in46a_netmasklen(const struct in46_addr *netmask);
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua);
int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua);
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);

View File

@@ -184,9 +184,19 @@ void in46a_inc(struct in46_addr *addr)
}
}
static bool addr_in_prefix_list(struct in46_addr *addr, struct in46_prefix *list, size_t list_size)
{
int i;
for (i = 0; i < list_size; i++) {
if (in46a_prefix_equal(addr, &list[i].addr))
return true;
}
return false;
}
/* Create new address pool */
int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const struct in46_prefix *stat,
int flags)
int flags, struct in46_prefix *blacklist, size_t blacklist_size)
{
/* Parse only first instance of pool for now */
@@ -210,18 +220,16 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
if (addr.len == sizeof(struct in6_addr))
addr.len = 64/8;
/* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
if (flags & IPPOOL_NOGATEWAY) {
flags |= IPPOOL_NONETWORK;
}
dynsize = (1 << (addr.len*8 - addrprefixlen)) -1;
dynsize = (1 << (addr.len*8 - addrprefixlen));
if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
dynsize--;
if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
dynsize--;
if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
dynsize--;
/* Exclude included blacklist addresses from pool */
for (i = 0; i < blacklist_size; i++) {
if (in46a_within_mask(&blacklist[i].addr, &addr, addrprefixlen))
dynsize--;
}
}
if (!stat || stat->addr.len == 0) {
@@ -232,7 +240,7 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
stataddr = stat->addr;
stataddrprefixlen = stat->prefixlen;
statsize = (1 << (addr.len - stataddrprefixlen + 1)) -1;
statsize = (1 << (stataddr.len*8 - stataddrprefixlen));
if (statsize > IPPOOL_STATSIZE)
statsize = IPPOOL_STATSIZE;
}
@@ -278,13 +286,17 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
(*this)->firstdyn = NULL;
(*this)->lastdyn = NULL;
if (flags & IPPOOL_NOGATEWAY) {
in46a_inc(&addr);
in46a_inc(&addr);
} else if (flags & IPPOOL_NONETWORK) {
if (flags & IPPOOL_NONETWORK) {
in46a_inc(&addr);
}
for (i = 0; i < dynsize; i++) {
if (addr_in_prefix_list(&addr, blacklist, blacklist_size)) {
SYS_ERR(DIP, LOGL_DEBUG, 0,
"addr blacklisted from pool: %s", in46a_ntoa(&addr));
in46a_inc(&addr);
i--;
continue;
}
(*this)->member[i].addr = addr;
in46a_inc(&addr);

View File

@@ -31,7 +31,6 @@
#define IPPOOL_NONETWORK 0x01
#define IPPOOL_NOBROADCAST 0x02
#define IPPOOL_NOGATEWAY 0x04
#define IPPOOL_STATSIZE 0x10000
@@ -72,7 +71,8 @@ extern unsigned long int ippool_hash(struct in46_addr *addr);
/* Create new address pool */
extern int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn,
const struct in46_prefix *stat, int flags);
const struct in46_prefix *stat, int flags,
struct in46_prefix *blacklist, size_t blacklist_size);
/* Delete existing address pool */
extern int ippool_free(struct ippool_t *this);

727
lib/netdev.c Normal file
View File

@@ -0,0 +1,727 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*/
/*
* netdev.c: Contains generic network device related functionality.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <errno.h>
#include <net/route.h>
#include <net/if.h>
#if defined(__linux__)
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#elif defined (__FreeBSD__)
#include <net/if_var.h>
#include <netinet/in_var.h>
#elif defined (__APPLE__)
#include <net/if.h>
#else
#error "Unknown platform!"
#endif
#include "netdev.h"
#include "syserr.h"
#if defined(__linux__)
#include <linux/ipv6.h>
static int netdev_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
int alen = NLMSG_ALIGN(n->nlmsg_len);
struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
if (alen + len > nsize)
return -1;
rta->rta_len = len;
rta->rta_type = type;
memcpy(RTA_DATA(rta), d, dlen);
n->nlmsg_len = alen + len;
return 0;
}
#endif
static int netdev_sifflags(const char *devname, int flags)
{
struct ifreq ifr;
int fd;
memset(&ifr, '\0', sizeof(ifr));
ifr.ifr_flags = flags;
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFFLAGS) failed");
close(fd);
return -1;
}
close(fd);
return 0;
}
int netdev_setaddr4(const char *devname, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask)
{
struct ifreq ifr;
int fd;
memset(&ifr, '\0', sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET;
ifr.ifr_dstaddr.sa_family = AF_INET;
#if defined(__linux__)
ifr.ifr_netmask.sa_family = AF_INET;
#elif defined(__FreeBSD__) || defined (__APPLE__)
((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
sizeof(struct sockaddr_in);
((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
sizeof(struct sockaddr_in);
#endif
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (addr) { /* Set the interface address */
memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
sizeof(*addr));
if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
if (errno != EEXIST) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFADDR) failed");
} else {
SYS_ERR(DTUN, LOGL_NOTICE, errno,
"ioctl(SIOCSIFADDR): Address already exists");
}
close(fd);
return -1;
}
}
if (dstaddr) { /* Set the destination address */
memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
dstaddr, sizeof(*dstaddr));
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFDSTADDR) failed");
close(fd);
return -1;
}
}
if (netmask) { /* Set the netmask */
#if defined(__linux__)
memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
netmask, sizeof(*netmask));
#elif defined(__FreeBSD__) || defined (__APPLE__)
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
netmask->s_addr;
#endif
if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFNETMASK) failed");
close(fd);
return -1;
}
}
close(fd);
netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
/* On linux the route to the interface is set automatically
on FreeBSD we have to do this manually */
#if defined(__FreeBSD__) || defined (__APPLE__)
netdev_addroute(dstaddr, addr, &this->netmask);
#endif
return 0;
}
int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr *dstaddr,
size_t prefixlen)
{
struct in6_ifreq ifr;
int fd;
memset(&ifr, 0, sizeof(ifr));
#if defined(__linux__)
ifr.ifr6_prefixlen = prefixlen;
ifr.ifr6_ifindex = if_nametoindex(devname);
if (ifr.ifr6_ifindex == 0) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", devname);
return -1;
}
#elif defined(__FreeBSD__) || defined (__APPLE__)
strncpy(ifr.ifr_name, devname, IFNAMSIZ);
#endif
/* Create a channel to the NET kernel */
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
return -1;
}
#if defined(__linux__)
if (addr) {
memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
if (errno != EEXIST) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
} else {
SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
}
close(fd);
return -1;
}
}
#if 0
/* FIXME: looks like this is not possible/necessary for IPv6? */
if (dstaddr) {
memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
close(fd);
return -1;
}
}
#endif
#elif defined(__FreeBSD__) || defined (__APPLE__)
if (addr)
memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
if (dstaddr)
memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
close(fd);
return -1;
}
#endif
close(fd);
netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
/* On linux the route to the interface is set automatically
on FreeBSD we have to do this manually */
#if 0 /* FIXME */
//#if defined(__FreeBSD__) || defined (__APPLE__)
netdev_addroute6(dstaddr, addr, prefixlen);
#endif
return 0;
}
int netdev_addaddr4(const char *devname, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask)
{
int fd;
#if defined(__linux__)
struct {
struct nlmsghdr n;
struct ifaddrmsg i;
char buf[TUN_NLBUFSIZE];
} req;
struct sockaddr_nl local;
socklen_t addr_len;
int status;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.n.nlmsg_type = RTM_NEWADDR;
req.i.ifa_family = AF_INET;
req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
req.i.ifa_flags = 0;
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
req.i.ifa_index = if_nametoindex(devname);
if (!req.i.ifa_index) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
return -1;
}
netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
if (dstaddr)
netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = 0;
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
close(fd);
return -1;
}
addr_len = sizeof(local);
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"getsockname() failed");
close(fd);
return -1;
}
if (addr_len != sizeof(local)) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address length %d", addr_len);
close(fd);
return -1;
}
if (local.nl_family != AF_NETLINK) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address family %d", local.nl_family);
close(fd);
return -1;
}
iov.iov_base = (void *)&req.n;
iov.iov_len = req.n.nlmsg_len;
msg.msg_name = (void *)&nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
req.n.nlmsg_seq = 0;
req.n.nlmsg_flags |= NLM_F_ACK;
status = sendmsg(fd, &msg, 0);
if (status != req.n.nlmsg_len) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
close(fd);
return -1;
}
status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
if (status == -1) {
close(fd);
return -1;
}
#elif defined (__FreeBSD__) || defined (__APPLE__)
struct ifaliasreq areq;
memset(&areq, 0, sizeof(areq));
/* Set up interface name */
strncpy(areq.ifra_name, devname, IFNAMSIZ);
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
sizeof(areq.ifra_addr);
((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
sizeof(areq.ifra_mask);
((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
netmask->s_addr;
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
sizeof(areq.ifra_broadaddr);
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
dstaddr->s_addr;
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCAIFADDR) failed");
close(fd);
return -1;
}
#endif
close(fd);
return 0;
}
int netdev_addaddr6(const char *devname, struct in6_addr *addr,
struct in6_addr *dstaddr, int prefixlen)
{
int fd;
#if defined(__linux__)
struct {
struct nlmsghdr n;
struct ifaddrmsg i;
char buf[TUN_NLBUFSIZE];
} req;
struct sockaddr_nl local;
socklen_t addr_len;
int status;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.n.nlmsg_type = RTM_NEWADDR;
req.i.ifa_family = AF_INET6;
req.i.ifa_prefixlen = 64; /* 64 FOR IPv6 */
req.i.ifa_flags = 0;
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
req.i.ifa_index = if_nametoindex(devname);
if (!req.i.ifa_index) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
return -1;
}
netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
if (dstaddr)
netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = 0;
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
close(fd);
return -1;
}
addr_len = sizeof(local);
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"getsockname() failed");
close(fd);
return -1;
}
if (addr_len != sizeof(local)) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address length %d", addr_len);
close(fd);
return -1;
}
if (local.nl_family != AF_NETLINK) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address family %d", local.nl_family);
close(fd);
return -1;
}
iov.iov_base = (void *)&req.n;
iov.iov_len = req.n.nlmsg_len;
msg.msg_name = (void *)&nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
req.n.nlmsg_seq = 0;
req.n.nlmsg_flags |= NLM_F_ACK;
status = sendmsg(fd, &msg, 0);
if (status != req.n.nlmsg_len) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
close(fd);
return -1;
}
status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
if (status == -1) {
close(fd);
return -1;
}
#elif defined (__FreeBSD__) || defined (__APPLE__)
struct ifaliasreq areq;
memset(&areq, 0, sizeof(areq));
/* Set up interface name */
strncpy(areq.ifra_name, devname, IFNAMSIZ);
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCAIFADDR) failed");
close(fd);
return -1;
}
#endif
close(fd);
return 0;
}
static int netdev_route(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
{
int fd;
#if defined(__linux__)
struct rtentry r;
memset(&r, '\0', sizeof(r));
r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
r.rt_dst.sa_family = AF_INET;
r.rt_gateway.sa_family = AF_INET;
r.rt_genmask.sa_family = AF_INET;
memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
sizeof(*gateway));
memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
sizeof(*mask));
if (delete) {
if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCDELRT) failed");
close(fd);
return -1;
}
} else {
if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCADDRT) failed");
close(fd);
return -1;
}
}
#elif defined(__FreeBSD__) || defined (__APPLE__)
struct {
struct rt_msghdr rt;
struct sockaddr_in dst;
struct sockaddr_in gate;
struct sockaddr_in mask;
} req;
struct rt_msghdr *rtm;
if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
memset(&req, 0x00, sizeof(req));
rtm = &req.rt;
rtm->rtm_msglen = sizeof(req);
rtm->rtm_version = RTM_VERSION;
if (delete) {
rtm->rtm_type = RTM_DELETE;
} else {
rtm->rtm_type = RTM_ADD;
}
rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
rtm->rtm_pid = getpid();
rtm->rtm_seq = 0044; /* TODO */
req.dst.sin_family = AF_INET;
req.dst.sin_len = sizeof(req.dst);
req.mask.sin_family = AF_INET;
req.mask.sin_len = sizeof(req.mask);
req.gate.sin_family = AF_INET;
req.gate.sin_len = sizeof(req.gate);
req.dst.sin_addr.s_addr = dst->s_addr;
req.mask.sin_addr.s_addr = mask->s_addr;
req.gate.sin_addr.s_addr = gateway->s_addr;
if (write(fd, rtm, rtm->rtm_msglen) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
close(fd);
return -1;
}
#endif
close(fd);
return 0;
}
int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
{
return netdev_route(dst, gateway, mask, 0);
}
int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
{
return netdev_route(dst, gateway, mask, 1);
}
#include <ifaddrs.h>
/*! Obtain the local address of a network device
* \param[in] devname Target device owning the IP
* \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
* \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
* \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
* \returns The number of ips found following the criteria specified by flags, -1 on error.
*
* This function will fill prefix_list with up to prefix_size IPs following the
* criteria specified by flags parameter. It returns the number of IPs matching
* the criteria. As a result, the number returned can be bigger than
* prefix_size. It can be used with prefix_size=0 to get an estimate of the size
* needed for prefix_list.
*/
int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
{
static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
struct ifaddrs *ifaddr, *ifa;
struct in46_addr netmask;
size_t count = 0;
bool is_ipv6_ll;
if (getifaddrs(&ifaddr) == -1) {
return -1;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue;
if (strcmp(ifa->ifa_name, devname))
continue;
if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
if (count < prefix_size) {
netmask.len = sizeof(netmask4->sin_addr);
netmask.v4 = netmask4->sin_addr;
prefix_list[count].addr.len = sizeof(sin4->sin_addr);
prefix_list[count].addr.v4 = sin4->sin_addr;
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
}
count++;
}
if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
continue;
if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
continue;
if (count < prefix_size) {
netmask.len = sizeof(netmask6->sin6_addr);
netmask.v6 = netmask6->sin6_addr;
prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
prefix_list[count].addr.v6 = sin6->sin6_addr;
prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
}
count++;
}
}
freeifaddrs(ifaddr);
return count;
}

72
lib/netdev.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
/*
* TUN interface functions.
* Copyright (C) 2002, 2003 Mondru AB.
* Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*/
#include <net/if.h>
#include "../lib/in46_addr.h"
#define TUN_NLBUFSIZE 1024
#include "config.h"
/* ipv6 ip type flags for tun_ipv6_local_get() */
enum {
IP_TYPE_IPv4 = 1,
IP_TYPE_IPv6_LINK = 2,
IP_TYPE_IPv6_NONLINK = 4,
};
#define IP_TYPE_IPv6 (IP_TYPE_IPv6_LINK | IP_TYPE_IPv6_NONLINK)
#ifndef HAVE_IPHDR
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
u_int8_t tos;
u_int16_t tot_len;
u_int16_t id;
u_int16_t frag_off;
u_int8_t ttl;
u_int8_t protocol;
u_int16_t check;
u_int32_t saddr;
u_int32_t daddr;
/*The options start here. */
};
#endif /* !HAVE_IPHDR */
extern int netdev_setaddr4(const char *devname, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
extern int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr *dstaddr,
size_t prefixlen);
extern int netdev_addaddr4(const char *devname, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
extern int netdev_addaddr6(const char *devname, struct in6_addr *addr,
struct in6_addr *dstaddr, int prefixlen);
extern int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
extern int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
extern int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
size_t prefix_size, int flags);

593
lib/tun.c
View File

@@ -1,7 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
* Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -42,8 +43,6 @@
#if defined(__linux__)
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#elif defined (__FreeBSD__)
#include <net/if_tun.h>
@@ -59,531 +58,98 @@
#include "tun.h"
#include "syserr.h"
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
#if defined(__linux__)
#include <linux/ipv6.h>
static int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
int alen = NLMSG_ALIGN(n->nlmsg_len);
struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
if (alen + len > nsize)
return -1;
rta->rta_len = len;
rta->rta_type = type;
memcpy(RTA_DATA(rta), d, dlen);
n->nlmsg_len = alen + len;
return 0;
}
#endif
static int tun_sifflags(struct tun_t *this, int flags)
{
struct ifreq ifr;
int fd;
memset(&ifr, '\0', sizeof(ifr));
ifr.ifr_flags = flags;
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFFLAGS) failed");
close(fd);
return -1;
}
close(fd);
return 0;
}
int tun_addaddr(struct tun_t *this,
struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask)
{
#if defined(__linux__)
struct {
struct nlmsghdr n;
struct ifaddrmsg i;
char buf[TUN_NLBUFSIZE];
} req;
struct sockaddr_nl local;
socklen_t addr_len;
int fd;
int status;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg;
if (!this->addrs) /* Use ioctl for first addr to make ping work */
return tun_setaddr4(this, addr, dstaddr, netmask);
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.n.nlmsg_type = RTM_NEWADDR;
req.i.ifa_family = AF_INET;
req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
req.i.ifa_flags = 0;
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
req.i.ifa_index = if_nametoindex(this->devname);
if (!req.i.ifa_index) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
return -1;
}
tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = 0;
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
close(fd);
return -1;
}
addr_len = sizeof(local);
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"getsockname() failed");
close(fd);
return -1;
}
if (addr_len != sizeof(local)) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address length %d", addr_len);
close(fd);
return -1;
}
if (local.nl_family != AF_NETLINK) {
SYS_ERR(DTUN, LOGL_ERROR, 0,
"Wrong address family %d", local.nl_family);
close(fd);
return -1;
}
iov.iov_base = (void *)&req.n;
iov.iov_len = req.n.nlmsg_len;
msg.msg_name = (void *)&nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
req.n.nlmsg_seq = 0;
req.n.nlmsg_flags |= NLM_F_ACK;
status = sendmsg(fd, &msg, 0);
if (status != req.n.nlmsg_len) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
close(fd);
return -1;
}
status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
if (status == -1) {
close(fd);
return -1;
}
close(fd);
this->addrs++;
return 0;
#elif defined (__FreeBSD__) || defined (__APPLE__)
int fd;
struct ifaliasreq areq;
/* TODO: Is this needed on FreeBSD? */
if (!this->addrs) /* Use ioctl for first addr to make ping work */
return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
memset(&areq, 0, sizeof(areq));
/* Set up interface name */
strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
sizeof(areq.ifra_addr);
((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
sizeof(areq.ifra_mask);
((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
netmask->s_addr;
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
sizeof(areq.ifra_broadaddr);
((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
dstaddr->s_addr;
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCAIFADDR) failed");
close(fd);
return -1;
}
close(fd);
this->addrs++;
return 0;
#endif
}
#include "gtp-kernel.h"
static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask)
{
struct ifreq ifr;
int fd;
int rc;
rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
if (rc < 0)
return rc;
memset(&ifr, '\0', sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET;
ifr.ifr_dstaddr.sa_family = AF_INET;
#if defined(__linux__)
ifr.ifr_netmask.sa_family = AF_INET;
#elif defined(__FreeBSD__) || defined (__APPLE__)
((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
sizeof(struct sockaddr_in);
((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
sizeof(struct sockaddr_in);
#endif
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
if (addr) { /* Set the interface address */
if (addr)
this->addr.s_addr = addr->s_addr;
memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
sizeof(*addr));
if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
if (errno != EEXIST) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFADDR) failed");
} else {
SYS_ERR(DTUN, LOGL_NOTICE, errno,
"ioctl(SIOCSIFADDR): Address already exists");
}
close(fd);
return -1;
}
}
if (dstaddr) { /* Set the destination address */
if (dstaddr)
this->dstaddr.s_addr = dstaddr->s_addr;
memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
dstaddr, sizeof(*dstaddr));
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFDSTADDR) failed");
close(fd);
return -1;
}
}
if (netmask) { /* Set the netmask */
if (netmask)
this->netmask.s_addr = netmask->s_addr;
#if defined(__linux__)
memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
netmask, sizeof(*netmask));
#elif defined(__FreeBSD__) || defined (__APPLE__)
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
netmask->s_addr;
#endif
if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCSIFNETMASK) failed");
close(fd);
return -1;
}
}
close(fd);
this->addrs++;
/* On linux the route to the interface is set automatically
on FreeBSD we have to do this manually */
/* TODO: How does it work on Solaris? */
tun_sifflags(this, IFF_UP | IFF_RUNNING);
#if defined(__FreeBSD__) || defined (__APPLE__)
tun_addroute(this, dstaddr, addr, &this->netmask);
this->routes = 1;
#endif
return 0;
return rc;
}
static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
size_t prefixlen)
{
struct in6_ifreq ifr;
int fd;
memset(&ifr, 0, sizeof(ifr));
#if defined(__linux__)
ifr.ifr6_prefixlen = prefixlen;
ifr.ifr6_ifindex = if_nametoindex(this->devname);
if (ifr.ifr6_ifindex == 0) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
return -1;
}
#elif defined(__FreeBSD__) || defined (__APPLE__)
strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
#endif
/* Create a channel to the NET kernel */
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
return -1;
}
#if defined(__linux__)
if (addr) {
memcpy(&this->addr, addr, sizeof(*addr));
memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
if (errno != EEXIST) {
SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
} else {
SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address alreadsy exists");
}
close(fd);
return -1;
}
}
#if 0
/* FIXME: looks like this is not possible/necessary for IPv6? */
if (dstaddr) {
memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
close(fd);
return -1;
}
}
#endif
#elif defined(__FreeBSD__) || defined (__APPLE__)
if (addr)
memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
int rc;
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
if (rc < 0)
return rc;
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);
memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
this->addrs++;
/* On linux the route to the interface is set automatically
on FreeBSD we have to do this manually */
/* TODO: How does it work on Solaris? */
tun_sifflags(this, IFF_UP | IFF_RUNNING);
#if 0 /* FIXME */
//#if defined(__FreeBSD__) || defined (__APPLE__)
tun_addroute6(this, dstaddr, addr, prefixlen);
#if defined(__FreeBSD__) || defined (__APPLE__)
this->routes = 1;
#endif
return 0;
return rc;
}
int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
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_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
case 16:
return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
default:
return -1;
}
}
static int tun_route(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask, int delete)
{
#if defined(__linux__)
struct rtentry r;
int fd;
memset(&r, '\0', sizeof(r));
r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
/* Create a channel to the NET kernel. */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
r.rt_dst.sa_family = AF_INET;
r.rt_gateway.sa_family = AF_INET;
r.rt_genmask.sa_family = AF_INET;
memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
sizeof(*gateway));
memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
sizeof(*mask));
if (delete) {
if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCDELRT) failed");
close(fd);
return -1;
}
} else {
if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno,
"ioctl(SIOCADDRT) failed");
close(fd);
return -1;
}
}
close(fd);
return 0;
#elif defined(__FreeBSD__) || defined (__APPLE__)
struct {
struct rt_msghdr rt;
struct sockaddr_in dst;
struct sockaddr_in gate;
struct sockaddr_in mask;
} req;
int fd;
struct rt_msghdr *rtm;
if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
return -1;
}
memset(&req, 0x00, sizeof(req));
rtm = &req.rt;
rtm->rtm_msglen = sizeof(req);
rtm->rtm_version = RTM_VERSION;
if (delete) {
rtm->rtm_type = RTM_DELETE;
} else {
rtm->rtm_type = RTM_ADD;
}
rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
rtm->rtm_pid = getpid();
rtm->rtm_seq = 0044; /* TODO */
req.dst.sin_family = AF_INET;
req.dst.sin_len = sizeof(req.dst);
req.mask.sin_family = AF_INET;
req.mask.sin_len = sizeof(req.mask);
req.gate.sin_family = AF_INET;
req.gate.sin_len = sizeof(req.gate);
req.dst.sin_addr.s_addr = dst->s_addr;
req.mask.sin_addr.s_addr = mask->s_addr;
req.gate.sin_addr.s_addr = gateway->s_addr;
if (write(fd, rtm, rtm->rtm_msglen) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
close(fd);
return -1;
}
close(fd);
return 0;
#endif
}
int tun_addroute(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask)
{
return tun_route(this, dst, gateway, mask, 0);
}
int tun_delroute(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask)
{
return tun_route(this, dst, gateway, mask, 1);
}
int tun_new(struct tun_t **tun, const char *dev_name)
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
{
#if defined(__linux__)
@@ -606,6 +172,7 @@ int tun_new(struct tun_t **tun, const char *dev_name)
(*tun)->routes = 0;
#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");
@@ -628,9 +195,27 @@ int tun_new(struct tun_t **tun, const char *dev_name)
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
return 0;
} else {
strncpy((*tun)->devname, dev_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
(*tun)->fd = -1;
if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
strerror(errno));
return -1;
}
LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
return 0;
}
#elif defined(__FreeBSD__) || defined (__APPLE__)
if (use_kernel) {
LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
return -1;
}
/* Find suitable device */
for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
@@ -682,12 +267,16 @@ int tun_free(struct tun_t *tun)
{
if (tun->routes) {
tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
}
if (tun->fd >= 0) {
if (close(tun->fd)) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
}
}
gtp_kernel_stop(tun->devname);
/* TODO: For solaris we need to unlink streams */
@@ -748,3 +337,21 @@ int tun_runscript(struct tun_t *tun, char *script)
}
return 0;
}
/*! Obtain the local address of the tun device.
* \param[in] tun Target device owning the IP
* \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
* \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
* \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
* \returns The number of ips found following the criteria specified by flags, -1 on error.
*
* This function will fill prefix_list with up to prefix_size IPs following the
* criteria specified by flags parameter. It returns the number of IPs matching
* the criteria. As a result, the number returned can be bigger than
* prefix_size. It can be used with prefix_size=0 to get an estimate of the size
* needed for prefix_list.
*/
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
{
return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
}

View File

@@ -1,7 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003 Mondru AB.
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
* Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -13,6 +13,7 @@
#ifndef _TUN_H
#define _TUN_H
#include <stdbool.h>
#include <net/if.h>
#include "../lib/in46_addr.h"
@@ -20,33 +21,9 @@
#define PACKET_MAX 8196 /* Maximum packet size we receive */
#define TUN_SCRIPTSIZE 256
#define TUN_ADDRSIZE 128
#define TUN_NLBUFSIZE 1024
#include "config.h"
#ifndef HAVE_IPHDR
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
u_int8_t tos;
u_int16_t tot_len;
u_int16_t id;
u_int16_t frag_off;
u_int8_t ttl;
u_int8_t protocol;
u_int16_t check;
u_int32_t saddr;
u_int32_t daddr;
/*The options start here. */
};
#endif /* !HAVE_IPHDR */
#include "netdev.h"
/* ***********************************************************
* Information storage for each tun instance
@@ -65,19 +42,13 @@ struct tun_t {
void *priv;
};
extern int tun_new(struct tun_t **tun, const char *dev_name);
extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u);
extern int tun_free(struct tun_t *tun);
extern int tun_decaps(struct tun_t *this);
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
struct in46_addr *his_adr, size_t prefixlen);
int tun_addroute(struct tun_t *this, struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask);
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
struct in46_addr *dstaddr, size_t prefixlen);
extern int tun_set_cb_ind(struct tun_t *this,
int (*cb_ind) (struct tun_t * tun, void *pack,
@@ -85,4 +56,7 @@ extern int tun_set_cb_ind(struct tun_t *this,
extern int tun_runscript(struct tun_t *tun, char *script);
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
size_t prefix_size, int flags);
#endif /* !_TUN_H */

View File

@@ -5,5 +5,11 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
sgsnemu_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS)
if ENABLE_GTP_KERNEL
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
sgsnemu_LDADD += $(LIBGTPNL_LIBS)
endif
sgsnemu_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,19 @@
# notice and this permission notice is included in all copies or
# substantial portions of the software.
#
# Use "gengetopt --conf-parser < cmdline.ggo"
# Use
# gengetopt --conf-parser < cmdline.ggo
# linux-2.6/scripts/Lindent cmdline.c
# linux-2.6/scripts/Lindent cmdline.h
# sed -i -e 's/int qose1_arg;/unsigned long long int qose1_arg;/' cmdline.h
# to generate cmdline.c and cmdline.h
package "sgsnemu"
defmode "createif" modedesc="any option of this mode is related to tun interface, \
all payload going in and out via tunN interface"
defmode "pinghost" modedesc="generate ICMP payload inside G-PDU without setting up tun interface"
option "debug" d "Run in debug mode" flag off
option "conf" c "Read configuration file" string no
@@ -16,7 +26,7 @@ option "pidfile" - "Filename of process id file" string default="./sgsn
option "statedir" - "Directory of nonvolatile data" string default="./" no
option "dns" - "DNS Server to use" string no
option "listen" l "Local interface" string no
option "listen" l "Local host" string no
option "remote" r "Remote host" string no
option "contexts" - "Number of contexts" int default="1" no
@@ -25,23 +35,36 @@ option "timelimit" - "Exit after timelimit seconds" int default="0" no
option "gtpversion" - "GTP version to use" int default="1" no
option "apn" a "Access point name" string default="internet" no
option "selmode" - "Selection mode" int default="0x01" no
option "rattype" - "Radio Access Technology Type" int default="1" no typestr="1..5"
option "userloc" - "User Location Information" string default="02509946241207" no typestr="type.MCC.MNC.LAC.CIorSACorRAC"
option "rai" - "Routing Area Information" string default="02509946241207" no typestr="MCC.MNC.LAC.RAC"
option "mstz" - "MS Time Zone" string default="0" no typestr="sign.NbQuartersOfAnHour.DSTAdjustment"
option "imeisv" - "IMEI(SV) International Mobile Equipment Identity (and Software Version)" string default="2143658709214365" no
option "norecovery" - "Do not send recovery" flag off
option "imsi" i "IMSI" string default="240010123456789" no
option "nsapi" - "NSAPI" int default="0" no
option "msisdn" m "Mobile Station ISDN number" string default="46702123456" no
option "qos" q "Requested quality of service" int default="0x0b921f" no
option "qos" q "Requested quality of service" int default="0x000b921f" no
option "qose1" - "Requested quality of service Extension 1" int default="0x9396404074f9ffff" no
option "qose2" - "Requested quality of service Extension 2" int default="0x11" no
option "qose3" - "Requested quality of service Extension 3" int default="0x0101" no
option "qose4" - "Requested quality of service Extension 4" int default="0x4040" no
option "charging" - "Charging characteristics" int default="0x0800" no
option "uid" u "Login user ID" string default="mig" no
option "pwd" p "Login password" string default="hemmelig" no
option "createif" - "Create local network interface" flag off
option "net" n "Network address for local interface" string no
option "defaultroute" - "Create default route" flag off
option "ipup" - "Script to run after link-up" string no
option "ipdown" - "Script to run after link-down" string no
modeoption "createif" - "Create local network interface" flag off mode="createif"
modeoption "net" n "Network address for local interface" string dependon="createif" no mode="createif"
modeoption "defaultroute" - "Create default route" flag dependon="createif" off mode="createif"
modeoption "ipup" - "Script to run after link-up" string dependon="createif" no mode="createif"
modeoption "ipdown" - "Script to run after link-down" string dependon="createif" no mode="createif"
modeoption "tun-device" - "Name of the local network interface" string dependon="createif" no mode="createif"
option "pinghost" - "Ping remote host" string no
option "pingrate" - "Number of ping req per second" unsigned int default="1" no
option "pingsize" - "Number of ping data bytes" unsigned int default="56" no
option "pingcount" - "Number of ping req to send" unsigned int default="0" no
option "pingquiet" - "Do not print ping packet info" flag off
modeoption "pinghost" - "Ping remote host" string no mode="pinghost"
modeoption "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no mode="pinghost"
modeoption "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no mode="pinghost"
modeoption "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no mode="pinghost"
modeoption "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" off mode="pinghost"
option "no-tx-gpdu-seq" - "Don't transmit G-PDU sequence nums" flag off
option "pdp-type" t "PDP Type" string default="v4" no typestr="(v4|v6)"

View File

@@ -1,6 +1,9 @@
/* cmdline.h */
/* File autogenerated by gengetopt version 2.17 */
/** @file cmdline.h
* @brief The header file for the command line option parser
* generated by GNU Gengetopt version 2.22.6
* http://www.gnu.org/software/gengetopt.
* DO NOT modify this file, since it can be overwritten
* @author GNU Gengetopt by Lorenzo Bettini */
#ifndef CMDLINE_H
#define CMDLINE_H
@@ -10,197 +13,517 @@
#include "config.h"
#endif
#include <stdio.h> /* for FILE */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef CMDLINE_PARSER_PACKAGE
#define CMDLINE_PARSER_PACKAGE PACKAGE
/** @brief the program name (used for printing errors) */
#define CMDLINE_PARSER_PACKAGE "sgsnemu"
#endif
#ifndef CMDLINE_PARSER_PACKAGE_NAME
/** @brief the complete program name (used for help and version) */
#define CMDLINE_PARSER_PACKAGE_NAME "sgsnemu"
#endif
#ifndef CMDLINE_PARSER_VERSION
/** @brief the program version */
#define CMDLINE_PARSER_VERSION VERSION
#endif
/** @brief Where the command line options are stored */
struct gengetopt_args_info {
const char *help_help; /* Print help and exit help description. */
const char *version_help; /* Print version and exit help description. */
int debug_flag; /* Run in debug mode (default=off). */
const char *debug_help; /* Run in debug mode help description. */
char *conf_arg; /* Read configuration file. */
char *conf_orig; /* Read configuration file original value given at command line. */
const char *conf_help; /* Read configuration file help description. */
char *pidfile_arg; /* Filename of process id file (default='./sgsnemu.pid'). */
char *pidfile_orig; /* Filename of process id file original value given at command line. */
const char *pidfile_help; /* Filename of process id file help description. */
char *statedir_arg; /* Directory of nonvolatile data (default='./'). */
char *statedir_orig; /* Directory of nonvolatile data original value given at command line. */
const char *statedir_help; /* Directory of nonvolatile data help description. */
char *dns_arg; /* DNS Server to use. */
char *dns_orig; /* DNS Server to use original value given at command line. */
const char *dns_help; /* DNS Server to use help description. */
char *listen_arg; /* Local interface. */
char *listen_orig; /* Local interface original value given at command line. */
const char *listen_help; /* Local interface help description. */
char *remote_arg; /* Remote host. */
char *remote_orig; /* Remote host original value given at command line. */
const char *remote_help; /* Remote host help description. */
int contexts_arg; /* Number of contexts (default='1'). */
char *contexts_orig; /* Number of contexts original value given at command line. */
const char *contexts_help; /* Number of contexts help description. */
int timelimit_arg; /* Exit after timelimit seconds (default='0'). */
char *timelimit_orig; /* Exit after timelimit seconds original value given at command line. */
const char *timelimit_help; /* Exit after timelimit seconds help description. */
int gtpversion_arg; /* GTP version to use (default='1'). */
char *gtpversion_orig; /* GTP version to use original value given at command line. */
const char *gtpversion_help; /* GTP version to use help description. */
char *apn_arg; /* Access point name (default='internet'). */
char *apn_orig; /* Access point name original value given at command line. */
const char *apn_help; /* Access point name help description. */
int selmode_arg; /* Selection mode (default='0x01'). */
char *selmode_orig; /* Selection mode original value given at command line. */
const char *selmode_help; /* Selection mode help description. */
char *rattype_arg; /* Radio Access Technology Type (optional). */
const char *help_help;
/**< @brief Print help and exit help description. */
const char *version_help;
/**< @brief Print version and exit help description. */
int debug_flag;
/**< @brief Run in debug mode (default=off). */
const char *debug_help;
/**< @brief Run in debug mode help description. */
char *conf_arg;
/**< @brief Read configuration file. */
char *conf_orig;
/**< @brief Read configuration file original value given at command line. */
const char *conf_help;
/**< @brief Read configuration file help description. */
char *pidfile_arg;
/**< @brief Filename of process id file (default='./sgsnemu.pid'). */
char *pidfile_orig;
/**< @brief Filename of process id file original value given at command line. */
const char *pidfile_help;
/**< @brief Filename of process id file help description. */
char *statedir_arg;
/**< @brief Directory of nonvolatile data (default='./'). */
char *statedir_orig;
/**< @brief Directory of nonvolatile data original value given at command line. */
const char *statedir_help;
/**< @brief Directory of nonvolatile data help description. */
char *dns_arg;
/**< @brief DNS Server to use. */
char *dns_orig;
/**< @brief DNS Server to use original value given at command line. */
const char *dns_help;
/**< @brief DNS Server to use help description. */
char *listen_arg;
/**< @brief Local host. */
char *listen_orig;
/**< @brief Local host original value given at command line. */
const char *listen_help;
/**< @brief Local host help description. */
char *remote_arg;
/**< @brief Remote host. */
char *remote_orig;
/**< @brief Remote host original value given at command line. */
const char *remote_help;
/**< @brief Remote host help description. */
int contexts_arg;
/**< @brief Number of contexts (default='1'). */
char *contexts_orig;
/**< @brief Number of contexts original value given at command line. */
const char *contexts_help;
/**< @brief Number of contexts help description. */
int timelimit_arg;
/**< @brief Exit after timelimit seconds (default='0'). */
char *timelimit_orig;
/**< @brief Exit after timelimit seconds original value given at command line. */
const char *timelimit_help;
/**< @brief Exit after timelimit seconds help description. */
int gtpversion_arg;
/**< @brief GTP version to use (default='1'). */
char *gtpversion_orig;
/**< @brief GTP version to use original value given at command line. */
const char *gtpversion_help;
/**< @brief GTP version to use help description. */
char *apn_arg;
/**< @brief Access point name (default='internet'). */
char *apn_orig;
/**< @brief Access point name original value given at command line. */
const char *apn_help;
/**< @brief Access point name help description. */
int selmode_arg;
/**< @brief Selection mode (default='0x01'). */
char *selmode_orig;
/**< @brief Selection mode original value given at command line. */
const char *selmode_help;
/**< @brief Selection mode help description. */
int rattype_arg;
/**< @brief Radio Access Technology Type (default='1'). */
char *rattype_orig;
char *rattype_help;
char *userloc_arg; /* User Location Information (optional). */
/**< @brief Radio Access Technology Type original value given at command line. */
const char *rattype_help;
/**< @brief Radio Access Technology Type help description. */
char *userloc_arg;
/**< @brief User Location Information (default='02509946241207'). */
char *userloc_orig;
char *userloc_help;
char *rai_arg; /* Routing Area Information (optional). */
/**< @brief User Location Information original value given at command line. */
const char *userloc_help;
/**< @brief User Location Information help description. */
char *rai_arg;
/**< @brief Routing Area Information (default='02509946241207'). */
char *rai_orig;
char *rai_help;
char *mstz_arg; /* MS Time Zone (optional). */
/**< @brief Routing Area Information original value given at command line. */
const char *rai_help;
/**< @brief Routing Area Information help description. */
char *mstz_arg;
/**< @brief MS Time Zone (default='0'). */
char *mstz_orig;
char *mstz_help;
char *imeisv_arg; /* IMEI(SV) (optional). */
/**< @brief MS Time Zone original value given at command line. */
const char *mstz_help;
/**< @brief MS Time Zone help description. */
char *imeisv_arg;
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) (default='2143658709214365'). */
char *imeisv_orig;
char *imeisv_help;
char *imsi_arg; /* IMSI (default='240010123456789'). */
char *imsi_orig; /* IMSI original value given at command line. */
const char *imsi_help; /* IMSI help description. */
int nsapi_arg; /* NSAPI (default='0'). */
char *nsapi_orig; /* NSAPI original value given at command line. */
const char *nsapi_help; /* NSAPI help description. */
char *msisdn_arg; /* Mobile Station ISDN number (default='46702123456'). */
char *msisdn_orig; /* Mobile Station ISDN number original value given at command line. */
const char *msisdn_help; /* Mobile Station ISDN number help description. */
int qos_arg; /* Requested quality of service (default='0x0b921f'). */
char *qos_orig; /* Requested quality of service original value given at command line. */
const char *qos_help; /* Requested quality of service help description. */
unsigned long long int qose1_arg; /* Requested quality of service Extension 1 */
char *qose1_orig; /* Requested quality of service Extension 1 original value given at command line. */
int qose2_arg; /* Requested quality of service Extension 2 */
char *qose2_orig; /* Requested quality of service Extension 2 original value given at command line. */
int qose3_arg; /* Requested quality of service Extension 3 */
char *qose3_orig; /* Requested quality of service Extension 3 original value given at command line. */
int qose4_arg; /* Requested quality of service Extension 4 */
char *qose4_orig; /* Requested quality of service Extension 4 original value given at command line. */
int charging_arg; /* Charging characteristics (default='0x0800'). */
char *charging_orig; /* Charging characteristics original value given at command line. */
const char *charging_help; /* Charging characteristics help description. */
char *uid_arg; /* Login user ID (default='mig'). */
char *uid_orig; /* Login user ID original value given at command line. */
const char *uid_help; /* Login user ID help description. */
char *pwd_arg; /* Login password (default='hemmelig'). */
char *pwd_orig; /* Login password original value given at command line. */
const char *pwd_help; /* Login password help description. */
int createif_flag; /* Create local network interface (default=off). */
const char *createif_help; /* Create local network interface help description. */
char *net_arg; /* Network address for local interface. */
char *net_orig; /* Network address for local interface original value given at command line. */
const char *net_help; /* Network address for local interface help description. */
int defaultroute_flag; /* Create default route (default=off). */
const char *defaultroute_help; /* Create default route help description. */
char *ipup_arg; /* Script to run after link-up. */
char *ipup_orig; /* Script to run after link-up original value given at command line. */
const char *ipup_help; /* Script to run after link-up help description. */
char *ipdown_arg; /* Script to run after link-down. */
char *ipdown_orig; /* Script to run after link-down original value given at command line. */
const char *ipdown_help; /* Script to run after link-down help description. */
char *pinghost_arg; /* Ping remote host. */
char *pinghost_orig; /* Ping remote host original value given at command line. */
const char *pinghost_help; /* Ping remote host help description. */
int pingrate_arg; /* Number of ping req per second (default='1'). */
char *pingrate_orig; /* Number of ping req per second original value given at command line. */
const char *pingrate_help; /* Number of ping req per second help description. */
int pingsize_arg; /* Number of ping data bytes (default='56'). */
char *pingsize_orig; /* Number of ping data bytes original value given at command line. */
const char *pingsize_help; /* Number of ping data bytes help description. */
int pingcount_arg; /* Number of ping req to send (default='0'). */
char *pingcount_orig; /* Number of ping req to send original value given at command line. */
const char *pingcount_help; /* Number of ping req to send help description. */
int pingquiet_flag; /* Do not print ping packet info (default=off). */
const char *pingquiet_help; /* Do not print ping packet info help description. */
int norecovery_flag; /* Do not print ping packet info (default=off). */
const char *norecovery_help; /* Do not print ping packet info help description. */
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) original value given at command line. */
const char *imeisv_help;
/**< @brief IMEI(SV) International Mobile Equipment Identity (and Software Version) help description. */
int norecovery_flag;
/**< @brief Do not send recovery (default=off). */
const char *norecovery_help;
/**< @brief Do not send recovery help description. */
char *imsi_arg;
/**< @brief IMSI (default='240010123456789'). */
char *imsi_orig;
/**< @brief IMSI original value given at command line. */
const char *imsi_help;
/**< @brief IMSI help description. */
int nsapi_arg;
/**< @brief NSAPI (default='0'). */
char *nsapi_orig;
/**< @brief NSAPI original value given at command line. */
const char *nsapi_help;
/**< @brief NSAPI help description. */
char *msisdn_arg;
/**< @brief Mobile Station ISDN number (default='46702123456'). */
char *msisdn_orig;
/**< @brief Mobile Station ISDN number original value given at command line. */
const char *msisdn_help;
/**< @brief Mobile Station ISDN number help description. */
int qos_arg;
/**< @brief Requested quality of service (default='0x000b921f'). */
char *qos_orig;
/**< @brief Requested quality of service original value given at command line. */
const char *qos_help;
/**< @brief Requested quality of service help description. */
unsigned long long int qose1_arg;
/**< @brief Requested quality of service Extension 1 (default='0x9396404074f9ffff'). */
char *qose1_orig;
/**< @brief Requested quality of service Extension 1 original value given at command line. */
const char *qose1_help;
/**< @brief Requested quality of service Extension 1 help description. */
int qose2_arg;
/**< @brief Requested quality of service Extension 2 (default='0x11'). */
char *qose2_orig;
/**< @brief Requested quality of service Extension 2 original value given at command line. */
const char *qose2_help;
/**< @brief Requested quality of service Extension 2 help description. */
int qose3_arg;
/**< @brief Requested quality of service Extension 3 (default='0x0101'). */
char *qose3_orig;
/**< @brief Requested quality of service Extension 3 original value given at command line. */
const char *qose3_help;
/**< @brief Requested quality of service Extension 3 help description. */
int qose4_arg;
/**< @brief Requested quality of service Extension 4 (default='0x4040'). */
char *qose4_orig;
/**< @brief Requested quality of service Extension 4 original value given at command line. */
const char *qose4_help;
/**< @brief Requested quality of service Extension 4 help description. */
int charging_arg;
/**< @brief Charging characteristics (default='0x0800'). */
char *charging_orig;
/**< @brief Charging characteristics original value given at command line. */
const char *charging_help;
/**< @brief Charging characteristics help description. */
char *uid_arg;
/**< @brief Login user ID (default='mig'). */
char *uid_orig;
/**< @brief Login user ID original value given at command line. */
const char *uid_help;
/**< @brief Login user ID help description. */
char *pwd_arg;
/**< @brief Login password (default='hemmelig'). */
char *pwd_orig;
/**< @brief Login password original value given at command line. */
const char *pwd_help;
/**< @brief Login password help description. */
int createif_flag;
/**< @brief Create local network interface (default=off). */
const char *createif_help;
/**< @brief Create local network interface help description. */
char *net_arg;
/**< @brief Network address for local interface. */
char *net_orig;
/**< @brief Network address for local interface original value given at command line. */
const char *net_help;
/**< @brief Network address for local interface help description. */
int defaultroute_flag;
/**< @brief Create default route (default=off). */
const char *defaultroute_help;
/**< @brief Create default route help description. */
char *ipup_arg;
/**< @brief Script to run after link-up. */
char *ipup_orig;
/**< @brief Script to run after link-up original value given at command line. */
const char *ipup_help;
/**< @brief Script to run after link-up help description. */
char *ipdown_arg;
/**< @brief Script to run after link-down. */
char *ipdown_orig;
/**< @brief Script to run after link-down original value given at command line. */
const char *ipdown_help;
/**< @brief Script to run after link-down help description. */
char *tun_device_arg;
/**< @brief Name of the local network interface. */
char *tun_device_orig;
/**< @brief Name of the local network interface original value given at command line. */
const char *tun_device_help;
/**< @brief Name of the local network interface help description. */
char *pinghost_arg;
/**< @brief Ping remote host. */
char *pinghost_orig;
/**< @brief Ping remote host original value given at command line. */
const char *pinghost_help;
/**< @brief Ping remote host help description. */
int pingrate_arg;
/**< @brief Number of ping req per second (default='1'). */
char *pingrate_orig;
/**< @brief Number of ping req per second original value given at command line. */
const char *pingrate_help;
/**< @brief Number of ping req per second help description. */
int pingsize_arg;
/**< @brief Number of ping data bytes (default='56'). */
char *pingsize_orig;
/**< @brief Number of ping data bytes original value given at command line. */
const char *pingsize_help;
/**< @brief Number of ping data bytes help description. */
int pingcount_arg;
/**< @brief Number of ping req to send (default='0'). */
char *pingcount_orig;
/**< @brief Number of ping req to send original value given at command line. */
const char *pingcount_help;
/**< @brief Number of ping req to send help description. */
int pingquiet_flag;
/**< @brief Do not print ping packet info (default=off). */
const char *pingquiet_help;
/**< @brief Do not print ping packet info help description. */
int no_tx_gpdu_seq_flag;
/**< @brief Don't transmit G-PDU sequence nums (default=off). */
const char *no_tx_gpdu_seq_help;
/**< @brief Don't transmit G-PDU sequence nums help description. */
char *pdp_type_arg;
/**< @brief PDP Type (default='v4'). */
char *pdp_type_orig;
/**< @brief PDP Type original value given at command line. */
const char *pdp_type_help;
/**< @brief PDP Type help description. */
int help_given; /* Whether help was given. */
int version_given; /* Whether version was given. */
int debug_given; /* Whether debug was given. */
int conf_given; /* Whether conf was given. */
int pidfile_given; /* Whether pidfile was given. */
int statedir_given; /* Whether statedir was given. */
int dns_given; /* Whether dns was given. */
int listen_given; /* Whether listen was given. */
int remote_given; /* Whether remote was given. */
int contexts_given; /* Whether contexts was given. */
int timelimit_given; /* Whether timelimit was given. */
int gtpversion_given; /* Whether gtpversion was given. */
int apn_given; /* Whether apn was given. */
int selmode_given; /* Whether selmode was given. */
int rattype_given; /* Whether rattype was given. */
int userloc_given; /* Whether userloc was given. */
int rai_given; /* Whether RAI was given. */
int mstz_given; /* Whether mstz was given. */
int imeisv_given; /* Whether imeisv was given. */
int imsi_given; /* Whether imsi was given. */
int nsapi_given; /* Whether nsapi was given. */
int msisdn_given; /* Whether msisdn was given. */
int qos_given; /* Whether qos was given. */
int qose1_given; /* Whether qos Extension 1 was given. */
int qose2_given; /* Whether qos Extension 2 was given. */
int qose3_given; /* Whether qos Extension 3 was given. */
int qose4_given; /* Whether qos Extension 4 was given. */
int charging_given; /* Whether charging was given. */
int uid_given; /* Whether uid was given. */
int pwd_given; /* Whether pwd was given. */
int createif_given; /* Whether createif was given. */
int net_given; /* Whether net was given. */
int defaultroute_given; /* Whether defaultroute was given. */
int ipup_given; /* Whether ipup was given. */
int ipdown_given; /* Whether ipdown was given. */
int pinghost_given; /* Whether pinghost was given. */
int pingrate_given; /* Whether pingrate was given. */
int pingsize_given; /* Whether pingsize was given. */
int pingcount_given; /* Whether pingcount was given. */
int pingquiet_given; /* Whether pingquiet was given. */
int norecovery_given; /* Whether norecovery was given. */
unsigned int help_given;
/**< @brief Whether help was given. */
unsigned int version_given;
/**< @brief Whether version was given. */
unsigned int debug_given;
/**< @brief Whether debug was given. */
unsigned int conf_given;
/**< @brief Whether conf was given. */
unsigned int pidfile_given;
/**< @brief Whether pidfile was given. */
unsigned int statedir_given;
/**< @brief Whether statedir was given. */
unsigned int dns_given;
/**< @brief Whether dns was given. */
unsigned int listen_given;
/**< @brief Whether listen was given. */
unsigned int remote_given;
/**< @brief Whether remote was given. */
unsigned int contexts_given;
/**< @brief Whether contexts was given. */
unsigned int timelimit_given;
/**< @brief Whether timelimit was given. */
unsigned int gtpversion_given;
/**< @brief Whether gtpversion was given. */
unsigned int apn_given;
/**< @brief Whether apn was given. */
unsigned int selmode_given;
/**< @brief Whether selmode was given. */
unsigned int rattype_given;
/**< @brief Whether rattype was given. */
unsigned int userloc_given;
/**< @brief Whether userloc was given. */
unsigned int rai_given;
/**< @brief Whether rai was given. */
unsigned int mstz_given;
/**< @brief Whether mstz was given. */
unsigned int imeisv_given;
/**< @brief Whether imeisv was given. */
unsigned int norecovery_given;
/**< @brief Whether norecovery was given. */
unsigned int imsi_given;
/**< @brief Whether imsi was given. */
unsigned int nsapi_given;
/**< @brief Whether nsapi was given. */
unsigned int msisdn_given;
/**< @brief Whether msisdn was given. */
unsigned int qos_given;
/**< @brief Whether qos was given. */
unsigned int qose1_given;
/**< @brief Whether qose1 was given. */
unsigned int qose2_given;
/**< @brief Whether qose2 was given. */
unsigned int qose3_given;
/**< @brief Whether qose3 was given. */
unsigned int qose4_given;
/**< @brief Whether qose4 was given. */
unsigned int charging_given;
/**< @brief Whether charging was given. */
unsigned int uid_given;
/**< @brief Whether uid was given. */
unsigned int pwd_given;
/**< @brief Whether pwd was given. */
unsigned int createif_given;
/**< @brief Whether createif was given. */
unsigned int net_given;
/**< @brief Whether net was given. */
unsigned int defaultroute_given;
/**< @brief Whether defaultroute was given. */
unsigned int ipup_given;
/**< @brief Whether ipup was given. */
unsigned int ipdown_given;
/**< @brief Whether ipdown was given. */
unsigned int tun_device_given;
/**< @brief Whether tun-device was given. */
unsigned int pinghost_given;
/**< @brief Whether pinghost was given. */
unsigned int pingrate_given;
/**< @brief Whether pingrate was given. */
unsigned int pingsize_given;
/**< @brief Whether pingsize was given. */
unsigned int pingcount_given;
/**< @brief Whether pingcount was given. */
unsigned int pingquiet_given;
/**< @brief Whether pingquiet was given. */
unsigned int no_tx_gpdu_seq_given;
/**< @brief Whether no-tx-gpdu-seq was given. */
unsigned int pdp_type_given;
/**< @brief Whether pdp-type was given. */
int createif_mode_counter;
/**< @brief Counter for mode createif */
int pinghost_mode_counter;
/**< @brief Counter for mode pinghost */
};
/** @brief The additional parameters to pass to parser functions */
struct cmdline_parser_params {
int override;
/**< @brief whether to override possibly already present options (default 0) */
int initialize;
/**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
int check_required;
/**< @brief whether to check that all required options were provided (default 1) */
int check_ambiguity;
/**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
int print_errors;
/**< @brief whether getopt_long should print an error message for a bad option (default 1) */
};
/** @brief the purpose string of the program */
extern const char *gengetopt_args_info_purpose;
/** @brief the usage string of the program */
extern const char *gengetopt_args_info_usage;
/** @brief the description string of the program */
extern const char *gengetopt_args_info_description;
/** @brief all the lines making the help output */
extern const char *gengetopt_args_info_help[];
int cmdline_parser(int argc, char *const *argv,
/**
* The command line parser
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser(int argc, char **argv,
struct gengetopt_args_info *args_info);
int cmdline_parser2(int argc, char *const *argv,
/**
* The command line parser (version with additional parameters - deprecated)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param override whether to override possibly already present options
* @param initialize whether to initialize the option structure my_args_info
* @param check_required whether to check that all required options were provided
* @return 0 if everything went fine, NON 0 if an error took place
* @deprecated use cmdline_parser_ext() instead
*/
int cmdline_parser2(int argc, char **argv,
struct gengetopt_args_info *args_info,
int override, int initialize, int check_required);
/**
* The command line parser (version with additional parameters)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param params additional parameters for the parser
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_ext(int argc, char **argv,
struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params);
/**
* Save the contents of the option struct into an already open FILE stream.
* @param outfile the stream where to dump options
* @param args_info the option struct to dump
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_dump(FILE * outfile,
struct gengetopt_args_info *args_info);
/**
* Save the contents of the option struct into a (text) file.
* This file can be read by the config file parser (if generated by gengetopt)
* @param filename the file where to save
* @param args_info the option struct to save
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_file_save(const char *filename,
struct gengetopt_args_info *args_info);
/**
* Print the help
*/
void cmdline_parser_print_help(void);
/**
* Print the version
*/
void cmdline_parser_print_version(void);
/**
* Initializes all the fields a cmdline_parser_params structure
* to their default values
* @param params the structure to initialize
*/
void cmdline_parser_params_init(struct cmdline_parser_params *params);
/**
* Allocates dynamically a cmdline_parser_params structure and initializes
* all its fields to their default values
* @return the created and initialized cmdline_parser_params structure
*/
struct cmdline_parser_params *cmdline_parser_params_create(void);
/**
* Initializes the passed gengetopt_args_info structure's fields
* (also set default values for options that have a default)
* @param args_info the structure to initialize
*/
void cmdline_parser_init(struct gengetopt_args_info *args_info);
/**
* Deallocates the string fields of the gengetopt_args_info structure
* (but does not deallocate the structure itself)
* @param args_info the structure to deallocate
*/
void cmdline_parser_free(struct gengetopt_args_info *args_info);
int cmdline_parser_configfile(char *const filename,
/**
* The config file parser (deprecated version)
* @param filename the name of the config file
* @param args_info the structure where option information will be stored
* @param override whether to override possibly already present options
* @param initialize whether to initialize the option structure my_args_info
* @param check_required whether to check that all required options were provided
* @return 0 if everything went fine, NON 0 if an error took place
* @deprecated use cmdline_parser_config_file() instead
*/
int cmdline_parser_configfile(const char *filename,
struct gengetopt_args_info *args_info,
int override, int initialize,
int check_required);
/**
* The config file parser
* @param filename the name of the config file
* @param args_info the structure where option information will be stored
* @param params additional parameters for the parser
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_config_file(const char *filename,
struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params);
/**
* Checks that all the required options were specified
* @param args_info the structure to check
* @param prog_name the name of the program that will be used to print
* possible errors
* @return
*/
int cmdline_parser_required(struct gengetopt_args_info *args_info,
const char *prog_name);

View File

@@ -1,6 +1,7 @@
/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017 Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -19,6 +20,7 @@
#endif
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <ctype.h>
#include <netdb.h>
@@ -30,6 +32,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -77,12 +80,14 @@ struct gsn_t *gsn = NULL; /* GSN instance */
struct tun_t *tun = NULL; /* TUN instance */
int maxfd = 0; /* For select() */
int echoversion = 1; /* First try this version */
void *tall_sgsnemu_ctx; /* root talloc ctx */
/* Struct with local versions of gengetopt options */
struct {
int debug; /* Print debug messages */
int createif; /* Create local network interface */
struct in_addr netaddr, destaddr, net; /* Network interface */
char *tun_dev_name;
struct in46_addr netaddr, destaddr, net; /* Network interface */
size_t prefixlen;
char *ipup, *ipdown; /* Filename of scripts */
int defaultroute; /* Set up default route */
@@ -117,6 +122,8 @@ struct {
int imeisv_given;
struct ul16_t msisdn;
int norecovery_given;
int tx_gpdu_seq;
uint8_t pdp_type;
} options;
/* Definitions to use for PING. Most of the ping code was derived from */
@@ -156,13 +163,13 @@ int tsum = 0;
int pingseq = 0; /* Ping sequence counter */
struct timeval firstping;
void signal_handler(int signo)
static void signal_handler(int signo)
{
if (state == 2)
state = 3; /* Tell main loop to finish. */
}
int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
static int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
{
int hash = ippool_hash(addr) % MAXCONTEXTS;
struct iphash_t *h;
@@ -178,7 +185,7 @@ int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
return 0;
}
int ipdel(struct iphash_t *ipaddr)
static int ipdel(struct iphash_t *ipaddr)
{
int hash = ippool_hash(&ipaddr->addr) % MAXCONTEXTS;
struct iphash_t *h;
@@ -196,7 +203,7 @@ int ipdel(struct iphash_t *ipaddr)
return EOF; /* End of linked list and not found */
}
int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
static int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
{
int hash = ippool_hash(addr) % MAXCONTEXTS;
struct iphash_t *h;
@@ -210,7 +217,7 @@ int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
}
/* Used to write process ID to file. Assume someone else will delete */
void log_pid(char *pidfile)
static void log_pid(char *pidfile)
{
FILE *file;
mode_t oldmask;
@@ -224,7 +231,7 @@ void log_pid(char *pidfile)
fclose(file);
}
int process_options(int argc, char **argv)
static int process_options(int argc, char **argv)
{
/* gengeopt declarations */
struct gengetopt_args_info args_info;
@@ -285,6 +292,8 @@ int process_options(int argc, char **argv)
printf("contexts: %d\n", args_info.contexts_arg);
printf("timelimit: %d\n", args_info.timelimit_arg);
printf("createif: %d\n", args_info.createif_flag);
if (args_info.tun_device_arg)
printf("tun-device: %d\n", args_info.tun_device_arg);
if (args_info.ipup_arg)
printf("ipup: %s\n", args_info.ipup_arg);
if (args_info.ipdown_arg)
@@ -297,6 +306,7 @@ int process_options(int argc, char **argv)
printf("pingcount: %d\n", args_info.pingcount_arg);
printf("pingquiet: %d\n", args_info.pingquiet_flag);
printf("norecovery: %d\n", args_info.norecovery_flag);
printf("no-tx-gpdu-seq: %d\n", args_info.no_tx_gpdu_seq_flag);
}
/* Try out our new parser */
@@ -340,6 +350,8 @@ int process_options(int argc, char **argv)
printf("contexts: %d\n", args_info.contexts_arg);
printf("timelimit: %d\n", args_info.timelimit_arg);
printf("createif: %d\n", args_info.createif_flag);
if (args_info.tun_device_arg)
printf("tun-device: %s\n", args_info.tun_device_arg);
if (args_info.ipup_arg)
printf("ipup: %s\n", args_info.ipup_arg);
if (args_info.ipdown_arg)
@@ -354,6 +366,7 @@ int process_options(int argc, char **argv)
printf("pingcount: %d\n", args_info.pingcount_arg);
printf("pingquiet: %d\n", args_info.pingquiet_flag);
printf("norecovery: %d\n", args_info.norecovery_flag);
printf("no-tx-gpdu-seq: %d\n", args_info.no_tx_gpdu_seq_flag);
}
}
@@ -565,9 +578,9 @@ int process_options(int argc, char **argv)
/* rattype */
if (args_info.rattype_given == 1) {
options.rattype_given = 1;
options.rattype.l = strlen(args_info.rattype_arg);
options.rattype.v[0] = atoi(args_info.rattype_arg);
printf("Using RAT Type: %s\n", args_info.rattype_arg);
options.rattype.l = 1;
options.rattype.v[0] = args_info.rattype_arg;
printf("Using RAT Type: %d\n", args_info.rattype_arg);
}
/* userloc */
@@ -857,27 +870,26 @@ int process_options(int argc, char **argv)
/* createif */
options.createif = args_info.createif_flag;
options.tun_dev_name = args_info.tun_device_arg;
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
struct in46_addr in46;
if (ippool_aton
(&in46, &options.prefixlen, args_info.net_arg, 0)) {
(&options.net, &options.prefixlen, args_info.net_arg, 0)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
"Invalid network address: %s!",
args_info.net_arg);
exit(1);
}
options.net.s_addr = in46.v4.s_addr;
options.netaddr.s_addr = options.net.s_addr;
options.destaddr.s_addr = options.net.s_addr;
options.netaddr = options.net;
options.destaddr = options.net;
} else {
options.net.s_addr = 0;
memset(&options.net, 0, sizeof(options.net));
options.prefixlen = 0;
options.netaddr.s_addr = 0;
options.destaddr.s_addr = 0;
memset(&options.netaddr, 0, sizeof(options.netaddr));
memset(&options.destaddr, 0, sizeof(options.destaddr));
}
/* ipup */
@@ -918,38 +930,77 @@ int process_options(int argc, char **argv)
/* norecovery */
options.norecovery_given = args_info.norecovery_flag;
if (args_info.no_tx_gpdu_seq_flag)
options.tx_gpdu_seq = 0;
else
options.tx_gpdu_seq = 1;
/* PDP Type */
if (!strcmp(args_info.pdp_type_arg, "v6"))
options.pdp_type = PDP_EUA_TYPE_v6;
else if (!strcmp(args_info.pdp_type_arg, "v4"))
options.pdp_type = PDP_EUA_TYPE_v4;
else {
SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unsupported/unknown PDP Type '%s'\n",
args_info.pdp_type_arg);
return -1;
}
if (options.pingcount && options.pdp_type != PDP_EUA_TYPE_v4) {
SYS_ERR(DSGSN, LOGL_ERROR, 0, "built-in ping only works with IPv4, use tun-device");
return -1;
}
return 0;
}
int encaps_printf(struct pdp_t *pdp, void *pack, unsigned len)
/* read a single value from a /procc file, up to 255 bytes, callee-allocated */
static char *proc_read(const char *path)
{
unsigned int i;
printf("The packet looks like this:\n");
for (i = 0; i < len; i++) {
printf("%02x ", (unsigned char)*(char *)(pack + i));
if (!((i + 1) % 16))
printf("\n");
};
printf("\n");
return 0;
char *ret = NULL;
FILE *f;
f = fopen(path, "r");
if (!f)
return NULL;
ret = malloc(256);
if (!ret)
goto out;
if (!fgets(ret, 256, f)) {
free(ret);
ret = NULL;
goto out;
}
char *print_ipprot(int t)
out:
fclose(f);
return ret;
}
/* Read value of a /proc/sys/net/ipv6/conf file for given device.
* Memory is dynamically allocated, caller must free it later. */
static char *proc_ipv6_conf_read(const char *dev, const char *file)
{
switch (t) {
case 1:
return "ICMP";
case 6:
return "TCP";
case 17:
return "UDP";
default:
const char *fmt = "/proc/sys/net/ipv6/conf/%s/%s";
char path[strlen(fmt) + strlen(dev) + strlen(file)+1];
snprintf(path, sizeof(path), fmt, dev, file);
return proc_read(path);
}
static char *print_ipprot(int t)
{
struct protoent *pe = getprotobynumber(t);
if (!pe)
return "Unknown";
};
else
return pe->p_name;
}
char *print_icmptype(int t)
static char *print_icmptype(int t)
{
static char *ttab[] = {
"Echo Reply",
@@ -975,7 +1026,7 @@ char *print_icmptype(int t)
return (ttab[t]);
}
int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
static int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
{
unsigned int n;
uint64_t i64 = 0;
@@ -1019,7 +1070,7 @@ int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add)
}
int imsi_add(uint64_t src, uint64_t * dst, int add)
static int imsi_add(uint64_t src, uint64_t * dst, int add)
{
/* TODO: big endian / small endian ??? */
uint64_t i64 = 0;
@@ -1054,7 +1105,7 @@ int imsi_add(uint64_t src, uint64_t * dst, int add)
}
/* Calculate time left until we have to send off next ping packet */
int ping_timeout(struct timeval *tp)
static int ping_timeout(struct timeval *tp)
{
struct timezone tz;
struct timeval tv;
@@ -1076,7 +1127,7 @@ int ping_timeout(struct timeval *tp)
}
/* Print out statistics when at the end of ping sequence */
int ping_finish()
static int ping_finish()
{
struct timezone tz;
struct timeval tv;
@@ -1109,7 +1160,7 @@ int ping_finish()
}
/* Handle a received ping packet. Print out line and update statistics. */
int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
{
struct timezone tz;
struct timeval tv;
@@ -1178,7 +1229,7 @@ int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
}
/* Create a new ping packet and send it off to peer. */
int create_ping(void *gsn, struct pdp_t *pdp,
static int create_ping(void *gsn, struct pdp_t *pdp,
struct in_addr *dst, int seq, unsigned int datasize)
{
@@ -1257,14 +1308,14 @@ int create_ping(void *gsn, struct pdp_t *pdp,
return gtp_data_req(gsn, pdp, &pack, 28 + datasize);
}
int delete_context(struct pdp_t *pdp)
static int delete_context(struct pdp_t *pdp)
{
if (tun && options.ipdown)
tun_runscript(tun, options.ipdown);
ipdel((struct iphash_t *)pdp->peer);
memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */
ipdel((struct iphash_t *)pdp->peer[0]);
memset(pdp->peer[0], 0, sizeof(struct iphash_t)); /* To be sure */
if (1 == options.contexts)
state = 5; /* Disconnected */
@@ -1272,19 +1323,46 @@ int delete_context(struct pdp_t *pdp)
return 0;
}
/* Link-Local address prefix fe80::/64 */
static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
/* Callback for receiving messages from tun */
int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
{
struct iphash_t *ipm;
struct in46_addr src;
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
if (iph->version == 4) {
if (len < sizeof(*iph) || len < 4*iph->ihl) {
printf("Dropping packet with too short IP header\n");
return 0;
}
src.len = 4;
src.v4.s_addr = iph->saddr;
} else if (iph->version == 6) {
/* We only have a single entry in the hash table, and it consists of the link-local
* address "fe80::prefix". So we need to make sure to convert non-link-local source
* addresses to that format before looking up the hash table via ippool_getip() */
src.len = 16;
if (!memcmp(ip6h->ip6_src.s6_addr, ll_prefix, sizeof(ll_prefix))) {
/* is a link-local address, we can do the hash lookup 1:1 */
src.v6 = ip6h->ip6_src;
} else {
/* it is not a link-local address, so we must convert from the /64 prefix
* to the link-local format that's used in the hash table */
memcpy(&src.v6.s6_addr[0], ll_prefix, sizeof(ll_prefix));
memcpy(&src.v6.s6_addr[sizeof(ll_prefix)], ip6h->ip6_src.s6_addr, 16-sizeof(ll_prefix));
}
} else {
printf("Dropping packet with invalid IP version %u\n", iph->version);
return 0;
}
if (ipget(&ipm, &src)) {
printf("Dropping packet from invalid source address: %s\n",
inet_ntoa(src.v4));
in46a_ntoa(&src));
return 0;
}
@@ -1293,7 +1371,7 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
return 0;
}
int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
struct in46_addr addr;
@@ -1324,7 +1402,7 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
return EOF; /* Not what we expected */
}
if (in46a_from_eua(&pdp->eua, &addr)) {
if (in46a_from_eua(&pdp->eua, &addr) < 1) {
printf
("Received create PDP context response. Cause value: %d\n",
cause);
@@ -1335,41 +1413,71 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
}
printf("Received create PDP context response. IP address: %s\n",
inet_ntoa(addr.v4));
in46a_ntoa(&addr));
if ((options.createif) && (!options.net.s_addr)) {
struct in_addr m;
#ifdef HAVE_INET_ATON
inet_aton("255.255.255.255", &m);
#else
m.s_addr = -1;
#endif
switch (addr.len) {
case 16: /* IPv6 */
/* we have to enable the kernel to perform stateless autoconfiguration,
* i.e. send a router solicitation using the lover 64bits of the allocated
* EUA as interface identifier, as per 3GPP TS 29.061 Section 11.2.1.3.2 */
memcpy(addr.v6.s6_addr, ll_prefix, sizeof(ll_prefix));
printf("Derived IPv6 link-local address: %s\n", in46a_ntoa(&addr));
break;
case 4: /* IPv4 */
break;
}
if ((options.createif) && (!options.net.len)) {
size_t prefixlen = 32;
if (addr.len == 16)
prefixlen = 64;
/* printf("Setting up interface and routing\n"); */
tun_addaddr(tun, &addr.v4, &addr.v4, &m);
tun_addaddr(tun, &addr, &addr, prefixlen);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
tun_addroute(tun, &rm, &addr.v4, &rm);
netdev_addroute(&rm, &addr.v4, &rm);
}
if (options.ipup)
tun_runscript(tun, options.ipup);
}
ipset((struct iphash_t *)pdp->peer, &addr);
/* now that ip-up has been executed, check if we are configured to
* accept router advertisements */
if (options.createif && options.pdp_type == PDP_EUA_TYPE_v6) {
char *accept_ra, *forwarding;
accept_ra = proc_ipv6_conf_read(tun->devname, "accept_ra");
forwarding = proc_ipv6_conf_read(tun->devname, "forwarding");
if (!accept_ra || !forwarding)
printf("Could not open proc file for %s ?!?\n", tun->devname);
else {
if (!strcmp(accept_ra, "0") ||
(!strcmp(forwarding, "1") && !strcmp(accept_ra, "1"))) {
printf("%s is %s, i.e. your tun device is not configured to accept "
"router advertisements; SLAAC will not suceed, please "
"fix your setup!\n");
}
}
free(accept_ra);
free(forwarding);
}
ipset(iph, &addr);
state = 2; /* Connected */
return 0;
}
int delete_pdp_conf(struct pdp_t *pdp, int cause)
static int delete_pdp_conf(struct pdp_t *pdp, int cause)
{
printf("Received delete PDP context response. Cause value: %d\n",
cause);
return 0;
}
int echo_conf(int recovery)
static int echo_conf(int recovery)
{
if (recovery < 0) {
@@ -1391,7 +1499,7 @@ int echo_conf(int recovery)
return 0;
}
int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
static int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
{
/* if (cause < 0) return 0; Some error occurred. We don't care */
switch (type) {
@@ -1408,7 +1516,7 @@ int conf(int type, int cause, struct pdp_t *pdp, void *cbp)
}
}
int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
{
/* printf("encaps_tun. Packet received: forwarding to tun\n"); */
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
@@ -1433,7 +1541,9 @@ int main(int argc, char **argv)
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
osmo_init_logging(&log_info);
tall_sgsnemu_ctx = talloc_named_const(NULL, 0, "sgsnemu");
msgb_talloc_ctx_init(tall_sgsnemu_ctx, 0);
osmo_init_logging2(tall_sgsnemu_ctx, &log_info);
/* Process options given in configuration file and command line */
if (process_options(argc, argv))
@@ -1461,7 +1571,7 @@ int main(int argc, char **argv)
if (options.createif) {
printf("Setting up interface\n");
/* Create a tunnel interface */
if (tun_new((struct tun_t **)&tun, NULL)) {
if (tun_new((struct tun_t **)&tun, options.tun_dev_name, false, -1, -1)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
"Failed to create tun");
exit(1);
@@ -1471,15 +1581,13 @@ int main(int argc, char **argv)
maxfd = tun->fd;
}
if ((options.createif) && (options.net.s_addr)) {
struct in_addr mask;
mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
if ((options.createif) && (options.net.len)) {
/* printf("Setting up interface and routing\n"); */
tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
tun_addaddr(tun, &options.netaddr, &options.destaddr, options.prefixlen);
if (options.defaultroute) {
struct in_addr rm;
rm.s_addr = 0;
tun_addroute(tun, &rm, &options.destaddr, &rm);
netdev_addroute(&rm, &options.destaddr.v4, &rm);
}
if (options.ipup)
tun_runscript(tun, options.ipup);
@@ -1508,7 +1616,7 @@ int main(int argc, char **argv)
/* Otherwise it is deallocated by gtplib */
pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
pdp->peer = &iparr[n];
pdp->peer[0] = &iparr[n]; /* FIXME: support v4v6, have 2 peers */
pdp->ipif = tun; /* TODO */
iparr[n].pdp = pdp;
@@ -1572,7 +1680,10 @@ int main(int argc, char **argv)
msisdn_add(&options.msisdn, &pdp->msisdn, n);
}
ipv42eua(&pdp->eua, NULL); /* Request dynamic IP address */
/* Request dynamic IP address */
pdp->eua.v[0] = PDP_EUA_ORG_IETF;
pdp->eua.v[1] = options.pdp_type;
pdp->eua.l = 2;
if (options.pco.l > sizeof(pdp->pco_req.v)) {
SYS_ERR(DSGSN, LOGL_ERROR, 0,
@@ -1591,6 +1702,8 @@ int main(int argc, char **argv)
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
512 = Flat rate, 256 = Hot billing */
pdp->tx_gpdu_seq = options.tx_gpdu_seq;
/* Create context */
/* We send this of once. Retransmissions are handled by gtplib */
gtp_create_context_req(gsn, pdp, &iparr[n]);

View File

41
tests/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
SUBDIRS = \
lib \
gtp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
echo '# Signature of the current package.' && \
echo 'm4_define([AT_PACKAGE_NAME],' && \
echo ' [$(PACKAGE_NAME)])' && \
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
echo ' [$(PACKAGE_TARNAME)])' && \
echo 'm4_define([AT_PACKAGE_VERSION],' && \
echo ' [$(PACKAGE_VERSION)])' && \
echo 'm4_define([AT_PACKAGE_STRING],' && \
echo ' [$(PACKAGE_STRING)])' && \
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
echo ' [$(PACKAGE_BUGREPORT)])'; \
echo 'm4_define([AT_PACKAGE_URL],' && \
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
TESTSUITE = $(srcdir)/testsuite
DISTCLEANFILES = atconfig $(NULL)
check-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' $(TESTSUITEFLAGS)
clean-local:
test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
mv $@.tmp $@

View File

19
tests/gtp/Makefile.am Normal file
View File

@@ -0,0 +1,19 @@
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
EXTRA_DIST = \
gtpie_test.ok \
$(NULL)
noinst_PROGRAMS = \
gtpie_test \
$(NULL)
gtpie_test_SOURCES = \
gtpie_test.c \
$(NULL)
gtpie_test_LDADD = \
$(top_builddir)/lib/debug.o \
$(top_builddir)/gtp/libgtp.la \
$(LIBOSMOCORE_LIBS) \
$(NULL)

132
tests/gtp/gtpie_test.c Normal file
View File

@@ -0,0 +1,132 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/bits.h>
#include "../../lib/syserr.h"
#include "../../gtp/gtpie.h"
static const uint8_t in[] = { 1,2,3,4,5,6 };
static uint8_t buf[256];
static int rc;
static void test_gtpie_tlv()
{
unsigned int len = 0;
printf("Testing gtpie_tlv()\n");
/* normal / successful case */
memset(buf, 0, sizeof(buf));
rc = gtpie_tlv(buf, &len, sizeof(buf), 23, sizeof(in), in);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == sizeof(in) + 3);
OSMO_ASSERT(buf[0] == 23);
OSMO_ASSERT(osmo_load16be(&buf[1]) == sizeof(in));
OSMO_ASSERT(!memcmp(buf+3, in, sizeof(in)));
/* overflow */
memset(buf, 0, sizeof(buf));
rc = gtpie_tlv(buf, &len, 4, 23, sizeof(in), in);
OSMO_ASSERT(rc == 1);
}
static void test_gtpie_tv0()
{
unsigned int len = 0;
printf("Testing gtpie_tv0()\n");
memset(buf, 0, sizeof(buf));
rc = gtpie_tv0(buf, &len, sizeof(buf), 42, sizeof(in), in);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == sizeof(in) + 1);
}
static void test_gtpie_tv1()
{
unsigned int len = 0;
printf("Testing gtpie_tv1()\n");
memset(buf, 0, sizeof(buf));
rc = gtpie_tv1(buf, &len, sizeof(buf), 42, 0xAD);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 2);
OSMO_ASSERT(buf[0] == 42);
OSMO_ASSERT(buf[1] == 0xAD);
}
static void test_gtpie_tv2()
{
unsigned int len = 0;
printf("Testing gtpie_tv2()\n");
memset(buf, 0, sizeof(buf));
rc = gtpie_tv2(buf, &len, sizeof(buf), 42, 0xABCD);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 3);
OSMO_ASSERT(buf[0] == 42);
OSMO_ASSERT(osmo_load16be(&buf[1]) == 0xABCD);
}
static void test_gtpie_tv4()
{
unsigned int len = 0;
printf("Testing gtpie_tv4()\n");
memset(buf, 0, sizeof(buf));
rc = gtpie_tv4(buf, &len, sizeof(buf), 42, 0xABCD0123);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 5);
OSMO_ASSERT(buf[0] == 42);
OSMO_ASSERT(osmo_load32be(&buf[1]) == 0xABCD0123);
}
static void test_gtpie_tv8()
{
unsigned int len = 0;
printf("Testing gtpie_tv8()\n");
memset(buf, 0, sizeof(buf));
rc = gtpie_tv8(buf, &len, sizeof(buf), 42, 0x0001020304050607ULL);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(len == 9);
OSMO_ASSERT(buf[0] == 42);
OSMO_ASSERT(osmo_load32be(&buf[1]) == 0x00010203);
OSMO_ASSERT(osmo_load32be(&buf[5]) == 0x04050607);
}
int main(int argc, char **argv)
{
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));
test_gtpie_tlv();
test_gtpie_tv0();
test_gtpie_tv1();
test_gtpie_tv2();
test_gtpie_tv4();
test_gtpie_tv8();
/* TODO: gtpie_decaps() */
/* TODO: gtpie_encaps() */
/* TODO: gtpie_encaps2() */
/* TODO: gtpie_getie(), gtpie_exist(), gtpie_get*() */
return 0;
}

6
tests/gtp/gtpie_test.ok Normal file
View File

@@ -0,0 +1,6 @@
Testing gtpie_tlv()
Testing gtpie_tv0()
Testing gtpie_tv1()
Testing gtpie_tv2()
Testing gtpie_tv4()
Testing gtpie_tv8()

28
tests/lib/Makefile.am Normal file
View File

@@ -0,0 +1,28 @@
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
EXTRA_DIST = ippool_test.ok \
ippool_test.err \
ippool_v6_test.ok \
ippool_v6_test.err \
in46a_test.ok \
in46a_v6_test.ok
noinst_PROGRAMS = ippool_test in46a_test
ippool_test_SOURCES = \
ippool_test.c \
$(NULL)
ippool_test_LDADD = \
$(top_builddir)/lib/libmisc.a \
$(LIBOSMOCORE_LIBS) \
$(NULL)
in46a_test_SOURCES = \
in46a_test.c \
$(NULL)
in46a_test_LDADD = \
$(top_builddir)/lib/libmisc.a \
$(LIBOSMOCORE_LIBS) \
$(NULL)

460
tests/lib/in46a_test.c Normal file
View File

@@ -0,0 +1,460 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/bits.h>
#include "../../lib/in46_addr.h"
#include "../../lib/syserr.h"
static const struct in46_addr g_ia4 = {
.len = 4,
.v4.s_addr = 0x0d0c0b0a,
};
static void test_in46a_to_af(void)
{
printf("Testing in46a_to_af() with IPv4 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
}
static void test_in46a_to_sas(void)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
printf("Testing in46a_to_sas() with IPv4 addresses\n");
memset(&ss, 0, sizeof(ss));
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia4) == 0);
OSMO_ASSERT(sin->sin_family == AF_INET);
OSMO_ASSERT(sin->sin_addr.s_addr == g_ia4.v4.s_addr);
}
static void test_in46a_ntop(void)
{
struct in46_addr ia;
char buf[256];
const char *res;
printf("Testing in46a_ntop() with IPv4 addresses\n");
res = in46a_ntop(NULL, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
printf("res = %s\n", res);
ia.len = 0;
res = in46a_ntop(&ia, buf, sizeof(buf));
printf("res = %s\n", res);
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
ia.len = 4;
ia.v4.s_addr = htonl(0x01020304);
res = in46a_ntop(&ia, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
printf("res = %s\n", res);
}
static void test_in46p_ntoa(void)
{
const struct in46_prefix ip46 = {
.prefixlen = 24,
.addr = {
.len = 4,
.v4.s_addr = htonl(0x10203000),
},
};
printf("in46p_ntoa() returns %s\n", in46p_ntoa(&ip46));
}
static void test_in46a_equal(void)
{
struct in46_addr b;
printf("Testing in46a_equal() with IPv4 addresses\n");
memset(&b, 0xff, sizeof(b));
b.len = g_ia4.len;
b.v4.s_addr = g_ia4.v4.s_addr;
OSMO_ASSERT(in46a_equal(&g_ia4, &b));
}
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
size_t prefixlen)
{
int rc;
printf("in46a_within_mask(%s, ", in46a_ntoa(addr));
printf("%s, %lu) = ", in46a_ntoa(net), prefixlen);
rc = in46a_within_mask(addr, net, prefixlen);
printf("%d\n", rc);
return rc;
}
static void test_in46a_within_mask(void)
{
struct in46_addr addr, mask;
printf("Testing in46a_within_mask() with IPv4 addresses\n");
addr = g_ia4;
mask = g_ia4;
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 32));
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfffffffC );
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfff80000 );
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 13));
addr.v4.s_addr = htonl(ntohl(addr.v4.s_addr) + 1);
mask = g_ia4;
OSMO_ASSERT(!log_in46a_within_mask(&addr, &mask, 32));
mask.v4.s_addr = htonl( ntohl(mask.v4.s_addr) & 0xfffffffC );
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
}
static void test_in46a_to_eua(void)
{
struct ul66_t eua;
printf("testing in46a_to_eua() with IPv4 addresses\n");
#if 0 /* triggers assert in current implementation */
const struct in46_addr ia_invalid = { .len = 3, };
OSMO_ASSERT(in46a_to_eua(&ia_invalid, &eua) < 0);
#endif
/* IPv4 address */
OSMO_ASSERT(in46a_to_eua(&g_ia4, 1, &eua) == 0);
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
}
static void test_in46a_from_eua(void)
{
struct in46_addr ia;
struct ul66_t eua;
const uint8_t v4_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4 };
const uint8_t v4_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4, 1,2,3,4 };
memset(&eua, 0, sizeof(eua));
printf("Testing in46a_from_eua() with IPv4 addresses\n");
/* default: v4 unspec */
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == 0);
/* invalid */
eua.v[0] = 0x23;
eua.v[1] = PDP_EUA_TYPE_v4;
eua.l = 6;
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
/* invalid */
eua.v[0] = PDP_EUA_ORG_IETF;
eua.v[1] = 0x23;
eua.l = 6;
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
/* unspecified V4 */
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
eua.l = sizeof(v4_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == 0);
/* specified V4 */
memcpy(eua.v, v4_spec, sizeof(v4_spec));
eua.l = sizeof(v4_spec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
}
static void test_in46a_netmasklen(void)
{
struct in46_addr netmask;
unsigned int len;
printf("Testing in46a_netmasklen() with IPv4 addresses\n");
netmask.len = 4;
netmask.v4.s_addr = 0xffffffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 32);
netmask.v4.s_addr = 0x00ffffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 24);
netmask.v4.s_addr = 0x00f0ffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 20);
netmask.v4.s_addr = 0x000000fe;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 7);
netmask.v4.s_addr = 0x00000000;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 0);
}
/* IPv6 specific tests */
static const struct in46_addr g_ia6 = {
.len = 16,
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
};
static void test_in46a_to_af_v6(void)
{
struct in46_addr ia;
printf("Testing in46a_to_af() with IPv6 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
ia.len = 8;
OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
}
static void test_in46a_to_sas_v6(void)
{
struct sockaddr_storage ss;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
printf("Testing in46a_to_sas() with IPv6 addresses\n");
memset(&ss, 0, sizeof(ss));
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
OSMO_ASSERT(sin6->sin6_family == AF_INET6);
OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
}
static void test_in46a_ntop_v6(void)
{
char buf[256];
const char *res;
printf("Testing in46a_ntop() with IPv6 addresses\n");
res = in46a_ntop(&g_ia6, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
printf("res = %s\n", res);
}
static void test_in46a_equal_v6(void)
{
struct in46_addr b;
printf("Testing in46a_equal() with IPv6 addresses\n");
memset(&b, 0xff, sizeof(b));
b.len = g_ia6.len;
b.v6 = g_ia6.v6;
OSMO_ASSERT(in46a_equal(&g_ia6, &b));
}
static void test_in46a_to_eua_v6(void)
{
const struct in46_addr ia_v6_8 = {
.len = 8,
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
};
struct ul66_t eua;
printf("Testing in46a_to_eua() with IPv6 addresses\n");
/* IPv6 address */
OSMO_ASSERT(in46a_to_eua(&g_ia6, 1, &eua) == 0);
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
/* IPv6 address with prefix / length 8 */
OSMO_ASSERT(in46a_to_eua(&ia_v6_8, 1, &eua) == 0);
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
}
static void test_in46a_to_eua_v4v6() {
const struct in46_addr ia_v4v6[2] = {
{
.len = 16,
.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
},
{
.len = 4,
.v4.s_addr = 0x0d0c0b0a,
}
};
struct ul66_t eua;
printf("Testing in46a_to_eua() with IPv4v6 addresses\n");
/* IPv4 address */
OSMO_ASSERT(in46a_to_eua(ia_v4v6, 2, &eua) == 0);
OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4v6);
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
OSMO_ASSERT(!memcmp(&eua.v[6], &g_ia6.v6, 16));
}
static void test_in46a_from_eua_v6(void)
{
struct in46_addr ia;
struct ul66_t eua;
const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
memset(&eua, 0, sizeof(eua));
printf("Testing in46a_from_eua() with IPv6 addresses\n");
/* unspecified V6 */
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
eua.l = sizeof(v6_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 16);
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
/* specified V6 */
memcpy(eua.v, v6_spec, sizeof(v6_spec));
eua.l = sizeof(v6_spec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 16);
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
}
static void test_in46a_from_eua_v4v6(void) {
struct in46_addr ia[2];
struct ul66_t eua;
const uint8_t v4_unspec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6 };
const uint8_t v4_spec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4 };
const uint8_t v4_unspec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
const uint8_t v4_spec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
memset(&eua, 0, sizeof(eua));
printf("Testing in46a_from_eua() with IPv4v6 addresses\n");
/* unspecified V4 & V6 */
memcpy(eua.v, v4_unspec_v6_unspec, sizeof(v4_unspec_v6_unspec));
eua.l = sizeof(v4_unspec_v6_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[1].len == 16);
OSMO_ASSERT(ia[0].v4.s_addr == 0);
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
/* specified V4, unspecified V6 */
memcpy(eua.v, v4_spec_v6_unspec, sizeof(v4_spec_v6_unspec));
eua.l = sizeof(v4_spec_v6_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[1].len == 16);
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
/* unspecified V4, specified V6 */
memcpy(eua.v, v4_unspec_v6_spec, sizeof(v4_unspec_v6_spec));
eua.l = sizeof(v4_unspec_v6_spec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[1].len == 16);
OSMO_ASSERT(ia[0].v4.s_addr == 0);
OSMO_ASSERT(!memcmp(&ia[1].v6, v4_unspec_v6_spec+2, ia[1].len));
/* specified V4, specified V6 */
memcpy(eua.v, v4_spec_v6_spec, sizeof(v4_spec_v6_spec));
eua.l = sizeof(v4_spec_v6_spec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[1].len == 16);
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
OSMO_ASSERT(!memcmp(&ia[1].v6, v4_spec_v6_spec+6, ia[1].len));
}
static void test_in46a_netmasklen_v6(void)
{
unsigned int len;
printf("Testing in46a_netmasklen() with IPv6 addresses\n");
const struct in46_addr netmaskA = {
.len = 16,
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
};
len = in46a_netmasklen(&netmaskA);
OSMO_ASSERT(len == 128);
const struct in46_addr netmaskB = {
.len = 16,
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00},
};
len = in46a_netmasklen(&netmaskB);
OSMO_ASSERT(len == 104);
const struct in46_addr netmaskC = {
.len = 16,
.v6.s6_addr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00},
};
len = in46a_netmasklen(&netmaskC);
OSMO_ASSERT(len == 103);
const struct in46_addr netmaskD = {
.len = 16,
.v6.s6_addr = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
};
len = in46a_netmasklen(&netmaskD);
OSMO_ASSERT(len == 0);
}
int main(int argc, char **argv)
{
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));
if (argc < 2 || strcmp(argv[1], "-v6")) {
test_in46a_to_af();
test_in46a_to_sas();
test_in46a_ntop();
test_in46p_ntoa();
test_in46a_equal();
test_in46a_within_mask();
test_in46a_to_eua();
test_in46a_from_eua();
test_in46a_netmasklen();
} else {
test_in46a_to_af_v6();
test_in46a_to_sas_v6();
test_in46a_ntop_v6();
test_in46a_equal_v6();
test_in46a_to_eua_v6();
test_in46a_from_eua_v6();
test_in46a_to_eua_v4v6();
test_in46a_from_eua_v4v6();
test_in46a_netmasklen_v6();
}
return 0;
}

17
tests/lib/in46a_test.ok Normal file
View File

@@ -0,0 +1,17 @@
Testing in46a_to_af() with IPv4 addresses
Testing in46a_to_sas() with IPv4 addresses
Testing in46a_ntop() with IPv4 addresses
res = UNDEFINED
res = UNDEFINED
res = 1.2.3.4
in46p_ntoa() returns 16.32.48.0/24
Testing in46a_equal() with IPv4 addresses
Testing in46a_within_mask() with IPv4 addresses
in46a_within_mask(10.11.12.13, 10.11.12.13, 32) = 1
in46a_within_mask(10.11.12.13, 10.11.12.12, 30) = 1
in46a_within_mask(10.11.12.13, 10.8.0.0, 13) = 1
in46a_within_mask(10.11.12.14, 10.11.12.13, 32) = 0
in46a_within_mask(10.11.12.14, 10.11.12.12, 30) = 1
testing in46a_to_eua() with IPv4 addresses
Testing in46a_from_eua() with IPv4 addresses
Testing in46a_netmasklen() with IPv4 addresses

View File

@@ -0,0 +1,10 @@
Testing in46a_to_af() with IPv6 addresses
Testing in46a_to_sas() with IPv6 addresses
Testing in46a_ntop() with IPv6 addresses
res = 102:304:506:708:90a:b0c:d0e:f10
Testing in46a_equal() with IPv6 addresses
Testing in46a_to_eua() with IPv6 addresses
Testing in46a_from_eua() with IPv6 addresses
Testing in46a_to_eua() with IPv4v6 addresses
Testing in46a_from_eua() with IPv4v6 addresses
Testing in46a_netmasklen() with IPv6 addresses

144
tests/lib/ippool_test.c Normal file
View File

@@ -0,0 +1,144 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include "../../lib/in46_addr.h"
#include "../../lib/ippool.h"
#include "../../lib/syserr.h"
static struct ippool_t *create_pool(const char *prefix_str, unsigned int flags, char **blacklist, size_t blacklist_size)
{
struct in46_prefix *blacklist_pfx;
struct ippool_t *pool;
struct in46_prefix pfx;
size_t t;
int rc;
int i;
blacklist_pfx = calloc(blacklist_size, sizeof(struct in46_prefix));
for (i = 0; i < blacklist_size; i++) {
rc = ippool_aton(&blacklist_pfx[i].addr, &t, blacklist[i], 0);
OSMO_ASSERT(rc == 0);
pfx.prefixlen = t;
}
/* dynamic-only v4 */
rc = ippool_aton(&pfx.addr, &t, prefix_str, 0);
OSMO_ASSERT(rc == 0);
pfx.prefixlen = t;
rc = ippool_new(&pool, &pfx, NULL, flags, blacklist_pfx, blacklist_size);
OSMO_ASSERT(rc == 0);
//ippool_printaddr(pool);
free(blacklist_pfx);
return pool;
}
static void test_pool_size(const char *pfx, unsigned int flags, char **blacklist, size_t blacklist_size, unsigned int expected_size)
{
struct ippool_t *pool;
struct ippoolm_t *member;
struct in46_addr addr;
int i, rc, n;
printf("testing pool for prefix %s, flags=0x%x, blacklist_size=%lu, expected_size=%u\n", pfx, flags, blacklist_size, expected_size);
pool = create_pool(pfx, flags, blacklist, blacklist_size);
OSMO_ASSERT(pool->listsize == expected_size);
memset(&addr, 0, sizeof(addr));
addr.len = pool->member[0].addr.len;
/* allocate all addresses */
for (i = 0; i < expected_size; i++) {
member = NULL;
rc = ippool_newip(pool, &member, &addr, 0);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(member);
printf("allocated address %s\n", in46a_ntoa(&member->addr));
}
/* allocate one more, expect that to fail */
rc = ippool_newip(pool, &member, &addr, 0);
OSMO_ASSERT(rc < 0);
/* release a (random) number N of (random) member address */
n = rand() % pool->listsize;
for (i = 0; i < n; i++) {
int r;
/* chose a random index that is in use */
do {
r = rand() % pool->listsize;
} while (!pool->member[r].inuse);
/* and free it... */
rc = ippool_freeip(pool, &pool->member[r]);
OSMO_ASSERT(rc == 0);
}
/* allocate all N previously released addresses */
for (i = 0; i < n; i++) {
member = NULL;
rc = ippool_newip(pool, &member, &addr, 0);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(member);
}
/* allocate one more, expect that to fail */
rc = ippool_newip(pool, &member, &addr, 0);
OSMO_ASSERT(rc < 0);
ippool_free(pool);
}
static void test_pool_sizes(void)
{
/* 256 addresses [0..255] */
test_pool_size("192.168.23.0/24", 0, NULL, 0, 256);
/* 255 addresses [1..255] */
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK, NULL, 0, 255);
/* 254 addresses [1..254] */
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 254);
/* 65534 addresses [0.1..255.254] */
test_pool_size("192.168.0.0/16", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 65534);
/* 253 addresses [1..254] & exclude 192.168.23.1/24 */
char *blacklist[] = {"176.16.222.10/24", "192.168.23.1/24", "192.168.38.2/24"};
test_pool_size("192.168.23.0/24", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, blacklist, 3, 253);
}
static void test_pool_sizes_v6(void)
{
/* 256 prefixes of /64 each */
test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
}
int main(int argc, char **argv)
{
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));
if (argc < 2 || strcmp(argv[1], "-v6")) {
test_pool_sizes();
} else {
test_pool_sizes_v6();
}
return 0;
}

10
tests/lib/ippool_test.err Normal file
View File

@@ -0,0 +1,10 @@
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available
No more IP addresses available

66557
tests/lib/ippool_test.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
No more IP addresses available
No more IP addresses available

257
tests/lib/ippool_v6_test.ok Normal file
View File

@@ -0,0 +1,257 @@
testing pool for prefix 2001:DB8::/56, flags=0x0, blacklist_size=0, expected_size=256
allocated address 2001:db8::
allocated address 2001:db8:0:1::
allocated address 2001:db8:0:2::
allocated address 2001:db8:0:3::
allocated address 2001:db8:0:4::
allocated address 2001:db8:0:5::
allocated address 2001:db8:0:6::
allocated address 2001:db8:0:7::
allocated address 2001:db8:0:8::
allocated address 2001:db8:0:9::
allocated address 2001:db8:0:a::
allocated address 2001:db8:0:b::
allocated address 2001:db8:0:c::
allocated address 2001:db8:0:d::
allocated address 2001:db8:0:e::
allocated address 2001:db8:0:f::
allocated address 2001:db8:0:10::
allocated address 2001:db8:0:11::
allocated address 2001:db8:0:12::
allocated address 2001:db8:0:13::
allocated address 2001:db8:0:14::
allocated address 2001:db8:0:15::
allocated address 2001:db8:0:16::
allocated address 2001:db8:0:17::
allocated address 2001:db8:0:18::
allocated address 2001:db8:0:19::
allocated address 2001:db8:0:1a::
allocated address 2001:db8:0:1b::
allocated address 2001:db8:0:1c::
allocated address 2001:db8:0:1d::
allocated address 2001:db8:0:1e::
allocated address 2001:db8:0:1f::
allocated address 2001:db8:0:20::
allocated address 2001:db8:0:21::
allocated address 2001:db8:0:22::
allocated address 2001:db8:0:23::
allocated address 2001:db8:0:24::
allocated address 2001:db8:0:25::
allocated address 2001:db8:0:26::
allocated address 2001:db8:0:27::
allocated address 2001:db8:0:28::
allocated address 2001:db8:0:29::
allocated address 2001:db8:0:2a::
allocated address 2001:db8:0:2b::
allocated address 2001:db8:0:2c::
allocated address 2001:db8:0:2d::
allocated address 2001:db8:0:2e::
allocated address 2001:db8:0:2f::
allocated address 2001:db8:0:30::
allocated address 2001:db8:0:31::
allocated address 2001:db8:0:32::
allocated address 2001:db8:0:33::
allocated address 2001:db8:0:34::
allocated address 2001:db8:0:35::
allocated address 2001:db8:0:36::
allocated address 2001:db8:0:37::
allocated address 2001:db8:0:38::
allocated address 2001:db8:0:39::
allocated address 2001:db8:0:3a::
allocated address 2001:db8:0:3b::
allocated address 2001:db8:0:3c::
allocated address 2001:db8:0:3d::
allocated address 2001:db8:0:3e::
allocated address 2001:db8:0:3f::
allocated address 2001:db8:0:40::
allocated address 2001:db8:0:41::
allocated address 2001:db8:0:42::
allocated address 2001:db8:0:43::
allocated address 2001:db8:0:44::
allocated address 2001:db8:0:45::
allocated address 2001:db8:0:46::
allocated address 2001:db8:0:47::
allocated address 2001:db8:0:48::
allocated address 2001:db8:0:49::
allocated address 2001:db8:0:4a::
allocated address 2001:db8:0:4b::
allocated address 2001:db8:0:4c::
allocated address 2001:db8:0:4d::
allocated address 2001:db8:0:4e::
allocated address 2001:db8:0:4f::
allocated address 2001:db8:0:50::
allocated address 2001:db8:0:51::
allocated address 2001:db8:0:52::
allocated address 2001:db8:0:53::
allocated address 2001:db8:0:54::
allocated address 2001:db8:0:55::
allocated address 2001:db8:0:56::
allocated address 2001:db8:0:57::
allocated address 2001:db8:0:58::
allocated address 2001:db8:0:59::
allocated address 2001:db8:0:5a::
allocated address 2001:db8:0:5b::
allocated address 2001:db8:0:5c::
allocated address 2001:db8:0:5d::
allocated address 2001:db8:0:5e::
allocated address 2001:db8:0:5f::
allocated address 2001:db8:0:60::
allocated address 2001:db8:0:61::
allocated address 2001:db8:0:62::
allocated address 2001:db8:0:63::
allocated address 2001:db8:0:64::
allocated address 2001:db8:0:65::
allocated address 2001:db8:0:66::
allocated address 2001:db8:0:67::
allocated address 2001:db8:0:68::
allocated address 2001:db8:0:69::
allocated address 2001:db8:0:6a::
allocated address 2001:db8:0:6b::
allocated address 2001:db8:0:6c::
allocated address 2001:db8:0:6d::
allocated address 2001:db8:0:6e::
allocated address 2001:db8:0:6f::
allocated address 2001:db8:0:70::
allocated address 2001:db8:0:71::
allocated address 2001:db8:0:72::
allocated address 2001:db8:0:73::
allocated address 2001:db8:0:74::
allocated address 2001:db8:0:75::
allocated address 2001:db8:0:76::
allocated address 2001:db8:0:77::
allocated address 2001:db8:0:78::
allocated address 2001:db8:0:79::
allocated address 2001:db8:0:7a::
allocated address 2001:db8:0:7b::
allocated address 2001:db8:0:7c::
allocated address 2001:db8:0:7d::
allocated address 2001:db8:0:7e::
allocated address 2001:db8:0:7f::
allocated address 2001:db8:0:80::
allocated address 2001:db8:0:81::
allocated address 2001:db8:0:82::
allocated address 2001:db8:0:83::
allocated address 2001:db8:0:84::
allocated address 2001:db8:0:85::
allocated address 2001:db8:0:86::
allocated address 2001:db8:0:87::
allocated address 2001:db8:0:88::
allocated address 2001:db8:0:89::
allocated address 2001:db8:0:8a::
allocated address 2001:db8:0:8b::
allocated address 2001:db8:0:8c::
allocated address 2001:db8:0:8d::
allocated address 2001:db8:0:8e::
allocated address 2001:db8:0:8f::
allocated address 2001:db8:0:90::
allocated address 2001:db8:0:91::
allocated address 2001:db8:0:92::
allocated address 2001:db8:0:93::
allocated address 2001:db8:0:94::
allocated address 2001:db8:0:95::
allocated address 2001:db8:0:96::
allocated address 2001:db8:0:97::
allocated address 2001:db8:0:98::
allocated address 2001:db8:0:99::
allocated address 2001:db8:0:9a::
allocated address 2001:db8:0:9b::
allocated address 2001:db8:0:9c::
allocated address 2001:db8:0:9d::
allocated address 2001:db8:0:9e::
allocated address 2001:db8:0:9f::
allocated address 2001:db8:0:a0::
allocated address 2001:db8:0:a1::
allocated address 2001:db8:0:a2::
allocated address 2001:db8:0:a3::
allocated address 2001:db8:0:a4::
allocated address 2001:db8:0:a5::
allocated address 2001:db8:0:a6::
allocated address 2001:db8:0:a7::
allocated address 2001:db8:0:a8::
allocated address 2001:db8:0:a9::
allocated address 2001:db8:0:aa::
allocated address 2001:db8:0:ab::
allocated address 2001:db8:0:ac::
allocated address 2001:db8:0:ad::
allocated address 2001:db8:0:ae::
allocated address 2001:db8:0:af::
allocated address 2001:db8:0:b0::
allocated address 2001:db8:0:b1::
allocated address 2001:db8:0:b2::
allocated address 2001:db8:0:b3::
allocated address 2001:db8:0:b4::
allocated address 2001:db8:0:b5::
allocated address 2001:db8:0:b6::
allocated address 2001:db8:0:b7::
allocated address 2001:db8:0:b8::
allocated address 2001:db8:0:b9::
allocated address 2001:db8:0:ba::
allocated address 2001:db8:0:bb::
allocated address 2001:db8:0:bc::
allocated address 2001:db8:0:bd::
allocated address 2001:db8:0:be::
allocated address 2001:db8:0:bf::
allocated address 2001:db8:0:c0::
allocated address 2001:db8:0:c1::
allocated address 2001:db8:0:c2::
allocated address 2001:db8:0:c3::
allocated address 2001:db8:0:c4::
allocated address 2001:db8:0:c5::
allocated address 2001:db8:0:c6::
allocated address 2001:db8:0:c7::
allocated address 2001:db8:0:c8::
allocated address 2001:db8:0:c9::
allocated address 2001:db8:0:ca::
allocated address 2001:db8:0:cb::
allocated address 2001:db8:0:cc::
allocated address 2001:db8:0:cd::
allocated address 2001:db8:0:ce::
allocated address 2001:db8:0:cf::
allocated address 2001:db8:0:d0::
allocated address 2001:db8:0:d1::
allocated address 2001:db8:0:d2::
allocated address 2001:db8:0:d3::
allocated address 2001:db8:0:d4::
allocated address 2001:db8:0:d5::
allocated address 2001:db8:0:d6::
allocated address 2001:db8:0:d7::
allocated address 2001:db8:0:d8::
allocated address 2001:db8:0:d9::
allocated address 2001:db8:0:da::
allocated address 2001:db8:0:db::
allocated address 2001:db8:0:dc::
allocated address 2001:db8:0:dd::
allocated address 2001:db8:0:de::
allocated address 2001:db8:0:df::
allocated address 2001:db8:0:e0::
allocated address 2001:db8:0:e1::
allocated address 2001:db8:0:e2::
allocated address 2001:db8:0:e3::
allocated address 2001:db8:0:e4::
allocated address 2001:db8:0:e5::
allocated address 2001:db8:0:e6::
allocated address 2001:db8:0:e7::
allocated address 2001:db8:0:e8::
allocated address 2001:db8:0:e9::
allocated address 2001:db8:0:ea::
allocated address 2001:db8:0:eb::
allocated address 2001:db8:0:ec::
allocated address 2001:db8:0:ed::
allocated address 2001:db8:0:ee::
allocated address 2001:db8:0:ef::
allocated address 2001:db8:0:f0::
allocated address 2001:db8:0:f1::
allocated address 2001:db8:0:f2::
allocated address 2001:db8:0:f3::
allocated address 2001:db8:0:f4::
allocated address 2001:db8:0:f5::
allocated address 2001:db8:0:f6::
allocated address 2001:db8:0:f7::
allocated address 2001:db8:0:f8::
allocated address 2001:db8:0:f9::
allocated address 2001:db8:0:fa::
allocated address 2001:db8:0:fb::
allocated address 2001:db8:0:fc::
allocated address 2001:db8:0:fd::
allocated address 2001:db8:0:fe::
allocated address 2001:db8:0:ff::

34
tests/testsuite.at Normal file
View File

@@ -0,0 +1,34 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([ippool])
AT_KEYWORDS([ippool])
cat $abs_srcdir/lib/ippool_test.ok > expout
cat $abs_srcdir/lib/ippool_test.err > experr
AT_CHECK([$abs_top_builddir/tests/lib/ippool_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([ippool_v6])
AT_KEYWORDS([ippool_v6])
cat $abs_srcdir/lib/ippool_v6_test.ok > expout
cat $abs_srcdir/lib/ippool_v6_test.err > experr
AT_CHECK([$abs_top_builddir/tests/lib/ippool_test -v6], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([in46a])
AT_KEYWORDS([in46a])
cat $abs_srcdir/lib/in46a_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/lib/in46a_test], [], [expout], [])
AT_CLEANUP
AT_SETUP([in46a_v6])
AT_KEYWORDS([in46a_v6])
cat $abs_srcdir/lib/in46a_v6_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/lib/in46a_test -v6], [], [expout], [])
AT_CLEANUP
AT_SETUP([gtpie])
AT_KEYWORDS([gtpie])
cat $abs_srcdir/gtp/gtpie_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gtp/gtpie_test], [], [expout], [])
AT_CLEANUP