Compare commits

...

110 Commits

Author SHA1 Message Date
Harald Welte
f5a268a96d Bump version: 1.2.2.44-6da8-dirty → 1.3.0
Change-Id: Ie12af1d57df178a9ab27937ef0b764c98dd96e32
2019-01-20 21:34:23 +01:00
Oliver Smith
6da888c5d0 contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: Ibd348eac3adca88663bd510172efbe4ec0bf3599
2018-12-06 13:43:26 +01:00
Oliver Smith
33c537e5a7 contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: I43e3b592c593237eb4df2d70c926c031ddb7b20b
2018-12-05 13:16:14 +01:00
Oliver Smith
aa69034c00 Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: I810b2b96ea077e8bd5ab01df5137e214a4349628
2018-12-04 15:33:20 +01:00
Oliver Smith
bf47f71785 build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I14533676d5774ee0d0ee5054ba77d7dac32cff43
2018-11-27 18:28:24 +01:00
Neels Hofmeyr
2b7a860ffb Merge history from osmo-gsm-manuals.git
Change-Id: Ic7cebd4e6f2836be80a6186939f98057969207d4
2018-11-27 18:27:58 +01:00
Neels Hofmeyr
932eeec240 ggsn: update vty reference
Change-Id: I8a5c37505c0180d5c04c7792d6d0afdb0dffb282
2018-11-27 18:27:47 +01:00
Harald Welte
6f7aabf6a3 vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
a491e42129 OsmoGGSN: update vty reference
Add new (generic) logging commands, talloc context print commands.
Add the jitter buffer logging category.

Change-Id: Ifdc735df6221bf6e9b6247912f2958974dcfc4f2
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
1ce111f72b OsmoGGSN: fix VTY additions' node IDs
Change-Id: I8cd5eb64300151d0de8023ed019568cfdc4fe453
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
e010dea56e OsmoGGSN vty: update VTY reference
Apply VTY reference changes from libosmocore: change node IDs from index
numbers to meaningful names from VTY node prompts.

Introduce section with common commands, do not repeat the common commands on
each child node.

Populate section names (so far empty).

Add apn / gpdu VTY reference.

This is generated using the recent libosmocore vty doc patches that conclude in
libosmocore change-id Iedd67750539b676271de0e0e9316d4e6f794406a.

Change-Id: Ia269c4bda0aa0b905abcccc75338f5f808e01727
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
f4530447f6 OsmoGGSN VTY ref: prep: convert newlines to unix
To omit whitespace changes in an upcoming patch that updates the VTY reference
and will use '\n' line breaks, convert line breaks from '\r\n' to '\n' without
any other changes.

Change-Id: Id0d1a3a82c3d670cbb041884554b5d79fdfb0f28
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
e7361067ac OsmoGGSN: typo: priveleges
Change-Id: Id7e59f5dfcbb632fde6c35ef014e9b85099fe06d
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
606837597f OsmoGGSN: add Routing section for IP forward and masquerading
Change-Id: Ie49ca7a45113f49e89ce09017500008cbec757f5
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
5f8b332e6b OsmoGGSN: multiple instances: mention GTP port
Change-Id: I781feeb955ace17d93206bc98d12bc423584ce32
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
43001cbc7a OsmoGGSN: more info on non-root operation / tun creation
Add examples for 'ip addr add' and mention correspondence to config file,
add examples for enabling masquerading and IP forwarding,
place the non-root config in its own section and highlight the diffs.

Add tiny hint at systemd-networkd.

Change-Id: I02bd9cfa35c7f2fb338d5d92c2e968fe80574a78
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
65d61c347b GGSN: don't say 'NITB'
Change-Id: I960ce8ee749621176ceaa556a1fe93b54e08b6fc
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
c8ca02b937 refactor Makefile build rules, don't use the FORCE
The initial goal was to make sure we don't have overall FORCE rules causing
unnecessary rebuilds -- annoying while writing documentation. As I looked
through possible dependencies, I finally understood what's going on here.

Remove code dup and nicely sort which belongs where in build/Makefile.*.inc. In
each, describe in a top comment how to use it, and also unify how they are
used:

- Rename Makefile.inc to Makefile.docbook.inc and refactor
- Add Makefile.vty-reference.inc
- Add Makefile.common.inc

Make sure that we accurately pick up all dependencies.

Drop use of the macro called 'command', that silenced the actual command lines
invoked and replaced them with short strings: it obscures what is actually
going on and makes the Makefiles hard to read and understand.

Each manual's makefile is greatly reduced to few definitions and a Makefile
include, e.g. one for asciidoc, one for VTY reference.

Move common/bsc_vty_additions.xml to OsmoBSC/vty/libbsc_vty_additions.xml, link
from OsmoNITB. It applies only to OsmoBSC and OsmoNITB.

Add a script that combines a VTY reference file with *all* additions files
found in a manual's vty/ dir. Call this from Makefile.vty-reference.inc.

Change-Id: I9758e04162a480e28c7dc83475b514cf7fd25ec0
2018-11-27 18:27:47 +01:00
Harald Welte
3ce5a3648a GGSN: Document how 'ip tuntap' is used for non-root; call netdev 'apn0'
* Some people want to manually create/configure their tun devices,
  show them how to do this using ip with the correct parameters
* Let's not call the network device 'ggsn' but rather 'apn0', as
  the device has a 1:1 correspondence to the APN, not to the GGSN.

Change-Id: I2fef818bfcb8cb521397136539f492922d5f6def
2018-11-27 18:27:47 +01:00
Max
a4cb02699e Expand OsmoGGSN manual
* add cross-references
* add example of running without root priviledges

