Compare commits

...

77 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
45 changed files with 2509 additions and 1508 deletions

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

@@ -18,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)
@@ -66,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"])
@@ -136,9 +135,9 @@ 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(
@@ -152,6 +151,27 @@ then
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
doc/examples/Makefile

View File

@@ -18,6 +18,9 @@ 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]")
@@ -35,7 +38,7 @@ set -x
cd "$base"
autoreconf --install --force
./configure CFLAGS="-Werror" CPPFLAGS="-Werror" $GTP
./configure --enable-sanitize --enable-werror $GTP
$MAKE $PARALLEL_MAKE
$MAKE distcheck

View File

@@ -5,7 +5,7 @@ After=networking.service
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg -f
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
RestartSec=2
RestartPreventExitStatus=1

138
debian/changelog vendored
View File

@@ -1,3 +1,141 @@
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

8
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: libgtp2
Package: libgtp3
Architecture: any
Multi-Arch: same
Section: libs
@@ -41,7 +41,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
libgtp2 (= ${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,7 +54,7 @@ Package: osmo-ggsn-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${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
@@ -65,7 +65,7 @@ Package: libgtp-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${binary:Version})
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

6
debian/rules vendored
View File

@@ -16,8 +16,4 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
override_dh_strip:
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
dh_strip -plibgtp2 --dbg-package=libgtp-dbg
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf
dh_strip -plibgtp3 --dbg-package=libgtp-dbg

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"
@@ -124,13 +125,14 @@ 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);
}
/* release tun device */
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
osmo_fd_unregister(&apn->tun.fd);
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
/* release tun device */
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
osmo_fd_unregister(&apn->tun.fd);
}
tun_free(apn->tun.tun);
apn->tun.tun = NULL;
}
gtp_kernel_stop(apn->tun.cfg.dev_name);
if (apn->v4.pool) {
LOGPAPN(LOGL_INFO, apn, "Releasing IPv4 pool\n");
@@ -194,6 +196,8 @@ int apn_start(struct apn_ctx *apn)
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;
@@ -202,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;
}
@@ -214,50 +218,6 @@ int apn_start(struct apn_ctx *apn)
/* Set TUN library callback */
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
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,
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));
apn_stop(apn, false);
return -1;
}
}
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,
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
apn_stop(apn, false);
return -1;
}
}
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);
}
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
if (tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK) < 1) {
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of "
"interface: %s\n", strerror(errno));
apn_stop(apn, false);
return -1;
}
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
}
/* set back-pointer from TUN device to APN */
apn->tun.tun->priv = apn;
break;
case APN_GTPU_MODE_KERNEL_GTP:
LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
@@ -266,9 +226,17 @@ int apn_start(struct apn_ctx *apn)
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 (gtp_kernel_init(apn->ggsn->gsn, apn->tun.cfg.dev_name,
&apn->v4.cfg.ifconfig_prefix, apn->tun.cfg.ipup_script) < 0) {
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;
@@ -277,6 +245,68 @@ int apn_start(struct apn_ctx *apn)
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_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));
apn_stop(apn, false);
return -1;
}
}
if (apn->v6.cfg.ifconfig_prefix.addr.len) {
LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
apn->v6.cfg.ifconfig_prefix.prefixlen)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
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);
}
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",
@@ -335,21 +365,26 @@ 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) {
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
ippool_freeip(ipp->pool, ipp);
} else
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
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(member->pool, member);
} else if(i == 0)
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
}
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
strerror(errno));
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;
@@ -357,6 +392,44 @@ static int delete_context(struct pdp_t *pdp)
#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,
@@ -390,85 +463,115 @@ 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;
}
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 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)
/* 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;
/* Three byte T16L header */
msgb_put_u16(msg, 0x8021); /* IPCP */
len1 = msgb_put(msg, 1); /* Length of contents: delay */
/* 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;
msgb_put_u8(msg, 0x02); /* ACK */
msgb_put_u8(msg, id); /* ID: Needs to match request */
msgb_put_u8(msg, 0x00); /* Length MSB */
len2 = msgb_put(msg, 1); /* Length LSB: delay */
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;
if (dns1 && dns1->len == 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);
/* 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, ipcp[1]); /* ID: Needs to match request */
msgb_put_u8(msg, 0x00); /* Length MSB */
len2 = msgb_put(msg, 1); /* Length LSB: delay */
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, ntohl(dns1->v4.s_addr));
}
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, ntohl(dns2->v4.s_addr));
}
/* patch in length values */
len_appended = msg->tail - start;
*len1 = len_appended - 3;
*len2 = len_appended - 3;
offset += 3 + ipcp_len;
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
}
if (dns2 && dns2->len == 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);
}
/* patch in length values */
len_appended = msg->tail - start;
*len1 = len_appended - 3;
*len2 = len_appended - 3;
return 0;
}
/* 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)
@@ -477,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)
@@ -514,10 +617,11 @@ 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);
@@ -552,56 +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)) {
/* does this APN actually have an IPv4 pool? */
if (!apn_supports_ipv4(apn))
goto err_wrong_af;
/* 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, 0);
if (rc < 0)
goto err_pool_full;
in46a_to_eua(&member->addr, &pdp->eua);
rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
if (rc < 0)
goto err_pool_full;
/* copy back */
memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
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 */
/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
addrv6 = member;
} else
OSMO_ASSERT(0);
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;
}
} else if (addr.len == sizeof(struct in6_addr)) {
struct in46_addr tmp;
}
/* 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, 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);
/* 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);
} else
OSMO_ASSERT(0);
pdp->peer = member;
pdp->ipif = apn->tun.tun; /* TODO */
pdp->priv = apn;
member->peer = pdp;
/* 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;
@@ -612,8 +729,10 @@ int create_context_ind(struct pdp_t *pdp)
/* 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, IP=%s\n",
name_buf, apn->cfg.name, pdp->teic_own, in46a_ntoa(&member->addr));
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 */
@@ -637,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;
}
@@ -661,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);
@@ -685,19 +815,53 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
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;
OSMO_ASSERT(tun);
OSMO_ASSERT(apn);
LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n");
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, &apn->v6_lladdr, 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",
@@ -929,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);
@@ -938,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);
@@ -961,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 */

View File

@@ -513,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,
@@ -668,6 +686,8 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
}
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);
@@ -893,6 +913,8 @@ 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);
@@ -926,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

@@ -180,17 +180,15 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
}
/* handle incoming packets to the all-routers multicast address */
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
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;
@@ -221,7 +219,7 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_a
/* Send router advertisement from GGSN link-local
* address to MS link-local address, including prefix
* allocated to this PDP context */
msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, &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,5 +3,7 @@
#include "../gtp/gtp.h"
#include "../gtp/pdp.h"
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
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)

381
gtp/gtp.c
View File

@@ -1,19 +1,19 @@
/*
/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2010-2011, 2016-2017 Harald Welte <laforge@gnumonks.org>
* Copyright (C) 2015-2017 sysmocom - s.f.m.c. GmbH
*
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*
*/
/*
* gtp.c: Contains all GTP functionality. Should be able to handle multiple
* tunnels in the same program.
* tunnels in the same program.
*
* TODO:
* - Do we need to handle fragmentation?
@@ -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 =
@@ -281,19 +349,19 @@ static uint32_t get_tei(void *pack)
/* ***********************************************************
* Reliable delivery of signalling messages
*
*
* Sequence numbers are used for both signalling messages and
* data messages.
*
* For data messages each tunnel maintains a sequence counter,
* which is incremented by one each time a new data message
* is sent. The sequence number starts at (0) zero at tunnel
* establishment, and wraps around at 65535 (29.060 9.3.1.1
* establishment, and wraps around at 65535 (29.060 9.3.1.1
* and 09.60 8.1.1.1). The sequence numbers are either ignored,
* or can be used to check the validity of the message in the
* receiver, or for reordering af packets.
*
* For signalling messages the sequence number is used by
* For signalling messages the sequence number is used by
* signalling messages for which a response is defined. A response
* message should copy the sequence from the corresponding request
* message. The sequence number "unambiguously" identifies a request
@@ -311,7 +379,7 @@ static uint32_t get_tei(void *pack)
* with path setup and teardown.
*
* If a response message is lost, the request will be retransmitted, and
* the receiving GSN will receive a "duplicated" request. The standard
* the receiving GSN will receive a "duplicated" request. The standard
* requires the receiving GSN to send a response, with the same information
* as in the original response. For most messages this happens automatically:
*
@@ -326,22 +394,22 @@ static uint32_t get_tei(void *pack)
* a nonexist reply message.
*
* The correct solution will be to make a queue containing response messages.
* This queue should be checked whenever a request is received. If the
* This queue should be checked whenever a request is received. If the
* response is allready in the queue that response should be transmitted.
* It should be possible to find messages in this queue on the basis of
* the sequence number and peer GSN IP address (The sequense number is unique
* within each path). This need to be implemented by a hash table. Furthermore
* it should be possibly to delete messages based on a timeout. This can be
* achieved by means of a linked list. The timeout value need to be larger
* than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
* than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are
* set in the peer GSN, so there is no way to know these parameters. On the
* other hand the timeout value need to be so small that we do not receive
* wraparound sequence numbere before the message is deleted. 60 seconds is
* probably not a bad choise.
*
*
* This queue however is first really needed from gtp1.
*
* gtp_req:
* gtp_req:
* Send off a signalling message with appropiate sequence
* number. Store packet in queue.
* gtp_conf:
@@ -357,7 +425,7 @@ 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)
{
@@ -432,7 +500,7 @@ 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);
@@ -523,7 +591,7 @@ 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)
{
@@ -581,7 +649,7 @@ 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)
{
@@ -626,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;
@@ -852,7 +920,7 @@ int gtp_free(struct gsn_t *gsn)
* For response messages we need to be able to respond to
* the relevant src port even if it is locally allocated by
* the peer.
*
*
* The need for path management!
* We might need to keep a list of active paths. This might
* be in the form of remote IP address + UDP port numbers.
@@ -932,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;
}
@@ -942,9 +1009,9 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
/* This message is somewhat special in that it actually is a
* response to some other message with unsupported GTP version
* For this reason it has parameters like a response, and does
* its own message transmission. No signalling queue is used
* its own message transmission. No signalling queue is used
* The reply is sent to the peer IP and peer UDP. This means that
* the peer will be receiving a GTP0 message on a GTP1 port!
* the peer will be receiving a GTP0 message on a GTP1 port!
* In practice however this will never happen as a GTP0 GSN will
* only listen to the GTP0 port, and therefore will never receive
* anything else than GTP0 */
@@ -971,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;
@@ -992,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)
{
@@ -1007,7 +1074,7 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
* Messages: create, update and delete PDP context
*
* Information storage
* Information storage for each PDP context is defined in
* Information storage for each PDP context is defined in
* 23.060 section 13.3. Includes IMSI, MSISDN, APN, PDP-type,
* PDP-address (IP address), sequence numbers, charging ID.
* For the SGSN it also includes radio related mobility
@@ -1015,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;
@@ -1093,7 +1160,7 @@ extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
pdp->cch_pdp);
}
/* TODO
/* TODO
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
pdp->traceref);
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
@@ -1265,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);
@@ -1365,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) */
@@ -1537,7 +1606,7 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
(!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l)))
{
/* OK! We are dealing with the same APN. We will copy new
* parameters to the old pdp and send off confirmation
* parameters to the old pdp and send off confirmation
* We ignore the following information elements:
* QoS: MS will get originally negotiated QoS.
* End user address (EUA). MS will get old EUA anyway.
@@ -1567,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);
@@ -1588,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 */
@@ -1651,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) */
@@ -1853,14 +1927,14 @@ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi);
/* TODO
/* TODO
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
pdp->traceref);
gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
pdp->tracetype); */
/* TODO if ggsn update message
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
pdp->eua.l, pdp->eua.v);
*/
@@ -1891,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)
@@ -1932,8 +2006,8 @@ int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID,
pdp->teid_own);
/* If ggsn
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
/* If ggsn
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
pdp->eua.l, pdp->eua.v); */
gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
@@ -1953,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)
{
@@ -2061,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) {
@@ -2127,7 +2200,7 @@ int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
"Missing mandatory information field");
memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
return gtp_update_pdp_resp(gsn, version, pdp,
return gtp_update_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
} */
@@ -2181,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;
@@ -2222,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 */
@@ -2292,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;
@@ -2338,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;
}
@@ -2397,7 +2488,7 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
if (pdp_getgtp1
(&secondary_pdp,
linked_pdp->secondary_tei[n])) {
LOGP(DLGTP, LOGL_ERROR,
LOGP(DLGTP, LOGL_ERROR,
"Unknown secondary PDP context\n");
return EOF;
}
@@ -2508,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 */
}
}
@@ -2525,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;
}
@@ -2547,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;
}
@@ -2557,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)
{
@@ -2591,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];
@@ -2647,16 +2754,16 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
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++;
@@ -2666,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++;
@@ -2681,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 */
@@ -2700,10 +2810,10 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version,
return 0;
}
/* Receives GTP packet and sends off for further processing
/* Receives GTP packet and sends off for further processing
* Function will check the validity of the header. If the header
* is not valid the packet is either dropped or a version not
* supported is returned to the peer.
* is not valid the packet is either dropped or a version not
* supported is returned to the peer.
* TODO: Need to decide on return values! */
int gtp_decaps0(struct gsn_t *gsn)
{
@@ -2712,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 */
@@ -2748,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 (GTPHDR_F_GET_VER(pheader->flags) > 0) {
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;
}
@@ -2780,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 */
}
@@ -2857,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 */
@@ -2893,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 (GTPHDR_F_GET_VER(pheader->flags) > 1) {
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;
@@ -2907,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 (GTPHDR_F_GET_VER(pheader->flags) < 1) {
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;
}
@@ -2955,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 */
}
@@ -3032,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 */
@@ -3069,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 (GTPHDR_F_GET_VER(pheader->flags) > 1) {
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;
}
@@ -3082,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 (GTPHDR_F_GET_VER(pheader->flags) < 1) {
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;
}
@@ -3241,19 +3357,10 @@ 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:
* - eua: End User Address. (29.060, 7.7.27, message type 128)
* - eua: End User Address. (29.060, 7.7.27, message type 128)
* Used for signalling address to mobile station. Supports IPv4
* IPv6 x.25 etc. etc.
* - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address

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 */
@@ -264,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 */
@@ -317,7 +325,10 @@ 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);
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,
void *pack, unsigned len);
@@ -351,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