Change-Id: I1743f370ee2b351d2847f2e29e0f59f35cd401f4
2018-11-27 18:27:47 +01:00
Harald Welte
f0fb2c2ddd OsmoGGSN: Add VTY reference manual
Change-Id: Iddf6fe26689172d7db001198943c816eaaed7931
2018-11-27 18:27:47 +01:00
Harald Welte
8a1e7b8658 initial version of OsmoGGSN user manual
Closes: OS#1721
Change-Id: I7cdf150e8dd4f9dfc5e6d28e780d05dc1e1e5458
2018-11-27 18:27:47 +01:00
Neels Hofmeyr
b7782d4d41 Importing history from osmo-gsm-manuals.git
Change-Id: I79f406ae78de4a82966cffebac0dcec2abab21c2
2018-11-27 18:27:29 +01:00
Stefan Sperling
b0b9c28284 properly store IPv6 addresses in struct tun_t
All addresses in struct tun_t were stored as an in_addr.
But IPv6 addresses need an in6_addr, so switch tun_t addresses
to the in64_addr wrapper struct.

This is an ABI break, as documented in TODO-RELEASE.

Fixes an out of bounds memcpy() identified by Coverity.

Change-Id: Idd2431ad25d7fa182e52e2bd5231ceb04d427c34
Related: CID#174278
2018-11-22 14:12:40 +00:00
Stefan Sperling
3730c550cd fix a format string directives in queue_seqset()
Coverity pointed out that a format string used inappropriate
format string directives for variables of type size_t.

Change-Id: I889019aad963932fdc032421e60a72c809a93bca
Related: CID#135197
2018-11-22 13:17:18 +00:00
Stefan Sperling
cc8181fefe fix format string error in ippool_printaddr()
The variable this->listsize is an unsigned int, but the format
string assumed ptrdiff_t. Found by Coverity.

Change-Id: Ib2a55907adae98f8aa7b079f1c9a3b4fc5f67fc5
Related: CID#188879
2018-11-22 13:17:01 +00:00
Stefan Sperling
7327360d10 initialize local variable addr in ippool_new()
Coverity points out that addr.len was potentially being used
uninitialized, via calls to in46a_inc(&addr).

Change-Id: Idb67394e5f4c2072380a33f46c848d92c4317245
Related: CID#174189
2018-11-22 13:16:50 +00:00
Stefan Sperling
e405c2f196 replace bogus memcpy() call in ippool_newip()
When copying an address to a reused static hash table member
with memcpy(), this code mistakenly passed the size of a
pointer as the amount of bytes to be copied, rather than
the actual size of the address.

This means the IP pool could contain bogus IP addresses because
only addr->len (a uint8_t) and 3 further bytes of the address
were actually copied on 32 bit platforms. On 64 bit platforms,
a sufficient amount of bytes were copied for IPv4 to work
correctly, but too few bytes were copied for IPv6.

This problem was found by Coverity.

Replace the bogus memcpy() call with direct assignments to the
appropriate struct in64addr union members, and assert that the
length recorded for the address actually corresponds to the
length used by the address family (IP4, IPv6).

Change-Id: Ic21560f7519e776107485a8779702fb1279d065c
Related: CID#57921
2018-11-22 13:16:29 +00:00
Stefan Sperling
411ff3b984 fix allocation of ippool's hash table
The calloc() call in ippool_new() had two problems.

The first problem is benign: The order of arguments were reversed.
Pass the number of elements in the array first, then the size of
each element, as calloc() expects.
This problem was found by me. There are more instances of this
problem in this file, which I'll address in follow-up patches.

The second problem is that the requested allocation was larger than
necessary: The hash table is an array of pointers to ippoolm_t, not
an array of struct ippoolm_t. Fix the required size passed to calloc().
This problem was found by Coverity.

Change-Id: I93fa5bc539771ca19714f6a665558c9140e2ce07
Related: CID#57920
2018-11-22 07:00:54 +00:00
Stefan Sperling
aee905b790 check ioctl() call return value in tun_new()
Coverity complains about a missing ioctl() return value check.
Check for failure of the TUNSETNOCSUM ioctl and log a warning
if it fails.

Change-Id: I88da2164d975d7a232619b8d31c5eadeef0f3a80
Related: CID#57661
2018-11-21 14:14:10 +01:00
Harald Welte
fb75adfeda ippool.c: Use "%td" format string for ptrdiff_t
Change-Id: Iacafa0919baebac6b5a799deb41a673c022c6743
Fixes: Coverity CID#135225
2018-10-21 13:30:07 +02:00
Harald Welte
7b9230acfe sgsnemu: Fix printing of tun device name
Change-Id: I6cd89b7b59a6c1d506cfbe9d3088cb844d133313
Fixes: Coverity CID#178638
2018-10-21 13:30:07 +02:00
Harald Welte
5662cb2152 osmo-ggsn.cfg: Ensure well-formed config file example
Change-Id: Ic7fd91745e7442eda741d46748c0a4a02dedef80
2018-09-25 18:52:02 +00:00
Alexander Couzens
e1412d9493 libgtp: implement gtp_clear_queues to clear req/resp queue
Clearing the request and response queue is useful for debugging
to reset "some" state. Otherwise some tests will get un-expected
packets.

Change-Id: I279d1d7cbf5d37dd5609c2b968f317fe9a0e348d
2018-09-16 10:30:10 +00:00
Pau Espin Pedrol
d1e2342f91 Install sample cfg file to /etc/osmocom
Change-Id: If41e69295ac23a61df138ceea83794059f111086
2018-09-12 18:37:08 +02:00
Pau Espin Pedrol
381b723543 Install systemd services with autotools
Change-Id: I563559f5b501eded44efafc60bb0c9ffdea20b3e
2018-09-10 16:10:05 +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
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
65 changed files with 4916 additions and 1554 deletions

11
.gitignore vendored
View File