@@ -1,17 +1,17 @@
/*
/*
* 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
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*
*/
/*
* pdp.c:
* pdp.c:
*
*/
@@ -44,17 +44,17 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Functions related to PDP storage
*
* Lifecycle
* For a GGSN pdp context life begins with the reception of a
* For a GGSN pdp context life begins with the reception of a
* create pdp context request. It normally ends with the reception
* of a delete pdp context request, but will also end with the
* reception of an error indication message.
* reception of an error indication message.
* Provisions should probably be made for terminating pdp contexts
* based on either idle timeout, or by sending downlink probe
* based on either idle timeout, or by sending downlink probe
* messages (ping?) to see if the MS is still responding.
*
*
* For an SGSN pdp context life begins with the application just
* before sending off a create pdp context request. It normally
* ends when a delete pdp context response message is received
* ends when a delete pdp context response message is received
* from the GGSN, but should also end when with the reception of
* an error indication message.
*
@@ -64,15 +64,15 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Downlink packets received in the GGSN are identified only by their
* network interface together with their destination IP address (Two
* network interfaces can use the same private IP address). Each IMSI
* (mobile station) can have several PDP contexts using the same IP
* (mobile station) can have several PDP contexts using the same IP
* address. In this case the traffic flow template (TFT) is used to
* determine the correct PDP context for a particular IMSI. Also it
* determine the correct PDP context for a particular IMSI. Also it
* should be possible for each PDP context to use several IP adresses
* For fixed wireless access a mobile station might need a full class
* C network. Even in the case of several IP adresses the PDP context
* should be determined on the basis of the network IP address.
* Thus we need a hash table based on network interface + IP address.
*
*
* Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
* is collectively called the tunnel identifier. There is also a 16 bit
* flow label that can be used for identification of uplink packets. This
@@ -85,7 +85,7 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
* Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
* be used for directly addressing the PDP context.
* pdp_newpdp
* pdp_newpdp
* Gives you a pdp context with no hash references In some way
* this should have a limited lifetime.
*
@@ -296,7 +296,7 @@ int pdp_iphash(void* ipif, struct ul66_t *eua) {
/#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
}
int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
int hash;
struct pdp_t *pdp2;
@@ -304,7 +304,7 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
(unsigned) ipif, eua->l,
eua->v[2], eua->v[3],
eua->v[2], eua->v[3],
eua->v[4], eua->v[5]);
pdp->ipnext = NULL;
@@ -316,9 +316,9 @@ int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
pdp_prev = pdp2;
if (!pdp_prev)
if (!pdp_prev)
haship[hash] = pdp;
else
else
pdp_prev->ipnext = pdp;
if (PDP_DEBUG) printf("End pdp_ipset\n");
return 0;
@@ -331,9 +331,9 @@ int pdp_ipdel(struct pdp_t *pdp) {
if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
if (pdp2 == pdp) {
if (!pdp_prev)
if (!pdp_prev)
haship[hash] = pdp2->ipnext;
else
else
pdp_prev->ipnext = pdp2->ipnext;
if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
return 0;
@@ -347,41 +347,23 @@ int pdp_ipdel(struct pdp_t *pdp) {
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
int hash = pdp_iphash(ipif, eua);
struct pdp_t *pdp2;
/#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
/#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
(memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
*pdp = pdp2;
/#printf("End pdp_ipget. Found\n");#/
return 0;
}
}
if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
(unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
return EOF; /# End of linked list and not found #/
}
*/
/* Various conversion functions */
int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
{
eua->l = 6;
eua->v[0] = PDP_EUA_ORG_IETF;
eua->v[1] = PDP_EUA_TYPE_v4;
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] != PDP_EUA_ORG_IETF) || (eua->v[1] != PDP_EUA_TYPE_v4)) {
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

@@ -1,13 +1,13 @@
/*
/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003 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
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*
*/
#ifndef _PDP_H
@@ -26,6 +26,7 @@ struct gsn_t;
#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 *
@@ -71,16 +72,16 @@ struct ul255_t {
* and 09.60.
* 31 * 4 + 15 structs + = 120 + 15 structs ~ 2k / context
* Structs: IP address 16+4 bytes (6), APN 255 bytes (2)
* QOS: 255 bytes (3), msisdn 16 bytes (1),
* QOS: 255 bytes (3), msisdn 16 bytes (1),
*
* TODO: We need to consider who manages the pdp_t hash tables
* Is it gtp_lib, or is it the application?
* I suppose that it will be gtp_lib.
* I suppose that it will be gtp_lib.
* SGSN will ask gtplib for new pdp_t. Fill out the fields,
* and pass it on to gtp_create_pdp_req.
* GGSN will receive gtp_create_pdp_ind, create new pdp_t and
* send responce to SGSN.
* SGSN will receive response and gtplib will find the
* SGSN will receive response and gtplib will find the
* original pdp_t corresponding to the request. This will be
* passed on to the application.
* Eventually the SGSN will close the connection, and the
@@ -88,10 +89,10 @@ struct ul255_t {
* This means that gtplib need to have functions to
* allocate, free, sort and find pdp_t
* (newpdp, freepdp, getpdp)
* Hash tables: TID, IMSI, IP etc.)
* Hash tables: TID, IMSI, IP etc.)
*
*
* Secondary PDP Context Activation Procedure
* Secondary PDP Context Activation Procedure
*
* With GTP version 1 it is possible to establish multiple PDP
* contexts with the same IP address. With this scheme the first
@@ -121,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. */
@@ -266,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

View File

@@ -77,56 +77,20 @@ static int gtp_kernel_init_once(void)
return 0;
}
int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup)
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
{
struct in_addr net;
const char *net_arg;
if (!gtp_nl.nl)
gtp_kernel_init_once();
if (prefix->addr.len != 4) {
LOGP(DGGSN, LOGL_ERROR, "we only support IPv4 in this path :/");
if (gtp_kernel_init_once() < 0)
return -1;
}
net = prefix->addr.v4;
if (gtp_dev_create(-1, devname, gsn->fd0, gsn->fd1u) < 0) {
LOGP(DGGSN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
strerror(errno));
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;
}
net_arg = in46p_ntoa(prefix);
DEBUGP(DGGSN, "Setting route to reach %s via %s\n", net_arg, devname);
if (gtp_dev_config(devname, &net, prefix->prefixlen) < 0) {
LOGP(DGGSN, LOGL_ERROR, "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, devname, net_arg);
cmd[sizeof(cmd)-1] = '\0';
err = system(cmd);
if (err < 0) {
LOGP(DGGSN, LOGL_ERROR, "Failed to launch script `%s'\n", ipup);
return -1;
}
}
LOGP(DGGSN, LOGL_NOTICE, "GTP kernel configured\n");
return 0;
return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u);
}
void gtp_kernel_stop(const char *devname)

View File

@@ -7,18 +7,20 @@ extern int debug;
extern char *ipup;
#ifdef GTP_KERNEL
int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup);
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_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup)
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) {}

View File

@@ -253,33 +253,66 @@ unsigned int in46a_netmasklen(const struct in46_addr *netmask)
}
}
/*! 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)
/*! 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)
{
switch (src->len) {
case 4:
eua->l = 6;
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] = 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:
OSMO_ASSERT(0);
return -1;
const struct in46_addr *src_v4, *src_v6;
if (size == 1) {
switch (src->len) {
case 4:
eua->l = 6;
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] = 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:
OSMO_ASSERT(0);
return -1;
}
return 0;
}
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 in46_addr to PDP End User Address
* \returns 0 on success; negative on error */
/*! 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)
@@ -295,22 +328,46 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
else
dst->v4.s_addr = 0;
break;
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);
break;
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;
}
dst[0].len = 4;
dst[1].len = 16;
return 2;
default:
return -1;
}
return 0;
default_to_dyn_v4:
/* assume dynamic IPv4 by default */
dst->len = 4;
dst->v4.s_addr = 0;
return 0;
return 1;
}

View File

@@ -29,5 +29,5 @@ extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr
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);

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

672
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,527 +58,98 @@
#include "tun.h"
#include "syserr.h"
#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;
}
#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(&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(&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)
{
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);
case 16:
return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
default:
return -1;
}
}
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;
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 */
memset(&areq, 0, sizeof(areq));
rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
if (rc < 0)
return rc;
/* 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
return rc;
}
static int tun_route(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask, int delete)
static int tun_addaddr6(struct tun_t *this,
struct in6_addr *addr,
struct in6_addr *dstaddr, int prefixlen)
{
int rc;
#if defined(__linux__)
if (!this->addrs) /* Use ioctl for first addr to make ping work */
return tun_setaddr6(this, addr, dstaddr, prefixlen);
struct rtentry r;
int fd;
rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
if (rc < 0)
return rc;
memset(&r, '\0', sizeof(r));
r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
this->addrs++;
/* 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 rc;
}
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
{
struct in_addr netmask;
switch (addr->len) {
case 4:
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
case 16:
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
default:
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__)
@@ -602,31 +172,50 @@ int tun_new(struct tun_t **tun, const char *dev_name)
(*tun)->routes = 0;
#if defined(__linux__)
/* Open the actual tun device */
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
goto err_free;
if (!use_kernel) {
/* Open the actual tun device */
if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
goto err_free;
}
/* Set device flags. For some weird reason this is also the method
used to obtain the network interface name */
memset(&ifr, 0, sizeof(ifr));
if (dev_name)
strcpy(ifr.ifr_name, dev_name);
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
goto err_close;
}
strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
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;
}
/* Set device flags. For some weird reason this is also the method
used to obtain the network interface name */
memset(&ifr, 0, sizeof(ifr));
if (dev_name)
strcpy(ifr.ifr_name, dev_name);
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
goto err_close;
}
strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
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);
@@ -678,13 +267,17 @@ 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 (close(tun->fd)) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
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 */
free(tun);
@@ -745,79 +338,6 @@ int tun_runscript(struct tun_t *tun, char *script)
return 0;
}
#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;
}
/*! 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.

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,43 +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"
/* 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 */
#include "netdev.h"
/* ***********************************************************
* Information storage for each tun instance
@@ -75,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,
@@ -95,9 +56,6 @@ extern int tun_set_cb_ind(struct tun_t *this,
extern int tun_runscript(struct tun_t *tun, char *script);
int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
size_t prefix_size, int flags);
int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
size_t prefix_size, int flags);

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

View File

@@ -41,7 +41,7 @@ const char *gengetopt_args_info_help[] = {
" --pidfile=STRING Filename of process id file\n (default=`./sgsnemu.pid')",
" --statedir=STRING Directory of nonvolatile data (default=`./')",
" --dns=STRING DNS Server to use",
" -l, --listen=STRING Local interface",
" -l, --listen=STRING Local host",
" -r, --remote=STRING Remote host",
" --contexts=INT Number of contexts (default=`1')",
" --timelimit=INT Exit after timelimit seconds (default=`0')",
@@ -65,12 +65,14 @@ const char *gengetopt_args_info_help[] = {
" --charging=INT Charging characteristics (default=`0x0800')",
" -u, --uid=STRING Login user ID (default=`mig')",
" -p, --pwd=STRING Login password (default=`hemmelig')",
"\n Mode: createif\n any option of this mode is related to tun interface, all payload going in and\n out via tunN interface",
" --createif Create local network interface (default=off)",
" -n, --net=STRING Network address for local interface",
" --defaultroute Create default route (default=off)",
" --ipup=STRING Script to run after link-up",
" --ipdown=STRING Script to run after link-down",
" --tun-device=STRING Name of the local network interface",
"\n Mode: pinghost\n generate ICMP payload inside G-PDU without setting up tun interface",
" --pinghost=STRING Ping remote host",
" --pingrate=INT Number of ping req per second (default=`1')",
" --pingsize=INT Number of ping data bytes (default=`56')",
@@ -168,6 +170,8 @@ void clear_given(struct gengetopt_args_info *args_info)
args_info->pingquiet_given = 0;
args_info->no_tx_gpdu_seq_given = 0;
args_info->pdp_type_given = 0;
args_info->createif_mode_counter = 0;
args_info->pinghost_mode_counter = 0;
}
static
@@ -290,19 +294,19 @@ void init_args_info(struct gengetopt_args_info *args_info)
args_info->charging_help = gengetopt_args_info_help[28];
args_info->uid_help = gengetopt_args_info_help[29];
args_info->pwd_help = gengetopt_args_info_help[30];
args_info->createif_help = gengetopt_args_info_help[31];
args_info->net_help = gengetopt_args_info_help[32];
args_info->defaultroute_help = gengetopt_args_info_help[33];
args_info->ipup_help = gengetopt_args_info_help[34];
args_info->ipdown_help = gengetopt_args_info_help[35];
args_info->tun_device_help = gengetopt_args_info_help[36];
args_info->pinghost_help = gengetopt_args_info_help[37];
args_info->pingrate_help = gengetopt_args_info_help[38];
args_info->pingsize_help = gengetopt_args_info_help[39];
args_info->pingcount_help = gengetopt_args_info_help[40];
args_info->pingquiet_help = gengetopt_args_info_help[41];
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[42];
args_info->pdp_type_help = gengetopt_args_info_help[43];
args_info->createif_help = gengetopt_args_info_help[32];
args_info->net_help = gengetopt_args_info_help[33];
args_info->defaultroute_help = gengetopt_args_info_help[34];
args_info->ipup_help = gengetopt_args_info_help[35];
args_info->ipdown_help = gengetopt_args_info_help[36];
args_info->tun_device_help = gengetopt_args_info_help[37];
args_info->pinghost_help = gengetopt_args_info_help[39];
args_info->pingrate_help = gengetopt_args_info_help[40];
args_info->pingsize_help = gengetopt_args_info_help[41];
args_info->pingcount_help = gengetopt_args_info_help[42];
args_info->pingquiet_help = gengetopt_args_info_help[43];
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[44];
args_info->pdp_type_help = gengetopt_args_info_help[45];
}
@@ -361,8 +365,7 @@ void cmdline_parser_params_init(struct cmdline_parser_params *params)
struct cmdline_parser_params *cmdline_parser_params_create(void)
{
struct cmdline_parser_params *params =
(struct cmdline_parser_params *)
struct cmdline_parser_params *params = (struct cmdline_parser_params *)
malloc(sizeof(struct cmdline_parser_params));
cmdline_parser_params_init(params);
return params;
@@ -853,6 +856,30 @@ int update_arg(void *field, char **orig_field,
return 0; /* OK */
}
static int check_modes(int given1[], const char *options1[],
int given2[], const char *options2[])
{
int i = 0, j = 0, errors = 0;
while (given1[i] >= 0) {
if (given1[i]) {
while (given2[j] >= 0) {
if (given2[j]) {
++errors;
fprintf(stderr,
"%s: option %s conflicts with option %s\n",
package_name, options1[i],
options2[j]);
}
++j;
}
}
++i;
}
return errors;
}
int
cmdline_parser_internal(int argc, char **argv,
struct gengetopt_args_info *args_info,
@@ -976,7 +1003,7 @@ cmdline_parser_internal(int argc, char **argv,
goto failure;
break;
case 'l': /* Local interface. */
case 'l': /* Local host. */
if (update_arg((void *)&(args_info->listen_arg),
&(args_info->listen_orig),
@@ -1073,6 +1100,7 @@ cmdline_parser_internal(int argc, char **argv,
break;
case 'n': /* Network address for local interface. */
args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->net_arg),
&(args_info->net_orig),
@@ -1391,6 +1419,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"createif") == 0) {
args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->createif_flag), 0,
@@ -1405,6 +1434,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"defaultroute") == 0) {
args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->defaultroute_flag), 0,
@@ -1419,6 +1449,7 @@ cmdline_parser_internal(int argc, char **argv,
/* Script to run after link-up. */
else if (strcmp(long_options[option_index].name, "ipup")
== 0) {
args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->ipup_arg),
&(args_info->ipup_orig),
@@ -1434,6 +1465,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"ipdown") == 0) {
args_info->createif_mode_counter += 1;
if (update_arg((void *)&(args_info->ipdown_arg),
&(args_info->ipdown_orig),
@@ -1449,6 +1481,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"tun-device") == 0) {
args_info->createif_mode_counter += 1;
if (update_arg
((void *)&(args_info->tun_device_arg),
@@ -1465,6 +1498,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pinghost") == 0) {
args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pinghost_arg),
@@ -1481,6 +1515,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingrate") == 0) {
args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingrate_arg),
@@ -1496,6 +1531,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingsize") == 0) {
args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingsize_arg),
@@ -1512,6 +1548,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingcount") == 0) {
args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingcount_arg),
@@ -1527,6 +1564,7 @@ cmdline_parser_internal(int argc, char **argv,
else if (strcmp
(long_options[option_index].name,
"pingquiet") == 0) {
args_info->pinghost_mode_counter += 1;
if (update_arg
((void *)&(args_info->pingquiet_flag), 0,
@@ -1566,6 +1604,31 @@ cmdline_parser_internal(int argc, char **argv,
} /* switch */
} /* while */
if (args_info->createif_mode_counter
&& args_info->pinghost_mode_counter) {
int createif_given[] =
{ args_info->createif_given, args_info->net_given,
args_info->defaultroute_given, args_info->ipup_given,
args_info->ipdown_given, args_info->tun_device_given, -1
};
const char *createif_desc[] =
{ "--createif", "--net", "--defaultroute", "--ipup",
"--ipdown", "--tun-device", 0
};
int pinghost_given[] =
{ args_info->pinghost_given, args_info->pingrate_given,
args_info->pingsize_given, args_info->pingcount_given,
args_info->pingquiet_given, -1
};
const char *pinghost_desc[] =
{ "--pinghost", "--pingrate", "--pingsize", "--pingcount",
"--pingquiet", 0
};
error_occurred +=
check_modes(createif_given, createif_desc, pinghost_given,
pinghost_desc);
}
if (check_required) {
error_occurred +=
cmdline_parser_required2(args_info, argv[0],

View File

@@ -15,6 +15,10 @@
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
@@ -22,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
@@ -49,18 +53,18 @@ option "charging" - "Charging characteristics" int default="0x0800
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 dependon="createif" no
option "defaultroute" - "Create default route" flag dependon="createif" off
option "ipup" - "Script to run after link-up" string dependon="createif" no
option "ipdown" - "Script to run after link-down" string dependon="createif" no
option "tun-device" - "Name of the local network interface" string dependon="createif" 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" int default="1" dependon="pinghost" no
option "pingsize" - "Number of ping data bytes" int default="56" dependon="pinghost" no
option "pingcount" - "Number of ping req to send" int default="0" dependon="pinghost" no
option "pingquiet" - "Do not print ping packet info" flag dependon="pinghost" 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

@@ -69,11 +69,11 @@ extern "C" {
const char *dns_help;
/**< @brief DNS Server to use help description. */
char *listen_arg;
/**< @brief Local interface. */
/**< @brief Local host. */
char *listen_orig;
/**< @brief Local interface original value given at command line. */
/**< @brief Local host original value given at command line. */
const char *listen_help;
/**< @brief Local interface help description. */
/**< @brief Local host help description. */
char *remote_arg;
/**< @brief Remote host. */
char *remote_orig;
@@ -370,6 +370,10 @@ extern "C" {
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 */

View File

@@ -1,13 +1,13 @@
/*
/*
* 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
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*
*/
/*
@@ -20,6 +20,7 @@
#endif
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <ctype.h>
#include <netdb.h>
@@ -79,13 +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 */
char *tun_dev_name;
struct in_addr netaddr, destaddr, net; /* Network interface */
struct in46_addr netaddr, destaddr, net; /* Network interface */
size_t prefixlen;
char *ipup, *ipdown; /* Filename of scripts */
int defaultroute; /* Set up default route */
@@ -372,10 +374,10 @@ static int process_options(int argc, char **argv)
/* foreground */
/* If fg flag not given run as a daemon */
/* Do not allow sgsnemu to run as deamon
/* Do not allow sgsnemu to run as deamon
if (!args_info.fg_flag)
{
closelog();
closelog();
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
freopen("/dev/null", "r", stdin);
@@ -873,23 +875,21 @@ static int process_options(int argc, char **argv)
/* 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 */
@@ -1314,8 +1314,8 @@ 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 */
@@ -1402,7 +1402,7 @@ static 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);
@@ -1427,17 +1427,16 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
break;
}
if ((options.createif) && (!options.net.s_addr)) {
if ((options.createif) && (!options.net.len)) {
size_t prefixlen = 32;
if (addr.len == 16)
prefixlen = 64;
/* printf("Setting up interface and routing\n"); */
/* FIXME: use tun_addattr() not tun_setaddr() */
tun_setaddr(tun, &addr, &addr, prefixlen);
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);
@@ -1464,7 +1463,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
free(forwarding);
}
ipset((struct iphash_t *)pdp->peer, &addr);
ipset(iph, &addr);
state = 2; /* Connected */
@@ -1542,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))
@@ -1570,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, options.tun_dev_name)) {
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);
@@ -1580,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);
@@ -1617,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;
@@ -1700,7 +1699,7 @@ int main(int argc, char **argv)
pdp->hisaddr0 = options.remote;
pdp->hisaddr1 = options.remote;
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid,
512 = Flat rate, 256 = Hot billing */
pdp->tx_gpdu_seq = options.tx_gpdu_seq;