@@ -69,3 +69,14 @@ tests/*/*_test
tests/testsuite
tests/testsuite.log
tests/package.m4
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build

View File

@@ -1,5 +1,5 @@
## Process this file with automake to produce Makefile.in
SUBDIRS = lib gtp ggsn sgsnemu doc tests
SUBDIRS = lib gtp ggsn sgsnemu doc contrib tests
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgtp.pc
@@ -12,4 +12,7 @@ dist-hook:
EXTRA_DIST = git-version-gen .version README.md README.FreeBSD README.MacOSX
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
@RELMAKE@

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)
@@ -39,9 +38,9 @@ AC_SUBST(EXEC_LDFLAGS)
case "${host}" in
i*86-*-linux-gnu*)
i*86-*-linux-gnu*)
EXEC_LDADD="" ;;
*solaris*)
*solaris*)
EXEC_LDADD="-lresolv -lsocket -lnsl" ;;
esac
@@ -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"])
@@ -127,7 +126,7 @@ AC_EGREP_HEADER(struct iphdr, netinet/ip.h,
# Checks for library functions.
AC_PROG_GCC_TRADITIONAL
# AC_FUNC_MALLOC
# AC_FUNC_MEMCMP
# AC_FUNC_MEMCMP
AC_CHECK_FUNCS([gethostbyname inet_ntoa memset select socket strdup strerror strtol])
AC_CHECK_FUNCS(inet_aton inet_addr, break)
@@ -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,85 @@ 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
# Generate manuals
AC_ARG_ENABLE(manuals,
[AS_HELP_STRING(
[--enable-manuals],
[Generate manual PDFs [default=no]],
)],
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
if test x"$osmo_ac_build_manuals" = x"yes"
then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
else
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
else
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
fi
fi
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
fi
# Find and run check-depends
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
if ! test -x "$CHECK_DEPENDS"; then
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
fi
if ! $CHECK_DEPENDS; then
AC_MSG_ERROR("missing dependencies for --enable-manuals")
fi
# Put in Makefile with absolute path
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_CONFIG_FILES([Makefile
doc/Makefile
doc/examples/Makefile
@@ -161,6 +239,9 @@ AC_CONFIG_FILES([Makefile
intl/Makefile
po/Makefile
sgsnemu/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
tests/Makefile
tests/lib/Makefile
tests/gtp/Makefile

1
contrib/Makefile.am Normal file
View File

@@ -0,0 +1 @@
SUBDIRS = systemd

View File

@@ -1,5 +1,11 @@
#!/usr/bin/env bash
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * GTP: configure GTP tunneling Linux kernel (values: "--enable-gtp-linux" or "--disable-gtp-linux")
# * WITH_MANUALS: build manual PDFs if set to "1"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
#
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
@@ -18,12 +24,23 @@ 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]")
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
# Additional configure options and depends
CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals"
fi
set +x
echo
@@ -35,8 +52,12 @@ set -x
cd "$base"
autoreconf --install --force
./configure CFLAGS="-Werror" CPPFLAGS="-Werror" $GTP
./configure --enable-sanitize --enable-werror $GTP $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE distcheck
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
osmo-clean-workspace.sh

View File

@@ -0,0 +1,6 @@
EXTRA_DIST = osmo-ggsn.service
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-ggsn.service
endif

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

185
debian/changelog vendored
View File

@@ -1,3 +1,188 @@
osmo-ggsn (1.3.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* ggsn: ctrl iface: listen on IP configured by VTY
* gtp: Log type name of unexpected signalling message
* gtp: Allow recv DEL CTX REQ in sgsn and DEL CTX RSP in ggsn
* gtp: Log ignore CTX DEL REQ due to no teardown and only 1 ctx active
* gtp: Add new API to avoid freeing pdp contexts during DEL CTX REQ
* gtp: Add new replacement cb_recovery2 for cb_recovery
* Install systemd services with autotools
* Install sample cfg file to /etc/osmocom
[ Stefan Sperling ]
* fix unaligned access in build_ipcp_pco()
* fix support for multiple IPCP in PDP protocol configuration options
* check ioctl() call return value in tun_new()
* fix allocation of ippool's hash table
* replace bogus memcpy() call in ippool_newip()
* initialize local variable addr in ippool_new()
* fix format string error in ippool_printaddr()
* fix a format string directives in queue_seqset()
* properly store IPv6 addresses in struct tun_t
[ Harald Welte ]
* debian/rules: Don't overwrite .tarball-version
* osmo-ggsn.cfg: Ensure well-formed config file example
* sgsnemu: Fix printing of tun device name
* ippool.c: Use "%td" format string for ptrdiff_t
* initial version of OsmoGGSN user manual
* OsmoGGSN: Add VTY reference manual
* GGSN: Document how 'ip tuntap' is used for non-root; call netdev 'apn0'
* vty-ref: Update URI of docbook 5.0 schema
[ Alexander Couzens ]
* libgtp: implement gtp_clear_queues to clear req/resp queue
[ Neels Hofmeyr ]
* Importing history from osmo-gsm-manuals.git
* refactor Makefile build rules, don't use the FORCE
* GGSN: don't say 'NITB'
* OsmoGGSN: more info on non-root operation / tun creation
* OsmoGGSN: multiple instances: mention GTP port
* OsmoGGSN: add Routing section for IP forward and masquerading
* OsmoGGSN: typo: priveleges
* OsmoGGSN VTY ref: prep: convert newlines to unix
* OsmoGGSN vty: update VTY reference
* OsmoGGSN: fix VTY additions' node IDs
* OsmoGGSN: update vty reference
* ggsn: update vty reference
[ Max ]
* Expand OsmoGGSN manual
[ Oliver Smith ]
* build manuals moved here from osmo-gsm-manuals.git
* Fix DISTCHECK_CONFIGURE_FLAGS override
* contrib/jenkins.sh: build and publish manuals
* contrib: fix makedistcheck with disabled systemd
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 21:34:22 +0100
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: libgtp4
Architecture: any
Multi-Arch: same
Section: libs
@@ -41,7 +41,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
libgtp2 (= ${binary:Version})
libgtp4 (= ${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}, libgtp4 (= ${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}, libgtp4 (= ${binary:Version})
Multi-Arch: same
Description: Debug symbols for OsmoGGSN
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile

View File

@@ -1,3 +1,5 @@
/etc/osmocom/osmo-ggsn.cfg
/lib/systemd/system/osmo-ggsn.service
/usr/bin/osmo-ggsn
/usr/bin/sgsnemu
/usr/share/man/man8/*

View File

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

7
debian/rules vendored
View File

@@ -16,8 +16,7 @@ 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
dh_strip -plibgtp4 --dbg-package=libgtp-dbg
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf
override_dh_auto_configure:
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system

View File

@@ -4,4 +4,5 @@ EXTRA_DIST = $(man_MANS)
SUBDIRS = \
examples \
manuals \
$(NULL)

View File

@@ -1,3 +1,8 @@
osmoconfdir = $(sysconfdir)/osmocom
osmoconf_DATA = osmo-ggsn.cfg
EXTRA_DIST = osmo-ggsn.cfg
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:

View File

@@ -3,32 +3,32 @@
!!
!
log stderr
logging filter all 1
logging color 1
logging print category 0
logging timestamp 0
logging level ip info
logging level tun info
logging level ggsn info
logging level sgsn notice
logging level icmp6 notice
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
logging level lctrl notice
logging level lgtp info
logging level lstats notice
logging level lgsup notice
logging level loap notice
logging level lss7 notice
logging level lsccp notice
logging level lsua notice
logging level lm3ua notice
logging level lmgcp notice
logging filter all 1
logging color 1
logging print category 0
logging timestamp 0
logging level ip info
logging level tun info
logging level ggsn info
logging level sgsn notice
logging level icmp6 notice
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
logging level lctrl notice
logging level lgtp info
logging level lstats notice
logging level lgsup notice
logging level loap notice
logging level lss7 notice
logging level lsccp notice
logging level lsua notice
logging level lm3ua notice
logging level lmgcp notice
!
stats interval 5
!

16
doc/manuals/Makefile.am Normal file
View File

@@ -0,0 +1,16 @@
EXTRA_DIST = osmoggsn-usermanual.adoc \
osmoggsn-usermanual-docinfo.xml \
osmoggsn-vty-reference.xml \
chapters \
vty
if BUILD_MANUALS
ASCIIDOC = osmoggsn-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
VTY_REFERENCE = osmoggsn-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -0,0 +1,335 @@
== Configuring OsmoGGSN
All configuration of OsmoGGSN is performed using the VTY. For more
general information on the VTY interface, see <<vty>>.
=== Configuring a virtual GGSN instance
OsmoGGSN can run multiple GGSN instances inside one program/process.
Each GGSN instance binds to its own transport-layer GTP IP address and
has its own set of APNs and associated IP address pools + tun/gtp
devices.
In most usage cases, yo will only have a single GGSN instance inside
your configuration file, like in below example:
.Example: Single GGSN configuration section
----
ggsn ggsn0
gtp state-dir /tmp
gtp bind-ip 127.0.0.6
apn internet
gtpu-mode tun
tun-device tun4
type-support v4
ip prefix dynamic 176.16.222.0/24
ip dns 0 192.168.100.1
ip dns 1 8.8.8.8
ip ifconfig 176.16.222.0/24
no shutdown
----
==== Creating/Editing a GGSN instance
Creating/Editing a GGSN instance can be done by the following sequence
of VTY commands:
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Create or edit the GGSN instance `ggsn0`. The name can be any ASCII
string, its significance is only to the local user.
<4> Your prompt is now in the `ggsn` config node, where you can
configure the properties of this GGSN instance.
NOTE:: After creating a new GGSN instance, it is in `shutdown` mode. See
<<unshutdown_apn>> to take it out of shutdown, but make sure to configure it fully
before taking it out of shutdown.
==== Configuring a GGSN instance
The following two mandatory configuration statements have to be given
for every GGSN instance:
----
OsmoGGSN(config-ggsn)# gtp state-dir /var/lib/ggsn/ggsn0 <1>
OsmoGGSN(config-ggsn)# gtp bind-ip 127.0.0.6 <2>
----
<1> Store the GSN restart state in the specified directory
<2> Bind the GGSN instance to the specified local IPv4 address
There are some further configuration statements that can be used at the
GGSN node, some examples are given below. For a full list, see the
_OsmoGGSN VTY reference manual_ <<vty-ref-osmoggsn>>.
----
OsmoGGSN(config-ggsn)# default-apn foobar <1>
----
<1> Configure a default APN to be used if the user-requested APN is not
found. The named APN must previously be configured
==== Deleting a GGSN instance
A GGSN instance can be removed like this
.Example: Deleting a GGSN instance
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# no ggsn ggsn0 <3>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Delete the GGSN instance
==== Taking a GGSN instance out of shutdown
.Example: Taking a GGSN instance out of shutdown
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# no shutdown ggsn <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Take the GGSN instance out of shutdown
==== Shutting a GGSN instance down
If you would like to take a GGSN instance out of service, you can
put it into shutdown mode. This will make the entire GGSN unavailable
to user traffic and permit you to e.g. reconfigure it before taking it
out of shutdown again.
.Example: Shutting down a GGSN instance
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# shutdown ggsn <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Shut down the GGSN instance
=== Configuring an Access Point Name
An Access Point Name (APN) represents a connection to an external packet
data network, such as the public Internet or private corporate networsk.
APNs are selected by terminals (MS/UE) when establishing PDP contexts.
Each OsmoGGSN GGSN instance can have any number of APNs configured.
Each APN is identified by a string name.
==== Creating/Editing an APN
.Example: Creating a new APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config node of the GGSN instance `ggsn0`
<4> Create or Edit an APN called `internet`
<5> Your prompt is now in the `ggsn` config node, where you can
configure the properties of this GGSN instance.
NOTE:: The newly-create APN is created in `shutdown` mode. See <<unshutdown_apn>> to take it
out of shutdown.
==== Configuring an APN
.Example: Configuring an APN
----
OsmoGGSN(config-ggsn-apn)# gtpu-mode tun <1>
OsmoGGSN(config-ggsn-apn)# type-support v4 <2>
OsmoGGSN(config-ggsn-apn)# ip prefix dynamic 176.16.222.0/24 <3>
OsmoGGSN(config-ggsn-apn)# ip dns 0 192.168.100.1 <4>
OsmoGGSN(config-ggsn-apn)# ip dns 1 8.8.8.8 <5>
OsmoGGSN(config-ggsn-apn)# ip ifconfig 176.16.222.0/24 <6>
----
<1> Use the userspace GTP-U handling using a TUN device
<2> Support (only) IPv4 Addresses
<3> Specify the pool of dynamic IPv4 addresses to be allocated to PDP
contexts
<4> Specify the primary DNS server to be provided using IPCP/PCO
<5> Specify the secondary DNS server to be provided using IPCP/PCO
<6> Request OsmoGGSN to configure the `tun4` device network/netmask
NOTE:: If you use the optional `ip ifconfig` command to set the network
device address/mask, OsmoGGSN must run with root or `CAP_NET_ADMIN`
support. It might be better to configure related tun devices at system
startup and run OsmoGGSN as non-privileged user. See <<ggsn_no_root>> for more
details.
==== Deleting an APN
An APN configuration can be removed like this
.Example: Deleting an APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# no apn internet <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config node of the GGSN instance `ggsn0`
<4> Delete the APN `internet`
[[unshutdown_apn]]
==== Taking an APN out of shutdown
In order to bring a deactived APN in `shutdown` state into active
operation, use the `no shutdown` command at the APN node as explained in
the following example:
.Example: Taking an APN out of shutdown
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# no shutdown <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Enter the config ndoe of the APN `internet`
<5> Take the APN out of shutdown
==== Shutting an APN down
If you would like to take an APN instance out of service, you can
put it into shutdown mode. This will make the APN unavailable
to user traffic and permit you to e.g. reconfigure it before taking it
out of shutdown again.
.Example: Shutting down an APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# shutdown <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Enter the config ndoe of the APN `internet`
<5> Shut down the APN
[[ggsn_no_root]]
=== Configuring for running without root privileges
It's possible to run OsmoGGSN without root privileges if the tun devices are already configured.
The interface creation + configuration must then happen before osmo-ggsn starting up. This can be
achieved by means such as
* a custom shell script run as root before starting osmo-ggsn (e.g. as init script)
* systemd .netdev and .network files, if your system is using systemd-networkd (see `networkctl status`).
==== Manual TUN device creation / configuration
If you chose to go for custom shell/init scripts, you may use the `ip` program which is the standard
tool for network interface configuration on Linux, part of the `iproute2` package. In order to
create a tun device, you must call it like this:
.Example: iproute2 command to create a tun device
----
# ip tuntap add dev apn0 mode tun user username group groupname
----
Where _username_ and _groupname_ correspond to the User and Group that will have ownership over the
device, i.e. the privileges which you intend to run osmo-ggsn under, and _apn0_ will be the
name of the network device created. After creating the interface, you can configure its addresses
using standard means like `ip addr add` or your distribution-specific utilities/tools
to match the `ip prefix dynamic` config item, and activate the link, for example:
----
# ip addr add 192.168.7.0/24 dev apn0
# ip link set apn0 up
----
==== systemd based TUN device creation+configuration
If you want to have systemd take care of creating and configuring a tun device for you,
you can use the below example config files.
.Example: device config via systemd-networkd using apn0.netdev
----
[NetDev]
Name=apn0 <1>
Kind=tun
[Tun]
User=username <2>
Group=username <3>
----
<1> The network interface name of the newly-created device
<2> The username under which you will run OsmoGGSN
<3> The group name under which you will run OsmoGGSN
.Example: network settings via systemd-networkd using ggsn.network
----
[Match]
Name=apn0 <1>
[Network]
Address=192.168.7.1 <2>
IPMasquerade=yes <3>
----
<1> The netowrk device name, which must match the one in the apn0.netdev unit file above
<2> The local IP address configured on the device
<3> Requesting systemd to configure IP masquerading for this interface. Depending on your needs,
You may not want this if you have proper end-to-end routing set up, and want to have transparent
inbound IP access to your GPRS-attached devices.
==== Config Changes
With the tun device pre-configured in one of the ways outlined above, the main
changes in your osmo-ggsn.cfg file are:
* remove `ip ifconfig` directive,
* make sure that `no shutdown` is present in the `apn` section as well as
`no shutdown ggsn` in the `ggsn` section.
.Example: using externally configured tun device `apn0` as non-root
----
ggsn ggsn0
gtp state-dir /tmp
gtp bind-ip 127.0.0.6
apn internet
gtpu-mode tun
tun-device apn0
type-support v4
ip prefix dynamic 192.168.7.0/24
ip dns 0 192.168.100.1
ip dns 1 8.8.8.8
no shutdown
default-apn internet
no shutdown ggsn
----

View File

@@ -0,0 +1,145 @@
[[chapter_introduction]]
== Overview
[[intro_overview]]
=== About OsmoGGSN
OsmoGGSN is a Free / Open Source Software implementation of the GPRS
GGSN (Gateway GPRS support node) element in side the packet switched
core network of 2G and 3G cellular networks.
The GGSN function is the tunnel endpoint on the core network side,
from where the external (IP) packet data network
=== Software Components
==== GTP Implementation (libgtp)
The OsmoGGSN source code includes a shared library implementation of
the GTP protocol used on the GGSN-SGSN interface. This library
and associated header files are installed system-wide and are
available to other programs/applications.
In fact, libgtp is what the OsmoSGSN also uses for its use of GTP.
==== sgsnemu
In order to test OsmoGGSN without running a SGSN and other elements
of a cellular network, there is a small command-line utility called
*sgsnemu* which is able to simulate the customary operations of a SGSN
towards the GGSN, such as a PDP Context Activation.
*sgsnemu* can even be used for testing against other GGSNs, as the GTP
protocol is standardized across implementations.
==== osmo-ggsn
*osmo-ggsn* is the actual name of the OsmoGGSN executable program. It
implements the GGSN functionality. All parameters are set using the
configuration file, by default located in *./osmo-ggsn.cfg*
==== systemd service file
In *contrib/osmo-ggsn.service* you can find a sample service file for
OsmoGGSN which can be used with systemd.
==== init script
In *contrib/osmo-ggsn.init* you can find a sample init script to be used
on systems with classic init process.
=== Limitations
OsmoGGSN supports both GTP0 (GSM 09.60) and GTP1 (3GPP 29.060). In the
following tables the support of each individual message type is
detailed. The numbers before each feature indicates the relevant
section in the standard.
==== GSM 09.60 (GTPv0)
[options="header",cols="50%,15%,15%,15%,5%"]
|===
| Feature | gtplib | osmo-ggsn | sgsnemu | notes
5+<|*7.4 Path Management Messages*
|7.4.1 Echo Request |Supported |Supported |Supported |
|7.4.2 Echo Response |Supported |Supported |Supported |
|7.4.3 Version Not Supported |Supported |Supported |Supported |
5+<| *7.5 Tunnel Management Messages*
|7.5.1 Create PDP Context Request|Supported |Supported |Supported |
|7.5.2 Create PDP Context Response|Supported |Supported |Supported |
|7.5.3 Update PDP Context Request|Supported |Supported |Not |
|7.5.4 Update PDP Context Response|Supported |Supported |Not |
|7.5.5 Delete PDP Context Request|Supported |Supported |Supported |
|7.5.6 Delete PDP Context Response|Supported |Supported |Supported |
|7.5.7 Create AA PDP Context Request|Unsupported |Unsupported |Unsupported |
|7.5.8 Create AA PDP Response|Unsupported |Unsupported |Unsupported |
|7.5.9 Delete AA PDP Context Request|Unsupported |Unsupported |Unsupported |
|7.5.10 Delete AA PDP Context Response|Unsupported |Unsupported |Unsupported |
|7.5.11 Error Indication |Supported |Supported |Supported |
|7.5.12 PDU Notification Request|Unsupported |Unsupported |Unsupported |
|7.5.13 PDU Notification Response|Unsupported |Unsupported |Unsupported |
|7.5.14 PDU Notification Reject Request|Unsupported |Unsupported |Unsupported |
|7.5.15 PDU Notification Reject Response|Unsupported |Unsupported |Unsupported |
5+<| *7.6 Location Management Messages*
|7.6.1 Send Routeing Information for GPRS Request|Unsupported |Unsupported |Not applicable |
|7.6.2 Send Routeing Information for GPRS Response|Unsupported |Unsupported |Not applicable |
|7.6.3 Failure Report Request|Unsupported |Unsupported |Not applicable |
|7.6.3 Failure Report Response|Unsupported |Unsupported |Not applicable |
|7.6.5 Note MS GPRS Present Request|Unsupported |Unsupported |Not applicable|
|7.6.6 Note MS GPRS Present Response|Unsupported |Unsupported |Not applicable|
5+<| *7.5 Mobility Management Messages*
|7.5.1 Identification Request|Unsupported |Not applicable|Not applicable|
|7.5.2 Identification Response|Unsupported |Not applicable|Not applicable |
|7.5.3 SGSN Context Request|Unsupported |Not applicable|Not applicable|
|7.5.4 SGSN Context Response|Unsupported |Not applicable|Not applicable|
|7.5.5 SGSN Context Acknowledge|Unsupported |Not applicable|Not applicable|
|===
==== 3GPP 29.060 (GTPv1)
[options="header",cols="50%,15%,15%,15%,5%"]
|===
|Feature |gtplib |osmo-ggsn |sgsnemu |notes
5+<|*7.2 Path Management Messages*
|7.2.1 Echo Request |Supported |Supported |Supported |
|7.2.2 Echo Response |Supported |Supported |Supported |
|7.2.3 Version Not Supported|Supported |Supported |Supported |
|7.2.4 Extension Headers Notification|Supported |Supported |Supported |
5+<|*7.3 Tunnel Management Messages*
|7.3.1 Create PDP Context Request|Supported |Supported |Supported |1
|7.3.2 Create PDP Context Response|Supported |Supported |Supported |
|7.3.3 Update PDP Context Request|Supported |Supported |Not applicable|1
|7.3.4 Update PDP Context Response|Supported |Supported |Not applicable|
|7.3.5 Delete PDP Context Request|Supported |Supported |Supported |
|7.3.6 Delete PDP Context Response|Supported |Supported |Supported |
|7.3.7 Error Indication |Supported |Supported |Supported |
|7.3.8 PDU Notification Request|Unsupported |Unsupported |Unsupported |
|7.3.9 PDU Notification Response|Unsupported |Unsupported |Unsupported |
|7.3.10 PDU Notification Reject Request|Unsupported |Unsupported |Unsupported |
|7.3.10 PDU Notification Reject Response|Unsupported |Unsupported |Unsupported |
5+<|*7.4 Location Management Messages*
|7.4.1 Send Routeing Information for GPRS Request|Unsupported |Unsupported |Not applicable |
|7.4.2 Send Routeing Information for GPRS Response|Unsupported |Unsupported |Not applicable |
|7.4.3 Failure Report Request|Unsupported |Unsupported |Not applicable|
|7.4.3 Failure Report Response|Unsupported |Unsupported |Not applicable|
|7.4.5 Note MS GPRS Present Request|Unsupported |Unsupported |Not applicable|
|7.4.6 Note MS GPRS Present Response|Unsupported |Unsupported |Not applicable|
5+<|*7.5 Mobility Management Messages*
|7.5.1 Identification Request|Unsupported |Not applicable|Not applicable|
|7.5.2 Identification Response|Unsupported |Not applicable |Not applicable|
|7.5.3 SGSN Context Request|Unsupported |Not applicable|Not applicable|
|7.5.4 SGSN Context Response|Unsupported |Not applicable |Not applicable|
|7.5.5 SGSN Context Acknowledge|Unsupported |Not applicable|Not applicable|
|7.5.6 Forward Relocation Request|Unsupported |Not applicable|Not applicable|
|7.5.7 Forward Relocation Response|Unsupported |Not applicable|Not applicable|
|7.5.8 Forward Relocation Complete|Unsupported |Not applicable|Not applicable|
|7.5.9 Relocation Cancel Request|Unsupported |Not applicable|Not applicable|
|7.5.10 Relocation Cancel Response|Unsupported |Not applicable|Not applicable|
|7.5.11 Forward Relocation Complete |Unsupported |Not applicable |Not applicable |
|7.5.12 Forward SRNS Context Acknowledge|Unsupported |Not applicable|Not applicable|
|7.5.13 Forward SRNS Context|Unsupported |Not applicable|Not applicable|
|===
Notes
1) The "Secondary PDP Context Activation Procedure" is not supported.

View File

@@ -0,0 +1,82 @@
== Running OsmoGGSN
The OsmoGGSN executable (`osmo-ggsn`) offers the following command-line
arguments:
=== SYNOPSIS
*osmo-ggsn* [-h|-V] [-D] [-c 'CONFIGFILE']
=== OPTIONS
*-h, --help*::
Print a short help message about the supported options
*-V, --version*::
Print the compile-time version number of the OsmoBTS program
*-D, --daemonize*::
Fork the process as a daemon into background.
*-c, --config-file 'CONFIGFILE'*::
Specify the file and path name of the configuration file to be
used. If none is specified, use `osmo-ggsn.cfg` in the current
working directory.
=== Routing
Operating the OpenGGSN tun device naturally creates a network setup with
multiple interfaces. Consider:
* Typical Linux setups prevent forwarding of packets between separate
interfaces by default. To let subscribers reach the internet uplink from the
tun device, it may be required to enable IP forwarding.
* Having a locally defined address range assigned to the tun device requires
either sensible routing for this address range, or that masquerading is
enabled to allow your single uplink IP address to "proxy" for the tun.
These are decisions to be made on a network administration level.
In a trivial case where you have a single box serving GPRS to few subscribers
on an arbitrary IP address range not known in the larger network, the easiest
way to enable GPRS uplink would be to enable IP forwarding and masquerading.
To manually enable IPv4 forwarding and masquerading ad-hoc, you can do:
----
sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
iptables -t nat -A POSTROUTING -o '*' -j MASQUERADE
----
(You may want to replace `*` with the network device name, like `-o eth0`)
There are various ways to enable these settings persistently, please refer to
your distribution's documentation -- e.g. look for @net.ipv4.ip_forward=1@ in
@/etc/sysctl.d/@, and https://wiki.debian.org/iptables for masquerading.
=== Multiple instances
Running multiple instances of `osmo-ggsn` is possible if all GGSN instances
are binding to different local IP addresse and all other interfaces (VTY,
OML) are separated using the appropriate configuration options. The IP based
interfaces are binding to local host by default. In order to separate the
processes, the user has to bind those services to specific but different
IP addresses.
The VTY and the control interface can be bound to IP addresses from the loopback
address range.
.Example: Binding VTY and control interface to a specific ip-address
----
line vty
bind 127.0.0.2
ctrl
bind 127.0.0.2
----
Also make sure to place each instance's GTP bind on a separate IP address (GTP
uses a port number that is fixed in the GTP specifications, so it will not be
possible to pick differing ports on the same IP address), like:
----
ggsn ggsn0
gtp bind-ip 127.0.0.2
----

View File

@@ -0,0 +1,46 @@
<revhistory>
<revision>
<revnumber>1</revnumber>
<date>August 2017</date>
<authorinitials>HW</authorinitials>
<revremark>
Initial version.
</revremark>
</revision>
</revhistory>
<authorgroup>
<author>
<firstname>Harald</firstname>
<surname>Welte</surname>
<email>hwelte@sysmocom.de</email>
<authorinitials>HW</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Managing Director</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2013-2017</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -0,0 +1,29 @@
OsmoGGSN User Manual
====================
Harald Welte <hwelte@sysmocom.de>
include::./common/chapters/preface.adoc[]
include::{srcdir}/chapters/overview.adoc[]
include::{srcdir}/chapters/running.adoc[]
//include::{srcdir}/chapters/control.adoc[]
include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/configuration.adoc[]
include::./common/chapters/control_if.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
ex:ts=2:sw=42sts=2:et
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
]>
<book>
<info>
<revhistory>
<revision>
<revnumber>v1</revnumber>
<date>06th September 2017</date>
<authorinitials>hw</authorinitials>
<revremark>Initial version as of OsmoGGSN v1.0.0</revremark>
</revision>
</revhistory>
<title>OsmoGGSN VTY Reference</title>
<copyright>
<year>2017</year>
</copyright>
<legalnotice>
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
</para>
</legalnotice>
</info>
<!-- Main chapters-->
&chapter-vty;
</book>

View File

@@ -0,0 +1,30 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
<node id='config-line'>
<child_of nodeid='config' />
<name>Telnet/VTY Configuration Node</name>
<description>
Configure parameters of the Telnet/VTY Interface, such as to which IP address it should bind/listen to.
</description>
</node>
<node id='config-ctrl'>
<child_of nodeid='config' />
<name>CTRL Configuration Node</name>
<description>
Configure parameters of the CTRL Interface, such as to which IP address it should bind/listen to.
</description>
</node>
<node id='config-ggsn'>
<child_of nodeid='config' />
<name>GGSN Instance Configuration Node</name>
<description>
Configure an Instance of a (virtual) GGSN
</description>
</node>
<node id='config-ggsn-apn'>
<child_of nodeid='config-ggsn' />
<name>APN Configuration Node</name>
<description>
Configure an Access Point Name (APN) inside a GGSN Instance
</description>
</node>
</vtydoc>

File diff suppressed because it is too large Load Diff

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=4: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)

399
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)
{
@@ -428,11 +496,29 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
return 0;
}
/**
* @brief clear the request and response queue. Useful for debugging to reset "some" state.
* @param gsn The GGSN instance
*/
void gtp_clear_queues(struct gsn_t *gsn)
{
struct qmsg_t *qmsg;
while (!queue_getfirst(gsn->queue_req, &qmsg)) {
queue_freemsg(gsn->queue_req, qmsg);
}
while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
queue_freemsg(gsn->queue_resp, qmsg);
}
}
/* gtp_conf
* 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 +609,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 +667,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 +712,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 +938,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 +1018,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 +1027,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 +1056,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 +1077,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 +1092,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 +1100,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 +1178,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 +1350,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 +1452,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 +1624,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 +1654,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 +1678,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 +1744,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 +1945,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 +1983,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 +2024,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 +2045,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 +2153,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 +2218,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 +2272,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 +2313,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 +2382,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 +2478,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 +2506,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 +2617,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 +2637,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 +2672,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 +2682,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 +2716,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 +2772,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 +2791,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 +2807,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 +2828,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 +2840,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 +2876,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 +2910,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 +2987,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 +3023,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 +3039,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 +3087,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 +3164,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 +3201,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 +3216,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 +3375,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,8 +362,15 @@ 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));
void gtp_clear_queues(struct gsn_t *gsn);
/* Internal functions (not part of the API */
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,

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

@@ -79,7 +79,7 @@ static int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
if (QUEUE_DEBUG)
printf("Begin queue_seqset seq = %d\n", (int)seq);
if (QUEUE_DEBUG)
printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer),
printf("SIZEOF PEER %zu, *PEER %zu\n", sizeof(peer),
sizeof(*peer));
qmsg->seq = seq;

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

View File

@@ -26,16 +26,16 @@ int ippool_printaddr(struct ippool_t *this)
{
unsigned int n;
printf("ippool_printaddr\n");
printf("Firstdyn %d\n", this->firstdyn - this->member);
printf("Lastdyn %d\n", this->lastdyn - this->member);
printf("Firststat %d\n", this->firststat - this->member);
printf("Laststat %d\n", this->laststat - this->member);
printf("Listsize %d\n", this->listsize);
printf("Firstdyn %td\n", this->firstdyn - this->member);
printf("Lastdyn %td\n", this->lastdyn - this->member);
printf("Firststat %td\n", this->firststat - this->member);
printf("Laststat %td\n", this->laststat - this->member);
printf("Listsize %u\n", this->listsize);
for (n = 0; n < this->listsize; n++) {
char s[256];
in46a_ntop(&this->member[n].addr, s, sizeof(s));
printf("Unit %d inuse %d prev %d next %d addr %s\n",
printf("Unit %d inuse %d prev %td next %td addr %s\n",
n,
this->member[n].inuse,
this->member[n].prev - this->member,
@@ -202,7 +202,7 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
/* Parse only first instance of pool for now */
int i;
struct in46_addr addr;
struct in46_addr addr = { 0 };
size_t addrprefixlen;
struct in46_addr stataddr;
size_t stataddrprefixlen;
@@ -276,9 +276,8 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
(*this)->hashmask = (*this)->hashsize - 1;
/* Allocate hash table */
if (!
((*this)->hash =
calloc(sizeof(struct ippoolm_t), (*this)->hashsize))) {
(*this)->hash = calloc((*this)->hashsize, sizeof(struct ippoolm_t *));
if (!(*this)->hash) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"Failed to allocate memory for hash members in ippool");
return -1;
@@ -513,7 +512,15 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
p2->next = NULL;
p2->prev = NULL;
p2->inuse = 2; /* Static address in use */
memcpy(&p2->addr, addr, sizeof(addr));
/* p2->addr.len and addr->len already match (see above). */
if (p2->addr.len == sizeof(struct in_addr))
p2->addr.v4 = addr->v4;
else if (p2->addr.len == sizeof(struct in6_addr))
p2->addr.v6 = addr->v6;
else {
SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
return -GTPCAUSE_UNKNOWN_PDP;
}
*member = p2;
(void)ippool_hashadd(this, *member);
if (0)

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

681
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,104 @@
#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) {
this->addr.len = sizeof(struct in_addr);
this->addr.v4.s_addr = addr->s_addr;
}
if (addr) { /* Set the interface address */
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) {
this->dstaddr.len = sizeof(struct in_addr);
this->dstaddr.v4.s_addr = dstaddr->s_addr;
}
if (dstaddr) { /* Set the destination address */
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? */
int rc;
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
if (rc < 0)
return rc;
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;
}
this->dstaddr.len = sizeof(*dstaddr);
memcpy(&this->dstaddr.v6, dstaddr, sizeof(*dstaddr));
}
#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);
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 +178,53 @@ 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;
/* Disable checksums */
if (ioctl((*tun)->fd, TUNSETNOCSUM, 1) < 0) {
SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", (*tun)->devname);
}
return 0;
} else {
strncpy((*tun)->devname, dev_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
(*tun)->fd = -1;
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 +276,17 @@ int tun_free(struct tun_t *tun)
{
if (tun->routes) {
tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
netdev_delroute(&tun->dstaddr.v4, &tun->addr.v4, &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);
@@ -727,7 +329,7 @@ int tun_runscript(struct tun_t *tun, char *script)
char smask[TUN_ADDRSIZE];
int rc;
strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
strncpy(snet, inet_ntoa(tun->addr.v4), sizeof(snet));
snet[sizeof(snet) - 1] = 0;
strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
smask[sizeof(smask) - 1] = 0;
@@ -745,79 +347,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
@@ -64,8 +31,8 @@ struct iphdr
struct tun_t {
int fd; /* File descriptor to tun interface */
struct in_addr addr;
struct in_addr dstaddr;
struct in46_addr addr;
struct in46_addr dstaddr;
struct in_addr netmask;
int addrs; /* Number of allocated IP addresses */
int routes; /* One if we allocated an automatic route */
@@ -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 */
@@ -291,7 +293,7 @@ static int process_options(int argc, char **argv)
printf("timelimit: %d\n", args_info.timelimit_arg);
printf("createif: %d\n", args_info.createif_flag);
if (args_info.tun_device_arg)
printf("tun-device: %d\n", args_info.tun_device_arg);
printf("tun-device: %s\n", args_info.tun_device_arg);
if (args_info.ipup_arg)
printf("ipup: %s\n", args_info.ipup_arg);
if (args_info.ipdown_arg)
@@ -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