View File

View File

@@ -7,6 +7,7 @@
#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"
@@ -108,7 +109,9 @@ static void test_gtpie_tv8()
int main(int argc, char **argv)
{
osmo_init_logging(&log_info);
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);

View File

@@ -1,7 +1,11 @@
AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
EXTRA_DIST = ippool_test.ok ippool_test.err \
in46a_test.ok
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

View File

@@ -10,6 +10,7 @@
#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"
@@ -20,41 +21,24 @@ static const struct in46_addr g_ia4 = {
.v4.s_addr = 0x0d0c0b0a,
};
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(void)
{
struct in46_addr ia;
printf("Testing in46a_to_af()\n");
printf("Testing in46a_to_af() with IPv4 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
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(void)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
printf("Testing in46a_to_sas()\n");
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);
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(void)
@@ -63,7 +47,7 @@ static void test_in46a_ntop(void)
char buf[256];
const char *res;
printf("Testing in46a_ntop()\n");
printf("Testing in46a_ntop() with IPv4 addresses\n");
res = in46a_ntop(NULL, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
@@ -79,10 +63,6 @@ static void test_in46a_ntop(void)
res = in46a_ntop(&ia, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
printf("res = %s\n", res);
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_in46p_ntoa(void)
@@ -101,21 +81,14 @@ static void test_in46a_equal(void)
{
struct in46_addr b;
printf("Testing in46a_equal()\n");
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));
memset(&b, 0xff, sizeof(b));
b.len = g_ia6.len;
b.v6 = g_ia6.v6;
OSMO_ASSERT(in46a_equal(&g_ia6, &b));
}
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
size_t prefixlen)
{
@@ -134,7 +107,7 @@ static void test_in46a_within_mask(void)
{
struct in46_addr addr, mask;
printf("Testing in46a_within_mask()\n");
printf("Testing in46a_within_mask() with IPv4 addresses\n");
addr = g_ia4;
mask = g_ia4;
@@ -155,13 +128,9 @@ static void test_in46a_within_mask(void)
static void test_in46a_to_eua(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()\n");
printf("testing in46a_to_eua() with IPv4 addresses\n");
#if 0 /* triggers assert in current implementation */
const struct in46_addr ia_invalid = { .len = 3, };
@@ -169,22 +138,10 @@ static void test_in46a_to_eua(void)
#endif
/* IPv4 address */
OSMO_ASSERT(in46a_to_eua(&g_ia4, &eua) == 0);
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);
/* IPv6 address */
OSMO_ASSERT(in46a_to_eua(&g_ia6, &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, &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_from_eua(void)
@@ -193,16 +150,12 @@ static void test_in46a_from_eua(void)
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 };
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()\n");
printf("Testing in46a_from_eua() with IPv4 addresses\n");
/* default: v4 unspec */
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == 0);
@@ -221,30 +174,16 @@ static void test_in46a_from_eua(void)
/* unspecified V4 */
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
eua.l = sizeof(v4_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
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) == 0);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
OSMO_ASSERT(ia.len == 4);
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
/* unspecified V6 */
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
eua.l = sizeof(v6_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
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) == 0);
OSMO_ASSERT(ia.len == 16);
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
}
static void test_in46a_netmasklen(void)
@@ -274,7 +213,188 @@ static void test_in46a_netmasklen(void)
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,
@@ -307,20 +427,34 @@ static void test_in46a_netmasklen(void)
int main(int argc, char **argv)
{
osmo_init_logging(&log_info);
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_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();
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;
}

View File

@@ -1,19 +1,17 @@
Testing in46a_to_af()
Testing in46a_to_sas()
Testing in46a_ntop()
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
res = 102:304:506:708:90a:b0c:d0e:f10
in46p_ntoa() returns 16.32.48.0/24
Testing in46a_equal()
Testing in46a_within_mask()
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()
Testing in46a_from_eua()
testing in46a_to_eua() with IPv4 addresses
Testing in46a_from_eua() with IPv4 addresses
Testing in46a_netmasklen() with IPv4 addresses
Testing in46a_netmasklen() with IPv6 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

View File

@@ -7,6 +7,7 @@
#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"
@@ -113,22 +114,31 @@ static void test_pool_sizes(void)
/* 65534 addresses [0.1..255.254] */
test_pool_size("192.168.0.0/16", IPPOOL_NONETWORK | IPPOOL_NOBROADCAST, NULL, 0, 65534);
/* 256 prefixes of /64 each */
test_pool_size("2001:DB8::/56", 0, NULL, 0, 256);
/* 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)
{
osmo_init_logging(&log_info);
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_pool_sizes();
if (argc < 2 || strcmp(argv[1], "-v6")) {
test_pool_sizes();
} else {
test_pool_sizes_v6();
}
return 0;
}

View File

@@ -8,5 +8,3 @@ 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

View File

@@ -66301,263 +66301,6 @@ allocated address 192.168.255.251
allocated address 192.168.255.252
allocated address 192.168.255.253
allocated address 192.168.255.254
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::
testing pool for prefix 192.168.23.0/24, flags=0x3, blacklist_size=3, expected_size=253
allocated address 192.168.23.2
allocated address 192.168.23.3

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

View File

@@ -8,12 +8,25 @@ 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