85 Commits

Author SHA1 Message Date
Neels Hofmeyr
07c95ce770 gtplab0 2024-09-21 07:13:08 +02:00
Neels Hofmeyr
a01dd18276 gtplab0 2024-09-16 23:42:15 +02:00
Neels Janosch Hofmeyr
570cfc9a43 udp flood test
Change-Id: Ia19ac41c9924ba4d44b3e087e17ede83a7c548bf
2024-09-16 23:37:50 +02:00
Neels Hofmeyr
4dbf2e7189 gtplab2 2024-09-14 06:20:48 +02:00
Neels Hofmeyr
85dd1f64a6 gtplab1 2024-09-14 06:20:21 +02:00
Neels Hofmeyr
ac52bbbffb gtplab0 2024-09-14 06:19:35 +02:00
Neels Janosch Hofmeyr
a75e42f210 gtpflood tunmap
Change-Id: I5226be3cc42b912fbd912d24f6c9de5fc5030230
2024-09-14 02:19:44 +02:00
Neels Janosch Hofmeyr
429686528e gtpflood tunmap
Change-Id: I621d40a9bdcd00987a7af10cc1efd7b5561476d0
2024-09-14 02:10:30 +02:00
Neels Janosch Hofmeyr
6aa0a7c97b gtpflood tunmap
Change-Id: Id5771f00ff393471dd2ca84b3b4f35f0560d7b07
2024-09-14 01:32:42 +02:00
Neels Janosch Hofmeyr
53408d0437 gtpflood tunmap
Change-Id: Ifdc9ad2a7a1924bc27371028d036683e51b617f3
2024-09-14 00:57:47 +02:00
Neels Janosch Hofmeyr
1c3bc6b531 gtpflood tunmap
Change-Id: Id4f15a9faca0ae7c082fed84ddf8757363a105c2
2024-09-13 23:31:53 +02:00
Neels Hofmeyr
9cb575b920 gtplab1 2024-09-13 06:52:15 +02:00
Neels Hofmeyr
34427ab995 osmo-pfcp-tool log tweak: s/NOTICE/DEBUG 2024-09-13 06:50:06 +02:00
Neels Hofmeyr
6428a9bf78 gtplab0 2024-09-13 05:58:56 +02:00
Neels Hofmeyr
033a21b5ff udp-responder 2024-09-13 05:53:22 +02:00
Neels Hofmeyr
c60686199f gtplab2 2024-09-13 05:53:22 +02:00
Neels Hofmeyr
bb4b6c1855 gtplab0 2024-09-13 05:51:17 +02:00
Neels Hofmeyr
245cf0ceed gtplab1 2024-09-13 05:50:42 +02:00
Neels Janosch Hofmeyr
c73263f85f gtp_flood wip
Change-Id: Ia9cf685081c91206198065619a4a6b30ed3deb7e
2024-09-13 05:18:12 +02:00
Neels Janosch Hofmeyr
bb96fbb1c2 gtp_flood wip
Change-Id: Ida26774b570b86ce6d91fde66c301bde8fd59930
2024-09-13 04:16:57 +02:00
Neels Janosch Hofmeyr
f6e3f3023b overview
Change-Id: I3db3614a3d4ffcd835e82a104eb13494e0848930
2024-09-13 02:44:38 +02:00
Neels Hofmeyr
b2ab19b894 TEMP udp-responder: disable ctr 2024-09-12 04:44:28 +02:00
Neels Hofmeyr
f137c01491 gtpflood tunend: flooding tweaks 2024-09-12 04:44:28 +02:00
Neels Hofmeyr
7cbf627329 gtpflood tunend osmo-upf.cfg 2024-09-12 04:41:49 +02:00
Neels Hofmeyr
30ce4a47c1 gtpflood tunend eupf.cfg 2024-09-12 04:41:49 +02:00
Neels Hofmeyr
ad730f410f wip 2024-09-12 04:41:49 +02:00
Neels Hofmeyr
1d12b2c01a gtpflood tunend: flooding tweaks 2024-09-12 04:12:38 +02:00
Neels Hofmeyr
5b32f65a95 gtpflood tunend 2024-09-12 04:12:38 +02:00
Neels Janosch Hofmeyr
f324fa5512 osmo-pfcp-tool gtp-flood: rx GTP packets
Change-Id: I49c7c9de09121cbdde859242775d4d0afeb71c29
2024-09-12 04:10:01 +02:00
Neels Hofmeyr
b5e89d9f98 udp_responder fu 2024-09-12 04:02:35 +02:00
Neels Hofmeyr
f235ca2228 gtpflood tunend 2024-09-12 02:36:08 +02:00
Neels Janosch Hofmeyr
1418f3fb9a udp-responder: add rate counter for bytes per seconds display
Change-Id: Ic81bfe2a363545886a2123baf67a9acab3b3d207
2024-09-12 02:34:27 +02:00
Neels Janosch Hofmeyr
2bc25789c9 gtplab wip
Change-Id: I58278d6f1d7eb5fff9e1eaf301d546101759a491
2024-09-11 03:11:15 +02:00
Neels Janosch Hofmeyr
eddc387d37 udp_responder
Change-Id: I39418fab40b073cd0eedf370ad6f9e1fed8efffe
2024-09-11 03:11:15 +02:00
Neels Janosch Hofmeyr
7d5c405e12 osmo-pfcp-tool: add GTP flooding using io_uring
Freely copying from gtp-load-gen.c, implement GTP flooding in
osmo-pfcp-tool scripts.

Add dependency liburing. It can be disabled with
  configure --disable-uring
in which case the new 'gtp flood' command only logs an error.

This verbosely commented example script serves as a good explanation:
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty

Related: SYS#6590
Change-Id: I332aa0e2efd55f6e357cde4752a3d8b584db531b
2024-09-11 03:11:15 +02:00
Neels Janosch Hofmeyr
32772f3846 move pfcp_tool.h to include/osmocom/pfcptool/
Related: SYS#6590
Change-Id: If3e7cc4df3defd08df9e75965715a1be0388ed01
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
7a31d130ff pfcp_tool: add 'date'
Allow scripts to output timestamps to easily measure how long certain
actions took to complete. Related: measuring PFCP session management
performance bounds of osmo-upf.

Change-Id: I0486cc92ea298bb9926a0e5c26da17ba5970a72c
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
a7a3a012b4 pfcp-tool: n-sessions [4/4]: implement 'n <0-2147483647> session create'
Related: SYS#6590
Change-Id: I74a21cc31296ab89a2acda1da8ae9693c1992e66
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
ac46306cc5 pfcp-tool: n-sessions [3/4]: add poll arg to pfcp_tool_mainloop()
Allow calling in non-blocking mode, prepare for N sessions.

Related: SYS#6590
Change-Id: I20bb2803b28681face18ee665d8a1aad06d58091
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
fc8b905a7b pfcp-tool: n-sessions [2/4]: generalize function args
Change some VTY DEFUN into generally callable functions. Prepare for N
sessions commands.

Related: SYS#6590
Change-Id: I112206049e704b7adad7072b1f7953f7ee4f18ca
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
968666caab pfcp-tool: n-sessions [1/4]: add generators for TEID and UE IP
Add the ability to establish a large number of sessions automatically.
Useful for load testing.

Related: SYS#6590
Change-Id: Iec164a222782d382aefe8d0342f398ebba1eac05
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
2b3e46d3b1 pfcp-tool: always use specific PDR ids for access and core
Makes it easier to find the right one later.
This will be used to fetch the UPF chosen TEIDs from PFCP responses in
upcoming "n-sessions [4/4]" I74a21cc31296ab89a2acda1da8ae9693c1992e66.

Related: SYS#6590
Change-Id: Ic343494001c70a84f3402ce5749d08e729551b26
2024-09-10 05:46:28 +02:00
Neels Janosch Hofmeyr
6859de09d2 upf gtp-u echo: rx Echo Response messages
Support receiving of Echo Responses, so far showed an error log for
unsupported message type. Just log the message.

Prep for upcoming patch to allow sending Echo Requests from VTY.

Change-Id: Idad417746a1ea797e8fbfe04ca9c84923a6118fa
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
5ac599ce17 upf gtp-u echo: improve loging
Tweak GTPv1-U Echo logging to more consistently show logging like:

DGTP INFO apn11 [23] 127.0.0.11:2152: <- 127.0.0.12:2152: rx GTPv1-U Echo Request: seq_nr=123 recovery_count=131
DGTP INFO apn11 [23] 127.0.0.11:2152: -> 127.0.0.12:2152: tx GTPv1-U Echo Response: seq_nr=123 recovery_count=570

Change-Id: I3c7fe7c3eb1467ae34085da6bbf26a935a6c927b
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
33061164f3 contrib/pfcp-tool-scripts: adjust tunend_session_est.vty and upf cfg to match up
Change-Id: I3d6a26b45f084c281887bee541cf01b690c9d1de
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
62e0f7e135 drop unreachable statement
Change-Id: I0710fd0e38a5ce26ac532ab7150e285f171accfb
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
58a9c167b3 fix EXTRA_DIST for vty test scripts
Like e.g. in osmo-msc.git, osmo-bsc.git, use wildcard $(srcdir)/*.vty to
make sure we never forget to distribute committed VTY test scripts.

Change-Id: If4dca81ce287ce1b1af32057634af912a8b89665
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
7bd92c13aa fix msgb memleak on GTP echo response
Change-Id: I2bd2e9c57bf392074eed9628a69eb710d4e459a6
2024-08-16 02:20:44 +02:00
Neels Janosch Hofmeyr
3efa019656 pfcp-tool: fix extra newline in vty_out
Change-Id: Ib3bb0795238f251ea5b515fe639280be66542a01
2024-07-31 06:30:44 +02:00
Neels Janosch Hofmeyr
4738fc2014 vty doc fix
Change-Id: Ie1ba8c0a1fb6613b7f21d6889e3c6978288a51e4
2024-07-31 05:29:10 +02:00
Oliver Smith
afe7a51c8f debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I8994759df644d6edd8f937051b95690537b749be
2024-05-14 15:21:07 +02:00
Oliver Smith
7713d784c9 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I0cc8d753b26648efa7c9fb5798f7c4227c328547
2024-05-13 08:39:06 +00:00
Neels Janosch Hofmeyr
0d96ea1730 tunmap: always set GTP-U source port to 2152 when forwarding
We see GTP-U originating from ports other than 2152 in the field. When
osmo-upf forwards these, we want to forward from our GTP-U port 2152,
since that is the only port osmo-upf has bound for GTP-U (for echo).

According to 3GPP TS 29.060, the *destination* port for GTP-U shall be
2152 -- but the source port is apparently allowed to be different.

Before this patch, we would forward GTP-U like this:

  3.3.3.3:33333 -> (3.3.3.4:2152  UPF  2.2.2.2:33333) -> 1.1.1.1:2152
                                               ^^^^^

Instead we want to always send from UDP source port 2152:

  3.3.3.3:33333 -> (3.3.3.4:2152  UPF  2.2.2.2:2152) -> 1.1.1.1:2152
                                               ^^^^

This hasn't shown up before because so far all GTP-U peers we saw
consistently used source port 2152.

Related: SYS#6773
Change-Id: Idaf43f1c2b915846b50a8b97305f0229e34ad539
2024-05-08 17:09:52 +02:00
Oliver Smith
a21bcec358 contrib/systemd: AmbientCapabilities=CAP_NET_ADMIN
Set CAP_NET_ADMIN so osmo-upf can set up GTP tunends and tunmaps even if
running as user.

Fix for:
  Operation not permitted (you must be root)
  netlink: Error: cache initialization failed: Operation not permitted
  20240430095022378 DNFT ERROR error running nft ruleset: rc=-1 ruleset="add table inet osmo-upf { flags owner; };\n" (upf_nft.c:79)
  20240430095022378 DNFT ERROR Failed to create nft table "osmo-upf" (upf_nft.c:111)

Fixes: OS#6444
Change-Id: I17b21ad92837ad360d667248f3f002d44251891c
2024-04-30 09:55:13 +02:00
Oliver Smith
9395752540 .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Consistently use tabs through the file, instead of mixing tabs
  and spaces.
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories, for which it does not seem to be a good
  idea to make the postinst script less maintainable. Something similar
  can be achieved by using your own Osmocom config file in a different
  path with different permissions.

Related: OS#4107
Change-Id: Ib129217f6aff713d1d0e7aa831b4b54823e9bade
2024-04-26 15:08:47 +02:00
Max
0b83ceb20d .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: I6caa561eff63d6ee58b5c7b0f8c69bfd2d134eb3
2024-04-24 11:53:19 +02:00
Neels Janosch Hofmeyr
77806ea88b manual: explain IP forwarding
Change-Id: I7b54f9203c1a77efd43f90b9a1c0105bc5c3efde
2024-04-15 16:38:45 +02:00
Neels Janosch Hofmeyr
60cbef5885 manual: explain GTP Echo workaround for tunmap
Change-Id: Ic824fc876d1fad181254cb6894e51464c443b53c
2024-04-13 02:54:51 +02:00
Neels Janosch Hofmeyr
8ec1871914 manual: 'Running': tweak word, fix ws at line end
Change-Id: Id9a4d2d75f86a252df0da6e7e0ae5ab47e8a7bf9
2024-04-13 02:54:44 +02:00
Neels Janosch Hofmeyr
aa6eabf766 manual: 'Running': tweak, mention 'tunmap' and 'tunend'
Change-Id: I9760ca214933d0b05080a3e70807b0cd06380a27
2024-03-15 20:17:22 +00:00
Neels Janosch Hofmeyr
203ce0b34f manual: 'Running': flatten section depths a bit
I'd like to add more sub-levels in an upcoming commit, and the levels
are becoming too many. So let's get rid of one depth level in the
'Running osmo-upf' chapter.

Change-Id: I0bd43300aa4b45315ea58ab35c77da005d1a4fa4
2024-01-24 03:41:51 +01:00
Neels Janosch Hofmeyr
4f4163032f manual: fix typo in running.adoc
Change-Id: Ibb1b548f588b27b23af687e5c44d18e81bca7c87
2024-01-24 03:15:23 +01:00
Andreas Eversberg
166bba4532 Use uniform log format for default config files
Related: OS#6272
Change-Id: I5f364aa88a020dd8a0de501b3547ad077457c616
2023-12-05 09:07:30 +01:00
Oliver Smith
e6c65defac systemd: depend on networking-online.target
Related: SYS#6400
Change-Id: Idadcbbf55e976ae035cfac4b85ccd870e0f27b82
2023-05-26 14:10:49 +02:00
Vadim Yanitskiy
edb94b3f8a copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
Change-Id: I907147c79fc6c95b8c1b3277a0855f19b004ef6e
2023-05-18 18:47:17 +07:00
Oliver Smith
8b85851d22 debian: set compat level to 10
Related: OS#5958
Change-Id: Ib3d33f323b0c3004911ec026612934c12c1162aa
2023-04-25 16:48:34 +02:00
Vadim Yanitskiy
1d422d6283 contrib/jenkins.sh: clone libnftnl and libnftables via git://
From time to time we see sporadic master build failures on Jenkins
because git fails to clone one of the repositories:

```
Cloning into 'nftables'...
error: garbage at end of loose object '0ca03ecd6ab3cfdc94f8f9ef6e3a7c40d1aa7195'
fatal: loose object 0ca03ecd6ab3cfdc94f8f9ef6e3a7c40d1aa7195
(stored in /build/libnftnl/nftables/.git/objects/0c/a03ecd6ab3cfdc94f8f9ef6e3a7c40d1aa7195) is corrupt

Cloning into 'libnftnl'...
fatal: unable to access 'https://git.netfilter.org/libnftnl/':
Failed to connect to git.netfilter.org port 443: Connection timed out
```

Running git with GIT_CURL_VERBOSE=true reveals that the server is using
an old "dumb" git protocol, so the client is sending hundreds of HTTP
requests to the server.  I also noticed that cloning via http[s]://
takes significantly more time than cloning via git://, because of the
old protocol being used.

```
$ time git clone https://git.netfilter.org/nftables
...
real    1m16.848s
user    0m4.867s
sys     0m1.883s

$ time git clone git://git.netfilter.org/nftables
...
real    0m2.453s
user    0m1.180s
sys     0m0.158s
```

According to [1], there is a more modern "smart" protocol, which is
relatively more stable and fast.  However it's not supported by the
remote server, so let's use git:// as a workaround.

[1] https://www.git-scm.com/docs/http-protocol

Change-Id: I8e943c74052cc74eae8dc1d80ab243f792a90156
2023-04-09 18:10:12 +07:00
Neels Janosch Hofmeyr
6a2763cfdf unique_ids_test.c: fix coverity ASSERT_SIDE_EFFECT
Do the assignment separately, outside of the assert().

Related: CID#311450
Change-Id: I4490a62f444d5048779c9b184b5f580cecd4c149
2023-03-22 19:04:28 +01:00
arehbein
5db469aa7e up_session: Silence coverity warning
Coverity complains about a supposedly missing NULL check for the pointer 'pdr', that check
however happens before the function in question is called.

It makes sense to be consistent inside the function, so remove the NULL check before calling
'pdr_del', because we don't NULL check anywhere else in the function either.

Fixes: Coverity scan CID#307494
Change-Id: Ia33e4211b4a24abc87c3c2ceffe807ca3322f29d
2023-03-20 15:17:18 +00:00
arehbein
21dae5cd35 osmo-pfcp-tool: Fix call to strerror
Fixes: Coverity scan CID#307499
Change-Id: I91a4116e7cf3721771cea3af82328cf02f1cfc14
2023-03-20 15:15:52 +00:00
Neels Janosch Hofmeyr
5bd84491b8 cosmetic: clarify session active / partially active semantics
Change-Id: I2db85b3ffd61cbf8fb404b17ee3b6593d1d189c4
2023-03-18 02:11:28 +00:00
Neels Janosch Hofmeyr
40a30fce4a tunmap: ensure assigned chain_id is unused
When handing out a chain_id, make sure it is not in use yet.

So far picking a chain_id was of PoC grade quality. As osmo-upf is
approaching production grade, make this waterproof.

So far with inefficient iteration of all sessions; faster lookup follows
in I36a75ec4698cd83558185c1f202400eb53ae8ff6.

Related: OS#5900
Change-Id: I139b46de0bd15185a7a06109d55f7c759755ec81
2023-03-18 01:33:05 +00:00
Neels Janosch Hofmeyr
27a90869c7 add unique_ids_test.c
Verify that skipping used IDs works for:
- PFCP UP-SEID
- GTP local TEID
- chain_id for nft rulesets -- so far expected to fail,
  fix follows in I139b46de0bd15185a7a06109d55f7c759755ec81.

Related: OS#5900
Change-Id: I36acff15f22d23ade4d281c2af3eb117dfc10359
2023-03-18 01:33:05 +00:00
Neels Janosch Hofmeyr
4e4315c2ba build: add libupf.la (noinst)
Like we do in osmo-bsc.git, gather the osmo-upf objects into a
not-installed libupf.la, so that we can trivially and flexibly link
these to regression test programs.

Will be used by upcoming patch I36acff15f22d23ade4d281c2af3eb117dfc10359
(unique_ids_test).

Change-Id: Id179a47b5d40821d86c7214add14449600198e07
2023-03-18 01:33:05 +00:00
Oliver Smith
8e5fa9ef7b contrib/jenkins: netfilter: use PARALLEL_MAKE
Change-Id: Ia707f7411548c6d82bbb06b835a1930b30c447ec
2023-03-16 11:55:53 +01:00
Oliver Smith
1670321cdc contrib/jenkins: clone netfilter repos with https
Change-Id: Id50c5dac3cfebdf5cf33467ef24d7c4cf5984cdc
2023-03-16 11:55:53 +01:00
Oliver Smith
a3e85aefcb contrib/jenkins: build nftables without python
We don't use the python bindings of nftables with osmo-upf, so don't
build them. Without this, it tries to build them with python2 for some
reason and since a recent nftables commit it fails with:

  running install
  Checking .pth file support in /build/deps/install/stow/nftables/lib/python2.7/site-packages/
  /usr/bin/python -E -c pass
  TEST FAILED: /build/deps/install/stow/nftables/lib/python2.7/site-packages/ does NOT support .pth files
  error: bad install directory or PYTHONPATH

Change-Id: Ie172dca3e6953c353239173bca07b1f62fbf4c34
2023-03-16 11:55:49 +01:00
Neels Janosch Hofmeyr
6007cb92d0 build: drop LIBOSMO_GTLV
After libosmo-gtlv was dropped from configure.ac in
22006ba039, these $(LIBOSMO_GTLV_*) are
empty anyway.

Since we depend on libosmo-pfcp, there is no need to list libosmo-gtlv,
which should be implicitly included.

Change-Id: I3acd3d674e226b004101d65d47beacbbdeed0466
2023-03-14 22:48:44 +01:00
Neels Janosch Hofmeyr
36cca044c4 cosmetic: rename next_seid to next_up_seid
There are UP-SEID and CP-SEID. Only UP-SEID are chosen by the UPF.
Clarify naming.

Change-Id: Ib725857079400accb4781f8a91eca6495b6b92a9
2023-02-24 01:52:02 +01:00
Neels Janosch Hofmeyr
8e17c9933c move next_teid from up_endpoint to g_upf
up_endpoint is about the PFCP endpoint, handing out local TEID is about
local GTP endpoints. Move the TEID allocation to g_upf / upf.c.

An upcoming patch will use a hash table in g_upf to speed up lookup
whether a local TEID is already in use; cosmetically prepare for that.

Change-Id: I8eae5b53c563400ddfded264678d9cfb28b6f737
2023-02-24 01:52:02 +01:00
Neels Janosch Hofmeyr
1961cf90b5 cosmetic: rename g_upf->gtp to tunend, ->nft to tunmap
Upcoming patch I8eae5b53c563400ddfded264678d9cfb28b6f737 will introduce
a g_upf->gtp sub struct for more generally GTP related things (local
TEID assignment).

Change-Id: I74df838af50f38604e2ff06cac0af11ccfdab386
2023-02-24 01:51:06 +01:00
Neels Janosch Hofmeyr
0a87f42f10 cosmetic: reduce dup in tunnel struct definitions
Use a common struct upf_tun_ep and struct upf_tun for both tunend and
tunmap definitions, with a nicer local / remote sub-structuring.

Change-Id: I07866e2acbeb74914e1fd6f66839a5a8ae247b1e
2023-02-22 16:05:03 +01:00
Neels Janosch Hofmeyr
75c07af406 cosmetic: simplify naming: struct upf_tunmap, struct upf_tunend
The "desc" has no meaning, every struct is a description of its data.

The "nft" and "gtp" hint at the specific "nftables" and "GTP kernel
module" implementations. I'd rather keep it more abstract and shorter.
That serves removing dup of shared bits in an upcoming patch.

Change-Id: I15e4552a20067265abb8d2dd716861cd50270028
2023-02-22 16:05:03 +01:00
Neels Janosch Hofmeyr
a2f2650786 minor api doc
Change-Id: I2291e34545844d3a6b82c0e9a3278bb422cc890c
2023-02-22 16:05:03 +01:00
Oliver Smith
4633c23471 Run struct_endianness.py
Ensure there is no diff to prepare to run this in CI.

Related: OS#5884
Change-Id: Ic9c587e8d2a6cedb3d08ce6c1b130e8025617694
2023-02-20 10:54:53 +01:00
85 changed files with 5241 additions and 513 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ config.h
config.h.in
*.pc
*~
*.png
*.*~
*.sw?

View File

@@ -19,7 +19,6 @@ SUBDIRS = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
contrib/osmo-upf.spec.in \
debian \
git-version-gen \
osmoappdesc.py \

View File

@@ -7,3 +7,4 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
osmo-pfcp-tool liburing new dependency to support GTP flooding

View File

@@ -43,6 +43,20 @@ PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.1.0)
PKG_CHECK_MODULES(LIBGTPNL, libgtpnl >= 1.2.0)
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
AC_ARG_ENABLE([uring], [AS_HELP_STRING([--disable-uring], [Build without io_uring support])],
[
ENABLE_URING=$enableval
],
[
ENABLE_URING="yes"
])
AS_IF([test "x$ENABLE_URING" = "xyes"], [
PKG_CHECK_MODULES(LIBURING, [liburing >= 0.7])
AC_DEFINE([HAVE_URING],[1],[Build with io_uring support for GTP flood commands])
])
AM_CONDITIONAL(ENABLE_URING, test "x$ENABLE_URING" = "xyes")
AC_SUBST(ENABLE_URING)
dnl checks for header files
AC_HEADER_STDC
@@ -198,11 +212,13 @@ AC_OUTPUT(
include/Makefile
include/osmocom/Makefile
include/osmocom/upf/Makefile
include/osmocom/pfcptool/Makefile
src/Makefile
src/osmo-upf/Makefile
src/osmo-pfcp-tool/Makefile
tests/Makefile
tests/atlocal
tests/unique_ids/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile

View File

@@ -1 +1,38 @@
SUBDIRS = systemd
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBURING_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBURING_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-udp-responder \
udp_flood_test \
udp_rx_test \
$(NULL)
osmo_udp_responder_SOURCES = \
udp_responder.c \
$(NULL)
udp_flood_test_SOURCES = \
udp_flood_test.c \
$(NULL)
udp_rx_test_SOURCES = \
udp_rx_test.c \
$(NULL)

View File

@@ -0,0 +1,35 @@
digraph G {
rankdir=TB;
labelloc=t;
label="gtplab @ sysmocom";
subgraph cluster_gtplab0 {
label="gtplab0";
rankdir=TB;
gtplab0_172_31 [label="172.16.31.1/24"];
tool [label="osmo-pfcp-tool",shape=box3d];
}
subgraph cluster_gtplab1 {
label="gtplab1";
gtplab1_172_31 [label="172.16.31.2/24"];
gtplab1_172_32 [label="172.16.32.1/24"];
upf_tunend [label="UPF\ntunend",shape=box3d];
}
subgraph cluster_gtplab2 {
label="gtplab2";
gtplab2_172_32 [label="172.16.32.2/24"];
responder [label="osmo-udp-responder",shape=box3d];
}
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunend [label="PFCP", dir=both];
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunend [label="GTP tun",style=bold];
upf_tunend -> gtplab1_172_32 -> gtplab2_172_32 -> responder [label="UDP/IP",style=bold];
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunend [style=dotted,dir=back];
upf_tunend -> gtplab1_172_32 -> gtplab2_172_32 -> responder [style=dotted,dir=back];
}

View File

@@ -0,0 +1,37 @@
digraph G {
rankdir=TB;
labelloc=t;
label="gtplab @ sysmocom";
subgraph cluster_gtplab0 {
label="gtplab0";
rankdir=TB;
gtplab0_172_31 [label="172.16.31.1/24"];
tool [label="osmo-pfcp-tool",shape=box3d];
insert_teid [label="insert return TEID\nin UDP payload",shape=note];
}
subgraph cluster_gtplab1 {
label="gtplab1";
gtplab1_172_31 [label="172.16.31.2/24"];
gtplab1_172_32 [label="172.16.32.1/24"];
upf_tunmap [label="UPF\ntunmap",shape=box3d];
}
subgraph cluster_gtplab2 {
label="gtplab2";
gtplab2_172_32 [label="172.16.32.2/24"];
responder [label="osmo-udp-responder",shape=box3d];
send_to_teid [label="use TEID from\nUDP payload",shape=note];
}
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunmap [label="PFCP", dir=both];
tool -> insert_teid -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunmap [label="GTP tun",style=bold];
upf_tunmap -> gtplab1_172_32 -> gtplab2_172_32 -> responder [label="GTP tun",style=bold];
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf_tunmap [style=dotted,dir=back];
upf_tunmap -> gtplab1_172_32 -> gtplab2_172_32 -> send_to_teid -> responder [style=dotted,dir=back];
}

View File

@@ -0,0 +1,43 @@
digraph G {
rankdir=TB;
labelloc=t;
label="gtplab @ sysmocom";
subgraph cluster_gtplab0 {
label="gtplab0";
rankdir=TB;
gtplab0_10 [label="10.9.25.20/24"];
gtplab0_172_31 [label="172.16.31.1/24"];
tool [label="osmo-pfcp-tool",shape=box3d];
}
subgraph cluster_gtplab1 {
label="gtplab1";
gtplab1_10 [label="10.9.25.21/24"];
gtplab1_172_31 [label="172.16.31.2/24"];
gtplab1_172_32 [label="172.16.32.1/24"];
upf1 [label="UPF\ntunmap",shape=box3d];
}
subgraph cluster_gtplab2 {
label="gtplab2";
gtplab2_10 [label="10.9.25.22/24"];
gtplab2_172_32 [label="172.16.32.2/24"];
upf2 [label="UPF\ntunend",shape=box3d];
responder [label="osmo-udp-responder",shape=box3d];
}
tool -> gtplab0_10 -> gtplab1_10 -> upf1 [label="PFCP", dir=both];
gtplab0_10 -> gtplab2_10 -> upf2 [label="PFCP", dir=both];
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf1 [label="GTP tun",style=bold];
upf1 -> gtplab1_172_32 -> gtplab2_172_32 -> upf2 [label="GTP tun",style=bold];
upf2 -> responder [label="UDP/IP",style=bold];
upf2 -> responder [style=dotted,dir=back];
upf1 -> gtplab1_172_32 -> gtplab2_172_32 -> upf2 [style=dotted,dir=back];
tool -> gtplab0_172_31 -> gtplab1_172_31 -> upf1 [style=dotted,dir=back];
}

View File

@@ -0,0 +1,55 @@
# Establish N PFCP sessions for tunend, and emit massive GTP traffic to the UPF
# to each established tunnel.
#
# osmo-pfcp-tool UPF "internet host"
# |GTP-ep -------GTP-----> GTP-ep|UE-IP-addr -------IP------> arbitrary-IP|
# |10.0.1.1 10.0.2.1|192.168.10.23 123.234.42.23|
# |10.0.1.2
# ^ ^ ^
# ^ | | |
# | | configure by configure by
# configure by from UPF 'ue ip' 'payload target ip',
# 'gtp ip' ("F-TEID=choose") 'payload target port'
# Configure one or more local GTP endpoints to emit GTP packets from.
# Established sessions will use these round-robin.
# These need to be local IP addresses for 'gtp flood' to work.
gtp local 172.16.31.1
# use UE IP addresses from this range, +1 for each new UE:
# 192.168.0.1, 192.168.0.2, ...
ue ip range 192.168.1.2 192.168.254.254
# now associate with UPF and start N sessions.
pfcp-peer 172.16.31.2
tx assoc-setup-req
sleep 1
date
n 2000 session create tunend
wait responses
# All sessions established
date
# For each established PFCP session, emit GTP packets
gtp flood
workers 8
io-uring queue-size 16000
flows-per-session 16
packets-per-flow 1000
# configure the generated GTP payload: send UDP packets from the UE address
# and these source UDP ports to these target addresses and target UDP ports.
# They are used round-robin.
# Source IP is the UE IP address.
payload source port udp range 10000 10010
payload target ip range 172.16.32.2 172.16.32.2
payload target port udp range 23000 23000
date
# All GTP is flowing.
# osmo-pfcp-tool will keep this up for as long as there still are active GTP flows,
# or until receiving a signal interrupt (ctrl-C).
# give some time to gather counters before the tunnel is removed
sleep 1

View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -x
#sudo chrt -r 50 osmo-pfcp-tool 0.gtp_flood.vty -c osmo-pfcp-tool.cfg
sudo ionice -c 1 -n 0 osmo-pfcp-tool 0.gtp_flood.vty -c osmo-pfcp-tool.cfg
#sudo osmo-pfcp-tool 0.gtp_flood.vty -c osmo-pfcp-tool.cfg

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

View File

@@ -0,0 +1,13 @@
interface_name: [enp2s0f0np0]
xdp_attach_mode: generic
api_address: :8080
pfcp_address: 172.16.31.2:8805
pfcp_node_id: 172.16.31.2
metrics_address: :9090
n3_address: 172.16.31.2
qer_map_size: 1000000
far_map_size: 1000000
pdr_map_size: 1000000
feature_ueip: true
feature_ftup: true
teid_pool: 1000000

View File

@@ -0,0 +1,26 @@
log stderr
logging filter all 1
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all notice
logging level set-all info
logging level session debug
logging level nft debug
logging level gtp debug
logging level set-all error
#logging level set-all debug
line vty
bind 127.0.0.1
ctrl
bind 127.0.0.1
timer pfcp x24 5000
pfcp
local-addr 172.16.31.2
tunend
dev create apn-flood 172.16.31.2

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
sudo eupf --config 1.eupf.yaml

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
osmo-upf -c 1.osmo-upf.cfg

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
sudo ip addr add 192.168.1.1/16 dev apn-flood

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
osmo-udp-responder -l 172.16.32.2 -p 23000

View File

@@ -0,0 +1,2 @@
#!/bin/sh
sudo ip route add 192.168.0.0/16 via 172.16.32.1

View File

@@ -0,0 +1,12 @@
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all info
logging level lpfcp error
local-addr 0.0.0.0
listen

View File

@@ -0,0 +1,54 @@
# Establish N PFCP sessions for tunmap, and emit massive GTP traffic to the UPF
# to each established tunnel.
#
# osmo-pfcp-tool UPF "core"
# |GTP-ep -------GTP-----> GTP-ep|GTP-ep ----------IP------> GTP-ep
# |10.0.1.1 10.0.2.1|10.0.3.1 10.0.3.2
# |10.0.1.2
# ^ ^ ^
# ^ | | |
# | | / configure by
# configure by from UPF 'gtp core'
# 'gtp ip' ("F-TEID=choose")
# Configure one or more local GTP endpoints to emit GTP packets from.
# Established sessions will use these round-robin.
# These need to be local IP addresses for 'gtp flood' to work.
gtp local 172.16.31.1
gtp core 172.16.32.2
ue ip range 192.168.1.2 192.168.254.254
# now associate with UPF and start N sessions.
pfcp-peer 172.16.31.2
tx assoc-setup-req
sleep 1
date
n 1 session create tunmap
wait responses
# All sessions established
date
# For each established PFCP session, emit GTP packets
gtp flood
workers 4
io-uring queue-size 1024
flows-per-session 1
packets-per-flow 100000
# configure the generated GTP payload: include in the payload each tunnel's
# GTP TEID needed to correctly echo the GTP payload back with
# osmo-udp-responder.
payload append-info
slew 10000
date
# All GTP is flowing.
# osmo-pfcp-tool will keep this up for as long as there still are active GTP flows,
# or until receiving a signal interrupt (ctrl-C).
# give some time to gather counters before the tunnel is removed
sleep 1

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
osmo-pfcp-tool 0.gtp_flood.vty -c osmo-pfcp-tool.cfg

View File

@@ -0,0 +1,13 @@
interface_name: [enp2s0f0np0,enp2s0f1np1]
xdp_attach_mode: generic
api_address: :8080
pfcp_address: 172.16.31.2:8805
pfcp_node_id: 172.16.31.2
metrics_address: :9090
n3_address: 172.16.31.2
qer_map_size: 1000000
far_map_size: 1000000
pdr_map_size: 1000000
feature_ueip: true
feature_ftup: true
teid_pool: 1000000

View File

@@ -0,0 +1,26 @@
log stderr
logging filter all 1
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all notice
logging level set-all info
logging level session debug
logging level nft debug
logging level gtp debug
logging level set-all error
#logging level set-all debug
line vty
bind 127.0.0.1
ctrl
bind 127.0.0.1
timer pfcp x24 5000
pfcp
local-addr 172.16.31.2
tunend
dev create gtp-echo

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
sudo eupf --config 1.eupf.yaml

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
osmo-upf -c 1.osmo-upf.cfg

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -x
osmo-udp-responder -l 172.16.32.2 -p 2152

View File

@@ -0,0 +1,12 @@
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all info
logging level lpfcp error
local-addr 0.0.0.0
listen

View File

@@ -50,8 +50,12 @@ build_from_netfilter() {
git clone "git://git.netfilter.org/$project" "$project"
cd "$project"
autoreconf --install --force
./configure --prefix="$inst/stow/$project" --without-cli --disable-man-doc
$MAKE install
./configure \
--prefix="$inst/stow/$project" \
--without-cli \
--disable-man-doc \
--enable-python=no
$MAKE $PARALLEL_MAKE install
STOW_DIR="$inst/stow" stow --restow $project
}
build_from_netfilter libnftnl

View File

@@ -0,0 +1,51 @@
# Establish N PFCP sessions for tunend, and emit massive GTP traffic to the UPF
# to each established tunnel.
#
# osmo-pfcp-tool UPF "internet host"
# |GTP-ep -------GTP-----> GTP-ep|UE-IP-addr -------IP------> arbitrary-IP|
# |10.0.1.1 10.0.2.1|192.168.10.23 123.234.42.23|
# |10.0.1.2
# ^ ^ ^
# ^ | | |
# | | configure by configure by
# configure by from UPF 'ue ip' 'target ip',
# 'gtp ip' ("F-TEID=choose") 'target port'
# Configure one or more local GTP endpoints to emit GTP packets from.
# Established sessions will use these round-robin.
# These need to be local IP addresses for 'gtp flood' to work.
gtp local 127.0.1.1 10001
gtp local 127.0.1.2 10002
# use UE IP addresses from this range, +1 for each new UE:
# 192.168.0.1, 192.168.0.2, ...
ue ip range 192.168.23.1 192.168.23.254
# now associate with UPF and start N sessions.
pfcp-peer 127.0.0.11
tx assoc-setup-req
sleep 1
date
n 30 session create tunend
wait responses
# All sessions established
date
# For each established PFCP session, emit GTP packets
gtp flood
workers 2
flows-per-session 1
packets-per-flow 1000
# configure the generated GTP payload: send UDP packets from the UE address
# and these source UDP ports to these target addresses and target UDP ports.
# They are used round-robin.
# Source IP is the UE IP address.
payload source port udp range 10000 10010
payload target ip range 123.234.42.1 123.234.42.254
payload target port udp range 10000 10010
# All GTP is flowing.
# osmo-pfcp-tool will keep this up for as long as there still are active GTP flows,
# or until receiving a signal interrupt (ctrl-C).

View File

@@ -1,4 +1,10 @@
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all info
local-addr 127.0.0.2

View File

@@ -1,11 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print level 1
logging print category 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level set-all notice
logging level set-all info
logging level session debug

View File

@@ -1,11 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print level 1
logging print category 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level set-all notice
logging level set-all info
logging level session debug
@@ -23,3 +23,7 @@ pfcp
local-addr 127.0.0.12
tunmap
table-name osmo-upf-12
# gtp-dev only for GTP-U Echo service
tunend
dev create gtp-echo-12 127.0.0.12

View File

@@ -1,5 +1,5 @@
timer pfcp x23 0
pfcp-peer 127.0.0.1
pfcp-peer 127.0.0.11
tx assoc-setup-req
sleep 1
session tunend

View File

@@ -1,92 +0,0 @@
#
# spec file for package osmo-upf
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
## Disable LTO for now since it breaks compilation of the tests
## https://osmocom.org/issues/4113
%define _lto_cflags %{nil}
Name: osmo-upf
Version: @VERSION@
Release: 0
Summary: OsmoUPF: Osmocom User Plane Function
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/osmo-upf
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf-archive
BuildRequires: automake >= 1.9
BuildRequires: libtool >= 2
BuildRequires: lksctp-tools-devel
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libgtpnl) >= 1.2.0
BuildRequires: pkgconfig(libnftables) >= 1.0.2
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
BuildRequires: pkgconfig(libosmo-pfcp) >= 0.1.0
BuildRequires: pkgconfig(talloc)
%{?systemd_requires}
%description
OsmoUPF: Osmocom User Plane Function
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
%if 0%{?suse_version}
%preun
%service_del_preun %{name}.service
%postun
%service_del_postun %{name}.service
%pre
%service_add_pre %{name}.service
%post
%service_add_post %{name}.service
%endif
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files
%license COPYING
%doc AUTHORS README.md
%{_bindir}/osmo-upf
%{_bindir}/osmo-pfcp-tool
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-upf
%{_docdir}/%{name}/examples/osmo-upf/osmo-upf.cfg
%{_docdir}/%{name}/examples/osmo-upf/osmo-upf-create-dev.cfg
%{_docdir}/%{name}/examples/osmo-upf/osmo-upf-mockup.cfg
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-upf.cfg
%{_unitdir}/%{name}.service
%changelog

View File

@@ -1,13 +1,18 @@
[Unit]
Description=Osmocom User Plane Function (UPF)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-upf -c /etc/osmocom/osmo-upf.cfg
RestartSec=2
AmbientCapabilities=CAP_NET_ADMIN
[Install]
WantedBy=multi-user.target

144
contrib/udp_flood_test.c Normal file
View File

@@ -0,0 +1,144 @@
#include <liburing.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#define SEND_SLOTS 1024
#define BUF_SIZE 2048
struct io_uring ring = {};
int num_packets = 1000000;
int num_packets_prepped = 0;
int num_packets_completed = 0;
int src_fd;
struct osmo_sockaddr remote_osa;
struct send_slot {
struct iovec iov;
uint8_t buf[BUF_SIZE];
struct msghdr msgh;
};
struct send_slot send_slots[SEND_SLOTS];
bool submit_tx(struct send_slot *s)
{
struct io_uring_sqe *sqe;
if (num_packets_prepped >= num_packets)
return false;
num_packets_prepped++;
s->iov.iov_base = s->buf;
s->iov.iov_len = sizeof(s->buf);
s->msgh = (struct msghdr){
.msg_name = &remote_osa,
.msg_namelen = sizeof(remote_osa),
.msg_iov = &s->iov,
.msg_iovlen = 1,
};
sqe = io_uring_get_sqe(&ring);
OSMO_ASSERT(sqe);
io_uring_prep_sendmsg(sqe, src_fd, &s->msgh, 0);
io_uring_sqe_set_data(sqe, s);
return true;
}
void handle_completion(struct io_uring_cqe *cqe)
{
struct send_slot *s;
s = io_uring_cqe_get_data(cqe);
if (cqe->res < 0) {
printf("rc = %d\n", cqe->res);
return;
}
io_uring_cqe_seen(&ring, cqe);
num_packets_completed++;
/* submit more */
submit_tx(s);
}
int main(int argc, const char **argv)
{
int i;
int rc;
const char *local_addr_str = "0.0.0.0";
uint16_t local_port = 42000;
const char *remote_addr_str = "127.0.0.2";
uint16_t remote_port = 23000;
struct osmo_sockaddr_str local_addr = {};
struct osmo_sockaddr local_osa = {};
struct osmo_sockaddr_str remote_addr = {};
struct __kernel_timespec ts_zero = {};
struct __kernel_timespec ts_1s = { .tv_sec = 1 };
if (argc >= 2)
remote_addr_str = argv[1];
if (argc >= 3)
remote_port = atoi(argv[2]);
if (argc >= 4)
num_packets = atoi(argv[3]);
if (osmo_sockaddr_str_from_str(&local_addr, local_addr_str, local_port)
|| osmo_sockaddr_str_to_osa(&local_addr, &local_osa)) {
printf("ERROR: invalid address or port number: %s:%d\n", local_addr_str, local_port);
return -1;
}
if (osmo_sockaddr_str_from_str(&remote_addr, remote_addr_str, remote_port)
|| osmo_sockaddr_str_to_osa(&remote_addr, &remote_osa)) {
printf("ERROR: invalid address or port number: %s:%d\n", remote_addr_str, remote_port);
return -1;
}
/* create and bind socket */
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &local_osa, NULL, OSMO_SOCK_F_BIND);
if (rc < 0)
return -1;
src_fd = rc;
printf("bound UDP %s fd=%d\n", osmo_sock_get_name2(src_fd), src_fd);
printf("sending %d UDP packets to %s\n", num_packets, osmo_sockaddr_to_str(&remote_osa));
rc = io_uring_queue_init(ARRAY_SIZE(send_slots), &ring, 0);
/* fill up tx queue */
for (i = 0; i < ARRAY_SIZE(send_slots); i++) {
submit_tx(&send_slots[i]);
}
while (num_packets_completed < num_packets) {
uint32_t new_submissions;
uint32_t new_completions = 0;
struct io_uring_cqe *cqe;
/* submit any requests from previous loop */
new_submissions = io_uring_submit(&ring);
/* process all pending completions */
while (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_zero) == 0) {
handle_completion(cqe);
new_completions++;
}
/* Nothing happened in this loop iteration, so wait a bit longer */
if (!new_submissions && !new_completions) {
if (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_1s) == 0) {
handle_completion(cqe);
}
}
}
printf("done\n");
return 0;
}

641
contrib/udp_responder.c Normal file
View File

@@ -0,0 +1,641 @@
/* UDP responder: listen on a UDP port, and respond to each received UDP packet back to the sender. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#if HAVE_URING
#define _GNU_SOURCE
#include <getopt.h>
#include <limits.h>
#include <unistd.h>
#include <liburing.h>
#include <pthread.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/pfcptool/gtp_flood.h>
struct cmdline_cmd {
const char *short_option;
const char *long_option;
const char *arg_name;
const char *doc;
const char *value;
};
#define cmdline_foreach(ITER, CMDS) \
for (const struct cmdline_cmd *ITER = (CMDS); \
ITER->short_option || ITER->long_option || ITER->arg_name; \
ITER++)
int cmdline_doc_str_buf(char *buf, size_t buflen, const struct cmdline_cmd *cmds)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
/* First find the longest options part */
int w = 0;
cmdline_foreach (cmd, cmds) {
int cmd_w = 0;
if (cmd->short_option)
cmd_w += 2 + strlen(cmd->short_option);
if (cmd->long_option)
cmd_w += 3 + strlen(cmd->long_option);
if (cmd->arg_name)
cmd_w += 1 + strlen(cmd->arg_name);
w = OSMO_MAX(w, cmd_w);
}
/* vertical gap */
w += 2;
OSMO_STRBUF_PRINTF(sb, "Options:\n");
cmdline_foreach (cmd, cmds) {
char *line_start = sb.pos;
if (cmd->short_option)
OSMO_STRBUF_PRINTF(sb, " -%s", cmd->short_option);
if (cmd->long_option)
OSMO_STRBUF_PRINTF(sb, " --%s", cmd->long_option);
if (cmd->arg_name)
OSMO_STRBUF_PRINTF(sb, " %s", cmd->arg_name);
if (cmd->doc) {
int have = sb.pos - line_start;
int spaces = OSMO_MAX(1, w - have);
OSMO_STRBUF_PRINTF(sb, "%*s", spaces, "");
OSMO_STRBUF_PRINTF(sb, "%s", cmd->doc);
}
OSMO_STRBUF_PRINTF(sb, "\n");
}
return sb.chars_needed;
}
void cmdline_print_help(const struct cmdline_cmd *cmds)
{
char buf[8192];
cmdline_doc_str_buf(buf, sizeof(buf), cmds);
printf("%s", buf);
}
void cmdline_cmd_store_optarg(struct cmdline_cmd *cmd)
{
if (cmd->arg_name)
cmd->value = optarg;
else
cmd->value = (cmd->short_option ? : cmd->long_option);
}
int cmdline_read(struct cmdline_cmd *cmds, int argc, char **argv)
{
char short_options[256] = {};
struct option long_options[128] = {};
int long_options_i = 0;
int long_option_val = 0;
struct osmo_strbuf short_sb = { .buf = short_options, .len = sizeof(short_options) };
cmdline_foreach (cmd, cmds) {
if (cmd->short_option) {
OSMO_STRBUF_PRINTF(short_sb, "%s", cmd->short_option);
if (cmd->arg_name)
OSMO_STRBUF_PRINTF(short_sb, ":");
}
if (cmd->long_option) {
long_options[long_options_i] = (struct option){
cmd->long_option,
cmd->arg_name ? 1 : 0,
&long_option_val,
long_options_i,
};
long_options_i++;
}
}
while (1) {
int option_index = 0;
char c = getopt_long(argc, argv, short_options, long_options, &option_index);
if (c == -1)
break;
if (c == 0) {
struct cmdline_cmd *long_cmd = &cmds[long_option_val];
cmdline_cmd_store_optarg(long_cmd);
} else {
bool found = false;
cmdline_foreach (cc, cmds) {
if (strchr(cc->short_option, c)) {
cmdline_cmd_store_optarg((struct cmdline_cmd *)cc);
found = true;
break;
}
}
if (!found) {
fprintf(stderr, "%s: Error in command line options. Exiting.\n", argv[0]);
return -1;
}
}
}
/* positional args */
cmdline_foreach (cmd, cmds) {
if (optind >= argc)
break;
if (cmd->short_option || cmd->long_option)
continue;
if (!cmd->arg_name)
continue;
((struct cmdline_cmd *)cmd)->value = argv[optind];
optind++;
}
if (optind < argc) {
cmdline_print_help(cmds);
fprintf(stderr, "%s: Unsupported positional argument on command line\n", argv[optind]);
return -1;
}
return 0;
}
const char *cmdline_get(const struct cmdline_cmd *cmds, const char *option_name, const char *default_val)
{
cmdline_foreach (cmd, cmds) {
if (cmd->long_option && !strcmp(cmd->long_option, option_name))
return cmd->value;
if (cmd->short_option && !strcmp(cmd->short_option, option_name))
return cmd->value;
if (cmd->arg_name && !strcmp(cmd->arg_name, option_name))
return cmd->value;
}
return default_val;
}
bool cmdline_get_int(int *dst, int minval, int maxval, int default_val,
const struct cmdline_cmd *cmds, const char *option_name)
{
const char *str = cmdline_get(cmds, option_name, NULL);
if (!str) {
*dst = default_val;
return true;
}
if (osmo_str_to_int(dst, str, 10, minval, maxval)) {
cmdline_print_help(cmds);
printf("ERROR: invalid integer number: %s\n", str);
return false;
}
if (*dst < minval || *dst > maxval) {
cmdline_print_help(cmds);
printf("ERROR: number out of range: %d <= %d <= %d\n", minval, *dst, maxval);
return false;
}
return true;
}
struct udp_port {
struct llist_head entry;
/* IP address and UDP port from user input */
struct osmo_sockaddr osa;
/* locally bound socket */
int fd;
};
enum data_io_type {
IO_UNUSED = 0,
IO_RECV,
IO_SEND,
};
struct data_io {
enum data_io_type type;
struct osmo_sockaddr osa;
struct iovec iov;
struct msghdr msgh;
uint8_t *data;
size_t data_size;
int n;
};
struct io_queue {
size_t d_size;
struct data_io d[0];
};
static void data_io_prep_recv(struct io_uring *ring, struct udp_port *port, struct data_io *d)
{
struct io_uring_sqe *sqe;
*d = (struct data_io){
.type = IO_RECV,
.iov = {
.iov_base = d->data,
.iov_len = d->data_size,
},
.msgh = {
.msg_name = &d->osa,
.msg_namelen = sizeof(d->osa),
.msg_iov = &d->iov,
.msg_iovlen = 1,
},
.data_size = d->data_size,
.data = d->data,
};
sqe = io_uring_get_sqe(ring);
OSMO_ASSERT(sqe);
io_uring_prep_recvmsg(sqe, port->fd, &d->msgh, 0);
io_uring_sqe_set_data(sqe, d);
}
static void data_io_prep_send(struct io_uring *ring, struct udp_port *port, struct data_io *d)
{
struct io_uring_sqe *sqe;
d->type = IO_SEND;
sqe = io_uring_get_sqe(ring);
OSMO_ASSERT(sqe);
io_uring_prep_sendmsg(sqe, port->fd, &d->msgh, 0);
io_uring_sqe_set_data(sqe, d);
}
static bool get_payload_info(struct gtp_flood_payload_info *dst, struct data_io *d)
{
uint8_t *pi;
uint8_t *len;
size_t copy_len;
len = d->iov.iov_base + d->iov.iov_len - 1;
if ((*len) > d->iov.iov_len)
return false;
pi = len - (*len);
if (strncmp((void *)pi, "info", 4))
return false;
copy_len = OSMO_MIN(sizeof(*dst), *len);
*dst = (struct gtp_flood_payload_info){};
memcpy((void *)dst, pi, copy_len);
return true;
}
struct counter {
uint64_t count;
uint64_t last;
};
struct traffic_counter {
struct counter packets;
struct counter bytes;
};
uint64_t counter_get(struct counter *c)
{
uint64_t val = c->count - c->last;
c->last = c->count;
return val;
}
struct traffic_counter g_rx = {};
struct traffic_counter g_tx = {};
static void data_io_handle_completion(struct io_uring *ring, struct udp_port *port, struct io_uring_cqe *cqe,
int response_size, int response_n)
{
struct data_io *d;
struct osmo_sockaddr *osa = NULL;
int rc;
struct gtp_flood_payload_info pi;
d = io_uring_cqe_get_data(cqe);
osa = &d->osa;
rc = cqe->res;
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "%s -> rx error rc=%d flags=0x%x\n",
osa ? osmo_sockaddr_to_str(osa) : "NULL",
rc, cqe->flags);
return;
}
switch (d->type) {
case IO_RECV:
/* done reading */
d->iov.iov_len = rc;
LOGP(DLGLOBAL, LOGL_DEBUG, "%s -> rx rc=%d flags=0x%x: %s\n",
osa ? osmo_sockaddr_to_str(osa) : "NULL",
rc, cqe->flags,
osmo_quote_str(d->iov.iov_base, d->iov.iov_len));
io_uring_cqe_seen(ring, cqe);
g_rx.packets.count++;
g_rx.bytes.count += d->iov.iov_len;
if (response_n < 1)
break;
if (get_payload_info(&pi, d)) {
/* set the return TEID */
struct gtp1u_hdr *gtp_hdr = (void *)d->iov.iov_base;
gtp_hdr->tei = pi.return_teid;
}
/* resubmit back to sender */
/* adjust size? */
if (response_size > 0)
d->iov.iov_len = response_size;
data_io_prep_send(ring, port, d);
break;
case IO_SEND:
/* done writing. */
LOGP(DLGLOBAL, LOGL_DEBUG, "%s <- tx rc=%d flags=0x%x: %s\n",
osa ? osmo_sockaddr_to_str(osa) : "NULL",
rc, cqe->flags,
osmo_quote_str(d->iov.iov_base, rc));
io_uring_cqe_seen(ring, cqe);
g_tx.packets.count++;
g_tx.bytes.count += rc;
d->n++;
/* Send again? If not, re-submit open slot for reading. */
if (d->n < response_n)
data_io_prep_send(ring, port, d);
else
data_io_prep_recv(ring, port, d);
break;
default:
OSMO_ASSERT(0);
}
}
struct cmdline_cmd cmds[] = {
{
.short_option = "h",
.long_option = "help",
.doc = "Show this help",
},
{
.short_option = "l",
.long_option = "local-addr",
.arg_name = "IP-ADDR",
.doc = "Listen on local IP address (default is 0.0.0.0).",
},
{
.short_option = "p",
.long_option = "port",
.arg_name = "UDP-PORT",
.doc = "Listen on local UDP port.",
},
/*
{
.short_option = "P",
.long_option = "port-range-to",
.arg_name = "UDP-PORT-TO",
.doc = "Listen on a range of ports, from --port to --port-range-to, inclusive.",
},
*/
{
.short_option = "s",
.long_option = "response-size",
.arg_name = "BYTES",
.doc = "When responding, enlarge or shorten the payload to this size.",
},
{
.short_option = "n",
.long_option = "response-repeat",
.arg_name = "N",
.doc = "Respond N times, i.e. multiply the returned traffic.",
},
{
.long_option = "io-uring-queue",
.arg_name = "SIZE",
.doc = "I/O tuning: queue size to use for io_uring, default is 4000.",
},
{
.long_option = "io-uring-buf",
.arg_name = "SIZE",
.doc = "I/O tuning: maximum payload size, default is 2048.",
},
{
.long_option = "workers",
.arg_name = "N",
.doc = "Number of rx threads to run",
},
{}
};
static const struct log_info_cat categories[] = {
};
const struct log_info udp_responder_log_info = {
.cat = categories,
.num_cat = ARRAY_SIZE(categories),
};
struct worker {
int id;
struct io_queue *q;
struct io_uring ring;
pthread_t worker;
};
struct {
int port_nr;
const char *local_addr;
int queue_size;
int buf_size;
int response_size;
int response_n;
int workers_n;
struct udp_port port;
} cfg = {};
static void start_rx_worker(struct worker *w);
int main(int argc, char **argv)
{
struct osmo_sockaddr_str addr = {};
struct osmo_sockaddr osa = {};
osmo_init_logging2(OTC_GLOBAL, &udp_responder_log_info);
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
if (cmdline_read(cmds, argc, argv)
|| cmdline_get(cmds, "help", NULL)) {
cmdline_print_help(cmds);
return -1;
}
if (!cmdline_get_int(&cfg.port_nr, 1, 65535, 23000, cmds, "port"))
return -1;
cfg.local_addr = cmdline_get(cmds, "local-addr", "0.0.0.0");
if (osmo_sockaddr_str_from_str(&addr, cfg.local_addr, cfg.port_nr)
|| osmo_sockaddr_str_to_osa(&addr, &osa)) {
printf("ERROR: invalid interface or port number: %s:%d\n", cfg.local_addr, cfg.port_nr);
return -1;
}
if (!cmdline_get_int(&cfg.queue_size, 1, 65535, 4000, cmds, "io-uring-queue"))
return -1;
if (!cmdline_get_int(&cfg.buf_size, 1, 65535, 2048, cmds, "io-uring-buf"))
return -1;
if (!cmdline_get_int(&cfg.response_size, 0, cfg.buf_size, 0, cmds, "response-size"))
return -1;
if (!cmdline_get_int(&cfg.response_n, 0, INT_MAX, 1, cmds, "response-repeat"))
return -1;
if (!cmdline_get_int(&cfg.workers_n, 1, INT_MAX, 4, cmds, "workers"))
return -1;
cfg.port.osa = osa;
/* create and bind socket */
int rc;
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &cfg.port.osa, NULL, OSMO_SOCK_F_BIND);
/* (logging of errors already happens in osmo_sock_init_osa() */
if (rc < 0)
return -1;
cfg.port.fd = rc;
LOGP(DLGLOBAL, LOGL_NOTICE, "bound UDP %s fd=%d\n", osmo_sock_get_name2(cfg.port.fd), cfg.port.fd);
struct worker *workers = talloc_zero_array(OTC_GLOBAL, struct worker, cfg.workers_n);
for (int i = 0; i < cfg.workers_n; i++) {
workers[i].id = i + 1;
start_rx_worker(&workers[i]);
}
/* periodically log rx stats */
while (1) {
static time_t last_info_log = 0;
time_t now;
time_t diff_time;
now = time(NULL);
if (!last_info_log)
diff_time = 1;
else
diff_time = now - last_info_log;
/* the resolution is in seconds, output stats once per second. */
if (diff_time > 0) {
uint64_t diff_rx_packets;
uint64_t diff_rx_bytes;
uint64_t diff_tx_packets;
uint64_t diff_tx_bytes;
last_info_log = now;
/* hoping that the counter increments are atomic */
diff_rx_packets = counter_get(&g_rx.packets);
diff_rx_bytes = counter_get(&g_rx.bytes);
diff_tx_packets = counter_get(&g_tx.packets);
diff_tx_bytes = counter_get(&g_tx.bytes);
if (diff_rx_packets || diff_tx_packets) {
printf("%ld RX:%7"PRIu64" packets (%4"PRIu64"Mb/s) TX:%7"PRIu64" packets (%4"PRIu64"Mb/s)\n",
now,
g_rx.packets.count,
diff_rx_bytes / (1024*1024),
g_tx.packets.count,
diff_tx_bytes / (1024*1024));
fflush(stdout);
}
}
usleep(1000);
}
}
static void *rx_worker_func(void *_worker);
static void start_rx_worker(struct worker *w)
{
w->q = talloc_zero_size(OTC_GLOBAL, sizeof(struct io_queue) + cfg.queue_size * sizeof(struct data_io));
OSMO_ASSERT(w->q);
*w->q = (struct io_queue){
.d_size = cfg.queue_size,
};
for (int i = 0; i < w->q->d_size; i++) {
struct data_io *d = &w->q->d[i];
*d = (struct data_io){
.data = talloc_size(w->q, cfg.buf_size),
.data_size = cfg.buf_size,
};
}
int rc = pthread_create(&w->worker, NULL, rx_worker_func, w);
OSMO_ASSERT(rc >= 0);
}
static void *rx_worker_func(void *_worker)
{
struct worker *w = _worker;
struct io_queue *q = w->q;
int rc = io_uring_queue_init(q->d_size, &w->ring, 0);
OSMO_ASSERT(rc >= 0);
for (int i = 0; i < q->d_size; i++) {
struct data_io *d = &q->d[i];
/* fill once with random printable data */
for (int j = 0; j < d->data_size; j++)
d->data[j] = 32 + random() % (126 - 32 + 1);
}
/* fill the queue to start receiving */
for (int i = 0; i < q->d_size; i++) {
data_io_prep_recv(&w->ring, &cfg.port, &q->d[i]);
}
struct __kernel_timespec ts_zero = {};
struct __kernel_timespec ts_1s = { .tv_sec = 1 };
while (1) {
uint32_t submitted;
uint32_t completed = 0;
struct io_uring_cqe *cqe;
/* submit any requests from previous loop */
submitted = io_uring_submit(&w->ring);
/* process all pending completions */
while (io_uring_wait_cqe_timeout(&w->ring, &cqe, &ts_zero) == 0) {
data_io_handle_completion(&w->ring, &cfg.port, cqe, cfg.response_size, cfg.response_n);
completed++;
}
/* Wait a bit longer */
if (!submitted && !completed) {
if (io_uring_wait_cqe_timeout(&w->ring, &cqe, &ts_1s) == 0) {
data_io_handle_completion(&w->ring, &cfg.port, cqe, cfg.response_size, cfg.response_n);
completed++;
}
}
}
talloc_free(q);
return 0;
}
#endif /* HAVE_URING */

137
contrib/udp_rx_test.c Normal file
View File

@@ -0,0 +1,137 @@
#include <liburing.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#define RECV_SLOTS 1024
#define BUF_SIZE 2048
struct io_uring ring = {};
int num_packets_prepped = 0;
int num_packets_received = 0;
int rx_fd;
struct recv_slot {
struct iovec iov;
uint8_t buf[BUF_SIZE];
struct msghdr msgh;
};
struct recv_slot recv_slots[RECV_SLOTS];
bool submit_rx(struct recv_slot *s)
{
struct io_uring_sqe *sqe;
num_packets_prepped++;
s->iov.iov_base = s->buf;
s->iov.iov_len = sizeof(s->buf);
s->msgh = (struct msghdr){
.msg_iov = &s->iov,
.msg_iovlen = 1,
};
sqe = io_uring_get_sqe(&ring);
OSMO_ASSERT(sqe);
io_uring_prep_recvmsg(sqe, rx_fd, &s->msgh, 0);
io_uring_sqe_set_data(sqe, s);
return true;
}
void handle_completion(struct io_uring_cqe *cqe)
{
struct recv_slot *s;
s = io_uring_cqe_get_data(cqe);
if (cqe->res <= 0) {
printf("rc = %d\n", cqe->res);
return;
}
io_uring_cqe_seen(&ring, cqe);
num_packets_received++;
/* submit more */
submit_rx(s);
}
int main(int argc, const char **argv)
{
int i;
int rc;
const char *local_addr_str = "0.0.0.0";
uint16_t local_port = 23000;
struct osmo_sockaddr_str local_addr = {};
struct osmo_sockaddr local_osa = {};
struct __kernel_timespec ts_zero = {};
struct __kernel_timespec ts_1s = { .tv_sec = 1 };
if (argc >= 2)
local_addr_str = argv[1];
if (argc >= 3)
local_port = atoi(argv[2]);
if (osmo_sockaddr_str_from_str(&local_addr, local_addr_str, local_port)
|| osmo_sockaddr_str_to_osa(&local_addr, &local_osa)) {
printf("ERROR: invalid address or port number: %s:%d\n", local_addr_str, local_port);
return -1;
}
/* create and bind socket */
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &local_osa, NULL, OSMO_SOCK_F_BIND);
if (rc < 0)
return -1;
rx_fd = rc;
printf("bound UDP %s fd=%d\n", osmo_sock_get_name2(rx_fd), rx_fd);
rc = io_uring_queue_init(ARRAY_SIZE(recv_slots), &ring, 0);
/* fill up tx queue */
for (i = 0; i < ARRAY_SIZE(recv_slots); i++) {
submit_rx(&recv_slots[i]);
}
while (1) {
uint32_t new_submissions;
uint32_t new_completions = 0;
struct io_uring_cqe *cqe;
/* submit any requests from previous loop */
new_submissions = io_uring_submit(&ring);
/* process all pending completions */
while (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_zero) == 0) {
handle_completion(cqe);
new_completions++;
}
/* Nothing happened in this loop iteration, so wait a bit longer */
if (!new_submissions && !new_completions) {
if (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_1s) == 0) {
handle_completion(cqe);
new_completions++;
}
}
/* log rx */
if (1) {
static time_t last_info_log = 0;
time_t now;
now = time(NULL);
if (now != last_info_log) {
last_info_log = now;
printf("%ld RX: %7d packets\n", now, num_packets_received);
fflush(stdout);
}
}
}
printf("done\n");
return 0;
}

2
debian/compat vendored
View File

@@ -1 +1 @@
9
10

6
debian/control vendored
View File

@@ -2,7 +2,8 @@ Source: osmo-upf
Section: net
Priority: extra
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>=9),
# liburing-dev: don't try to install it on debian 10 and ubuntu 20.04
Build-Depends: debhelper (>= 10),
dh-autoreconf,
autotools-dev,
autoconf,
@@ -16,7 +17,8 @@ Build-Depends: debhelper (>=9),
libnftables-dev (>= 1.0.2),
libosmocore-dev (>= 1.6.0),
libosmo-pfcp-dev (>= 0.1.0),
osmo-gsm-manuals-dev (>= 1.2.0)
osmo-gsm-manuals-dev (>= 1.2.0),
liburing-dev | base-files (<< 11) | ubuntu-keyring (<< 2021),
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf

38
debian/postinst vendored Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "0.2.0"; then
if [ -e /etc/osmocom/osmo-upf.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-upf.cfg
chmod -v 0660 /etc/osmocom/osmo-upf.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@@ -1,11 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print level 1
logging print category 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level set-all notice
timer pfcp x24 5000

View File

@@ -1,11 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print level 1
logging print category 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level set-all notice
timer pfcp x24 5000

View File

@@ -1,11 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print level 1
logging print category 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level set-all notice
timer pfcp x24 5000

View File

@@ -59,9 +59,7 @@ tunmap
table-name osmo-upf-2
----
=== Configuring Primary Links
==== Configure PFCP Server
=== Configure PFCP Server
The following example configures OsmoUPF to listen for PFCP association requests
from Control Plane Function entities on local interface 10.9.8.7, port 8805:
@@ -83,15 +81,15 @@ ID, and so far uses the 'local-addr' as local Node ID -- hence the 'local-addr'
must not be "0.0.0.0", which is an unfortunate consequence. This is likely to
improve in the future, see https://osmocom.org/issues/5682 .
==== Configure Linux Kernel GTP Features
=== Linux Kernel Features
OsmoUPF uses two distinct Linux kernel features:
* The GTP module is used for GTP encapsulation/decapsulation from/to
* The GTP module is used for `tunend`: GTP encapsulation/decapsulation from/to
"the internet".
* The netfilter module is used for GTP tunnel proxying, also known as
tunnel forwarding or tunnel mapping.
* The netfilter framework and nftables are used for `tunmap`: GTP tunnel proxying,
also known as tunnel forwarding or tunnel mapping.
.Linux kernel feature usage
[graphviz]
@@ -99,11 +97,15 @@ OsmoUPF uses two distinct Linux kernel features:
include::upf_gtp_roles.dot[]
----
GTP kernel module configuration can be omitted for sites that serve only as GTP
forwarding proxy, without encapsulation/decapsulation of GTP payloads.
GTP kernel module configuration in the `tunend` section can be omitted for sites
that serve only as GTP forwarding proxy, without encapsulation/decapsulation of
GTP payloads -- except to provide GTP Echo service, see <<gtp_echo>>.
Netfilter configuration in the `tunmap` section can be omitted for sites only
serving as GTP tunnel endpoint.
[[gtp_module]]
===== Configure Linux Kernel GTP Module for `tunend`
=== Configure Linux Kernel GTP Module for `tunend`
The Linux kernel GTP module is used for the `tunend` use case, i.e. GTP
encapsulation/decapsulation from/to "the internet".
@@ -160,16 +162,62 @@ the `dev` config. In this case, all Network Instance names will be served by
this GTP device. When using ANY, there should be exactly one GTP dev configured.
[[nftables]]
===== Configure Linux netfilter for `tunmap`
=== Configure Linux netfilter for `tunmap`
The Linux kernel netfilter module is used for GTP tunnel proxying, also known as
tunnel forwarding or tunnel mapping.
Using the netfilter module usually requires no configuration in `osmo-upf.cfg`.
When using the netfilter module, you may set up `osmo-upf.cfg` for:
- GTP Echo (required)
- nft table name (optional)
`osmo-upf` creates a new netfilter table, under which it submits rule sets for
GTP tunnel proxying. This table name defaults to `osmo-upf`. A custom table name
can be configured in `osmo-upf.cfg` like this:
[[gtp_echo]]
==== GTP Echo
You need to ensure that OsmoUPF responds to GTP Echo requests.
- A GTP device configured for `tunend` implicitly includes a GTP Echo service.
- For `tunmap`, no GTP Echo mechanism is implemented.
So, when your use case is `tunmap` only, you should still add a GTP device as
for `tunend`, only to provide the GTP Echo service.
Here are some options to do so:
If you have no GTP devices configured in `osmo-upf.cfg` yet, you can add a
single GTP device without a specific IP address, in order to respond to GTP-U
Echo requests on all interfaces to anyone that is asking:
----
tunend
dev create gtp-echo
----
Note that `gtp-echo` is just an arbitrary GTP device name, choose any string
that makes a valid network device name and is still available, as in the `dev`
argument in the `ip addr show dev` command on Linux.
This will bind osmo-upf on 0.0.0.0:2152 to respond to GTP Echo requests.
If you would like to limit GTP Echo responses to specific network interfaces,
you need to add a separate GTP device per local IP address:
----
tunend
dev create gtp-echo1 192.168.0.23
dev create gtp-echo2 10.9.8.17
----
This will bind osmo-upf only on 192.168.0.23:2152 and 10.9.8.17:2152 to respond
to GTP Echo requests.
For creating and manipulating a GTP device in more versatile ways, see
<<gtp_module>>.
==== nft Table Name
For `tunmap`, `osmo-upf` creates a new nft table, under which it submits
rule sets for GTP tunnel proxying. This table name defaults to `osmo-upf`. A
custom table name can be configured in `osmo-upf.cfg` like this:
----
tunmap
@@ -177,4 +225,45 @@ tunmap
----
When running more than one osmo-upf process on a system, pick distinct table
names to avoid name collisions in the nftables reulesets.
names to avoid name collisions in the nftables rulesets.
=== IP Forwarding
In order to allow forwarding GTP payloads, the Linux operating system must
be configured to allow IP forwarding.
Note that there are many distribution-specific ways to configure this, and there
might be higher-level firewall rule management software available like `ufw`.
You should configure firewall rules matching your distribution and setup.
To allow IP forwarding from and to all interfaces globally in a reboot-safe way,
you may put a line like this in /etc/sysctl.conf:
----
net.ipv4.ip_forward=1
----
To do the same in an ad-hoc way that is not reboot safe but takes effect
immediately:
----
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
----
It is also possible to instruct the firewall to allow IP forwarding for specific
network devices only. For example, on a Debian based system, place an nft
ruleset like this in `/etc/nftables.conf`:
----
define gtp_netdevs = { eth0, eth23 };
table inet filter {
chain forward {
type filter hook forward priority filter; policy drop;
iifname $gtp_netdevs oifname $gtp_netdevs udp dport 2152 accept
}
}
----
This ruleset allows IP forwarding, but limited to the GTP-U port 2152,
and to two specific network devices eth0 and eth23.

View File

@@ -1,3 +1,4 @@
SUBDIRS = \
upf \
pfcptool \
$(NULL)

View File

@@ -0,0 +1,6 @@
noinst_HEADERS = \
checksum.h \
gtp_flood.h \
pfcp_tool.h \
range.h \
$(NULL)

View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
#include <netinet/in.h>
uint16_t ip_fast_csum(const void *iph, unsigned int ihl);
uint32_t csum_partial(const void *buff, int len, uint32_t wsum);
uint16_t ip_compute_csum(const void *buff, int len);
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
uint32_t len, uint8_t proto, uint32_t csum);
uint16_t csum_fold(uint32_t csum);

View File

@@ -0,0 +1,58 @@
#pragma once
#include <osmocom/core/socket.h>
/* According to 3GPP TS 29.060. */
struct gtp1u_hdr {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
#else
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
#endif
uint8_t type;
uint16_t length;
uint32_t tei;
};
struct gtp_flood;
struct udp_port;
struct gtp_flood_cfg {
unsigned int workers;
unsigned int queue_size;
unsigned int slew_us;
};
struct gtp_flood *gtp_flood_alloc(void *ctx, const struct gtp_flood_cfg *cfg);
/* information passed on within generated GTP payload. Main purpose is to allow echoing payloads back into a GTP tunnel
* by osmo-udp-responder, which requires knowledge of the counterpart TEID.
* (future: add in-band instructions for the responder to shape traffic in certain ways: multiple echos or modified
* packet size...)
*/
struct gtp_flood_payload_info {
char mark[4];
/* ordered exactly as it should be returned in a GTP header (network byte order) */
uint32_t return_teid;
} __attribute__((packed));
struct gtp_flood_flow_cfg {
bool rx;
struct udp_port *gtp_local;
/* below used only for rx == false */
struct osmo_sockaddr gtp_remote;
uint32_t gtp_remote_teid;
struct osmo_sockaddr payload_src;
struct osmo_sockaddr payload_dst;
unsigned int num_packets;
bool append_payload_info;
struct gtp_flood_payload_info payload_info;
};
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
const struct gtp_flood_flow_cfg *flow_cfg);
void gtp_flood_start(struct gtp_flood *gtp_flood);
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood);

View File

@@ -28,14 +28,23 @@
#include <osmocom/core/tdef.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/vty/command.h>
#include <osmocom/pfcp/pfcp_msg.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/pfcptool/range.h>
#include <osmocom/pfcptool/gtp_flood.h>
struct osmo_tdef;
struct ctrl_handle;
enum pfcp_tool_vty_node {
PEER_NODE = _LAST_OSMOVTY_NODE + 1,
SESSION_NODE,
GTP_FLOOD_NODE,
};
extern struct osmo_tdef g_pfcp_tool_tdefs[];
extern struct osmo_tdef_group g_pfcp_tool_tdef_groups[];
@@ -61,14 +70,14 @@ struct pfcp_tool_gtp_tun {
struct pfcp_tool_gtp_tun_ep remote;
};
struct pfcp_tool_tunend_desc {
struct pfcp_tool_tunend {
struct pfcp_tool_gtp_tun access;
struct {
struct osmo_sockaddr_str ue_local_addr;
} core;
};
struct pfcp_tool_tunmap_desc {
struct pfcp_tool_tunmap {
struct pfcp_tool_gtp_tun access;
struct pfcp_tool_gtp_tun core;
};
@@ -83,13 +92,23 @@ struct pfcp_tool_session {
enum up_gtp_action_kind kind;
union {
/* En-/De-capsulate GTP: add/remove a GTP header and forward the GTP payload from/to plain IP. */
struct pfcp_tool_tunend_desc tunend;
struct pfcp_tool_tunend tunend;
/* Tunnel-map GTP: translate from one TEID to another and forward */
struct pfcp_tool_tunmap_desc tunmap;
struct pfcp_tool_tunmap tunmap;
};
};
struct udp_port {
struct llist_head entry;
/* IP address and UDP port from user input */
struct osmo_sockaddr osa;
/* In case this is a locally bound port, this is the fd for the socket. */
struct osmo_fd ofd;
};
struct g_pfcp_tool {
struct ctrl_handle *ctrl;
@@ -100,6 +119,43 @@ struct g_pfcp_tool {
struct osmo_pfcp_endpoint *ep;
struct llist_head peers;
uint32_t next_teid_state;
struct {
struct range ip_range;
} ue;
struct {
/* list of struct udp_port */
struct llist_head gtp_local_addrs;
struct udp_port *gtp_local_addrs_next;
/* list of struct udp_port */
struct llist_head gtp_core_addrs;
struct udp_port *gtp_core_addrs_next;
struct {
struct {
/* source address is always the UE IP address */
struct range udp_port_range;
} source;
struct {
struct range ip_range;
struct range udp_port_range;
} target;
bool append_info;
} payload;
struct {
struct gtp_flood_cfg cfg;
unsigned int flows_per_session;
unsigned int packets_per_flow;
struct gtp_flood *state;
} flood;
} gtp;
};
extern struct g_pfcp_tool *g_pfcp_tool;
@@ -108,7 +164,7 @@ void g_pfcp_tool_alloc(void *ctx);
void pfcp_tool_vty_init_cfg();
void pfcp_tool_vty_init_cmds();
int pfcp_tool_mainloop();
int pfcp_tool_mainloop(int poll);
struct pfcp_tool_peer *pfcp_tool_peer_find_or_create(const struct osmo_sockaddr *remote_addr);
struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid,
@@ -117,3 +173,13 @@ void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, st
int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m);
uint64_t peer_new_seid(struct pfcp_tool_peer *peer);
uint32_t pfcp_tool_new_teid(void);
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst);
struct udp_port *pfcp_tool_have_local_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port);
struct udp_port *pfcp_tool_have_core_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port);
void pfcp_tool_gtp_flood_start(void);
int pfcp_tool_vty_go_parent(struct vty *vty);

View File

@@ -0,0 +1,38 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
struct osmo_sockaddr;
/* A value large enough for an IPv6 address (128bit) */
struct range_val {
uint64_t buf[2];
size_t size;
};
/* Manage stepping through a defined range of values large enough to handle IPv6 addresses. */
struct range {
struct range_val first;
struct range_val last;
struct range_val next;
bool wrapped;
};
/* struct range r = {};
* range_val_set_int(&r.first, 23);
* range_val_set_int(&r.last, 42);
* while (1) {
* range_next();
* unsigned int val = range_val_get_int(&r.next);
* printf("%u\n", val);
* }
*/
void range_next(struct range *r);
void range_val_set_int(struct range_val *rv, uint32_t val);
uint32_t range_val_get_int(const struct range_val *rv);
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val);
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv);
void range_val_inc(struct range_val *rv);
int range_val_cmp(const struct range_val *a, const struct range_val *b);

View File

@@ -7,5 +7,6 @@ noinst_HEADERS = \
upf_gtp.h \
upf_gtpu_echo.h \
upf_nft.h \
upf_tun.h \
up_gtp_action.h \
$(NULL)

View File

@@ -35,14 +35,14 @@ struct osmo_sockaddr;
struct up_endpoint {
struct osmo_pfcp_endpoint *pfcp_ep;
/* list of struct up_peer. */
struct llist_head peers;
uint64_t next_seid_state;
uint32_t next_teid_state;
uint64_t next_up_seid_state;
};
struct up_endpoint *up_endpoint_init(void *ctx, const struct osmo_sockaddr *local_addr);
struct up_endpoint *up_endpoint_alloc(void *ctx, const struct osmo_sockaddr *local_addr);
int up_endpoint_bind(struct up_endpoint *up_ep);
void up_endpoint_free(struct up_endpoint **ep);
uint64_t up_endpoint_next_seid(struct up_endpoint *ep);
uint32_t up_endpoint_next_teid(struct up_endpoint *ep);
uint64_t up_endpoint_next_up_seid(struct up_endpoint *ep);

View File

@@ -54,10 +54,10 @@ struct up_gtp_action {
enum up_gtp_action_kind kind;
union {
/* En-/De-capsulate GTP: add/remove a GTP header and forward the GTP payload from/to plain IP. */
struct upf_gtp_tunend_desc tunend;
struct upf_tunend tunend;
/* Tunnel-map GTP: translate from one TEID to another and forward */
struct upf_nft_tunmap_desc tunmap;
struct upf_tunmap tunmap;
};
/* volatile loop variable to match up wanted and actually present GTP actions */

View File

@@ -44,6 +44,7 @@ struct up_peer {
struct llist_head entry;
struct osmo_fsm_inst *fi;
/* backpointer */
struct up_endpoint *up_endpoint;
/* peer's remote address */

View File

@@ -46,6 +46,7 @@ struct up_session {
struct hlist_node node_by_cp_seid;
struct osmo_fsm_inst *fi;
/* backpointer */
struct up_peer *up_peer;
struct osmo_pfcp_ie_f_seid cp_f_seid;
@@ -65,8 +66,7 @@ struct up_session {
struct llist_head active_gtp_actions;
};
struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid,
const struct osmo_pfcp_ie_f_seid *up_f_seid);
struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
struct up_session *up_session_find_by_up_seid(struct up_peer *peer, uint64_t up_seid);
struct up_session *up_session_find_by_cp_f_seid(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
struct up_session *up_session_find_by_local_teid(struct up_peer *peer, uint32_t teid);

View File

@@ -68,7 +68,7 @@ struct tunend_vty_cfg_dev {
struct tunend_vty_cfg {
/* list of struct tunend_vty_cfg_dev, GTP devices as in the config file. The actual GTP devices in use are in
* g_upf->gtp.devs. */
* g_upf->tunend.devs. */
struct llist_head devs;
};
@@ -101,7 +101,7 @@ struct g_upf {
int32_t genl_id;
uint8_t recovery_count;
} gtp;
} tunend;
/* Tunnel forwarding via linux netfilter */
struct {
@@ -113,7 +113,11 @@ struct g_upf {
int priority_pre;
int priority_post;
uint32_t next_chain_id_state;
} nft;
} tunmap;
struct {
uint32_t next_local_teid_state;
} gtp;
struct llist_head netinst;
};
@@ -130,7 +134,11 @@ enum upf_log_subsys {
void g_upf_alloc(void *ctx);
void upf_vty_init();
int upf_pfcp_listen();
int upf_pfcp_init(void);
int upf_pfcp_listen(void);
int upf_gtp_devs_open();
void upf_gtp_devs_close();
uint32_t upf_next_local_teid(void);
uint32_t upf_next_chain_id(void);

View File

@@ -27,6 +27,8 @@
#include <osmocom/core/select.h>
#include <osmocom/core/logging.h>
#include <osmocom/upf/upf_tun.h>
#define LOG_GTP_DEV(DEV, LEVEL, FMT, ARGS...) \
LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_dev_to_str_c(OTC_SELECT, (DEV)), ##ARGS)
@@ -57,19 +59,14 @@ struct upf_gtp_dev {
/* Description of a GTP encapsulation / decapsulation.
* The active state to operate the GTP kernel module accordingly is kept in struct upf_gtp_tunend. */
struct upf_gtp_tunend_desc {
struct {
struct osmo_sockaddr gtp_local_addr;
uint32_t local_teid;
struct osmo_sockaddr gtp_remote_addr;
uint32_t remote_teid;
} access;
struct upf_tunend {
struct upf_tun access;
struct {
struct osmo_sockaddr ue_local_addr;
} core;
};
int upf_gtp_tunend_desc_cmp(const struct upf_gtp_tunend_desc *a, const struct upf_gtp_tunend_desc *b);
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b);
int upf_gtp_genl_ensure_open();
void upf_gtp_genl_close();
@@ -80,8 +77,8 @@ struct upf_gtp_dev *upf_gtp_dev_find_by_name(const char *name);
struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *local_addr);
struct upf_gtp_dev *upf_gtp_dev_first();
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *t);
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *t);
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_tunend *t);
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_tunend *t);
int upf_gtp_dev_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_dev *dev);
char *upf_gtp_dev_to_str_c(void *ctx, const struct upf_gtp_dev *dev);

View File

@@ -25,34 +25,27 @@
#include <stdint.h>
#include <osmocom/core/socket.h>
#include <osmocom/upf/upf_tun.h>
struct upf_nft_tunmap_desc {
struct {
struct osmo_sockaddr gtp_local_addr;
uint32_t local_teid;
struct osmo_sockaddr gtp_remote_addr;
uint32_t remote_teid;
uint32_t chain_id;
} access;
struct {
struct osmo_sockaddr gtp_local_addr;
uint32_t local_teid;
struct osmo_sockaddr gtp_remote_addr;
uint32_t remote_teid;
uint32_t chain_id;
} core;
struct upf_nft_tun {
struct upf_tun tun;
uint32_t chain_id;
};
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_nft_tunmap_desc *tunmap);
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_nft_tunmap_desc *tunmap);
struct upf_tunmap {
struct upf_nft_tun access;
struct upf_nft_tun core;
};
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap);
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_tunmap *tunmap);
int upf_nft_init();
int upf_nft_free();
char *upf_nft_tunmap_get_table_init_str(void *ctx);
char *upf_nft_tunmap_get_vmap_init_str(void *ctx);
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_nft_tunmap_desc *tunmap);
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_nft_tunmap_desc *tunmap);
int upf_nft_tunmap_create(struct upf_nft_tunmap_desc *tunmap);
int upf_nft_tunmap_delete(struct upf_nft_tunmap_desc *tunmap);
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap);
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap);
int upf_nft_tunmap_create(struct upf_tunmap *tunmap);
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap);

View File

@@ -0,0 +1,38 @@
/*
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/socket.h>
struct upf_tun_ep {
struct osmo_sockaddr addr;
uint32_t teid;
};
struct upf_tun {
struct upf_tun_ep local;
struct upf_tun_ep remote;
};

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# (C) 2021-2022 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
# (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or

View File

@@ -10,8 +10,8 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOGTLV_CFLAGS) \
$(LIBOSMOPFCP_CFLAGS) \
$(LIBURING_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -19,21 +19,20 @@ AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGTLV_LIBS) \
$(LIBOSMOPFCP_LIBS) \
$(LIBURING_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_HEADERS = \
pfcp_tool.h \
$(NULL)
bin_PROGRAMS = \
osmo-pfcp-tool \
$(NULL)
osmo_pfcp_tool_SOURCES = \
checksum.c \
gtp_flood.c \
osmo_pfcp_tool_main.c \
pfcp_tool.c \
pfcp_tool_vty.c \
range.c \
$(NULL)

View File

@@ -0,0 +1,211 @@
/*
*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* IP/TCP/UDP checksumming routines
*
* Authors: Jorge Cwik, <jorge@laser.satlink.net>
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
* Tom May, <ftom@netcom.com>
* Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
* Lots of code moved from tcp.c and ip.c; see those files
* for more names.
*
* 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
* Fixed some nasty bugs, causing some horrible crashes.
* A: At some points, the sum (%0) was used as
* length-counter instead of the length counter
* (%1). Thanks to Roman Hodek for pointing this out.
* B: GCC seems to mess up if one uses too many
* data-registers to hold input values and one tries to
* specify d0 and d1 as scratch registers. Letting gcc
* choose these registers itself solves the problem.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
kills, so most of the assembly has to go. */
#if defined(__FreeBSD__)
#define _KERNEL /* needed on FreeBSD 10.x for s6_addr32 */
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/endian.h>
#endif
#include <osmocom/pfcptool/checksum.h>
#include <arpa/inet.h>
static inline unsigned short from32to16(unsigned int x)
{
/* add up 16-bit and 16-bit for 16+c bit */
x = (x & 0xffff) + (x >> 16);
/* add up carry.. */
x = (x & 0xffff) + (x >> 16);
return x;
}
static unsigned int do_csum(const unsigned char *buff, int len)
{
int odd;
unsigned int result = 0;
if (len <= 0)
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
#if BYTE_ORDER == LITTLE_ENDIAN
result += (*buff << 8);
#else
result = *buff;
#endif
len--;
buff++;
}
if (len >= 2) {
if (2 & (unsigned long) buff) {
result += *(unsigned short *) buff;
len -= 2;
buff += 2;
}
if (len >= 4) {
const unsigned char *end = buff + ((unsigned)len & ~3);
unsigned int carry = 0;
do {
unsigned int w = *(unsigned int *) buff;
buff += 4;
result += carry;
result += w;
carry = (w > result);
} while (buff < end);
result += carry;
result = (result & 0xffff) + (result >> 16);
}
if (len & 2) {
result += *(unsigned short *) buff;
buff += 2;
}
}
if (len & 1)
#if BYTE_ORDER == LITTLE_ENDIAN
result += *buff;
#else
result += (*buff << 8);
#endif
result = from32to16(result);
if (odd)
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
out:
return result;
}
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries.
*/
uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
{
return (uint16_t)~do_csum(iph, ihl*4);
}
/*
* computes the checksum of a memory block at buff, length len,
* and adds in "sum" (32-bit)
*
* returns a 32-bit number suitable for feeding into itself
* or csum_tcpudp_magic
*
* this function must be called with even lengths, except
* for the last fragment, which may be odd
*
* it's best to have buff aligned on a 32-bit boundary
*/
uint32_t csum_partial(const void *buff, int len, uint32_t wsum)
{
unsigned int sum = (unsigned int)wsum;
unsigned int result = do_csum(buff, len);
/* add in old sum, and carry.. */
result += sum;
if (sum > result)
result += 1;
return (uint32_t)result;
}
/*
* this routine is used for miscellaneous IP-like checksums, mainly
* in icmp.c
*/
uint16_t ip_compute_csum(const void *buff, int len)
{
return (uint16_t)~do_csum(buff, len);
}
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
uint32_t len, uint8_t proto, uint32_t csum)
{
int carry;
uint32_t ulen;
uint32_t uproto;
uint32_t sum = (uint32_t)csum;
sum += (uint32_t)saddr->s6_addr32[0];
carry = (sum < (uint32_t)saddr->s6_addr32[0]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[1];
carry = (sum < (uint32_t)saddr->s6_addr32[1]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[2];
carry = (sum < (uint32_t)saddr->s6_addr32[2]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[3];
carry = (sum < (uint32_t)saddr->s6_addr32[3]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[0];
carry = (sum < (uint32_t)daddr->s6_addr32[0]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[1];
carry = (sum < (uint32_t)daddr->s6_addr32[1]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[2];
carry = (sum < (uint32_t)daddr->s6_addr32[2]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[3];
carry = (sum < (uint32_t)daddr->s6_addr32[3]);
sum += carry;
ulen = (uint32_t)htonl((uint32_t) len);
sum += ulen;
carry = (sum < ulen);
sum += carry;
uproto = (uint32_t)htonl(proto);
sum += uproto;
carry = (sum < uproto);
sum += carry;
return csum_fold((uint32_t)sum);
}
/* fold a partial checksum */
uint16_t csum_fold(uint32_t csum)
{
uint32_t sum = (uint32_t)csum;
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}

View File

@@ -0,0 +1,689 @@
/* GTP-U traffic/load generator. Generates a configurable amount of UDP/IP flows using io_uring.
*
* Based on gtp-load-gen.c from https://gitea.osmocom.org/cellular-infrastructure/gtp-load-gen
* which is marked (C) 2021 by Harald Welte <laforge@osmocom.org>
*/
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <osmocom/pfcptool/gtp_flood.h>
#include <osmocom/core/logging.h>
#if HAVE_URING
#include <unistd.h>
#include <liburing.h>
#include <pthread.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/checksum.h>
#define PKT_BUF_SIZE 2048
struct gtp_flood_worker;
struct gtp_flood {
struct gtp_flood_cfg cfg;
/* list of struct gtp_flood_worker */
struct llist_head tx_workers;
/* state for round robin */
void *next_tx_worker;
int tx_workers_started;
int tx_workers_running;
/* list of struct gtp_flood_worker */
struct llist_head rx_workers;
void *next_rx_worker;
int rx_workers_started;
int rx_workers_running;
};
struct counter {
uint64_t count;
uint64_t last;
};
struct traffic_counter {
struct counter packets;
struct counter bytes;
};
uint64_t counter_get(struct counter *c)
{
uint64_t val = c->count - c->last;
c->last = c->count;
return val;
}
struct gtp_flood_worker {
struct llist_head entry;
/* backpointer */
struct gtp_flood *gtp_flood;
/* list of struct gtp_flood_flow */
struct llist_head flows;
struct io_uring ring;
pthread_t worker;
struct counter submitted_packets;
struct counter submitted_packets2;
struct traffic_counter tx;
struct traffic_counter rx;
};
struct gtp_flood_flow {
struct llist_head entry;
/* backpointer */
struct gtp_flood_worker *worker;
struct gtp_flood_flow_cfg cfg;
/* for logging and included in generated payloads */
unsigned int id;
/* must live until completion */
struct iovec iov[1];
struct msghdr msgh;
/* flow-private packet buffer */
uint8_t *pkt_buf;
unsigned int submitted_gtp_packets;
unsigned int sent_gtp_packets;
unsigned int received_udp_packets;
unsigned int received_udp_bytes;
bool stop;
};
static void gtp_flood_worker_init(struct gtp_flood *gtp_flood, struct gtp_flood_worker *worker)
{
worker->gtp_flood = gtp_flood;
INIT_LLIST_HEAD(&worker->flows);
}
struct gtp_flood *gtp_flood_alloc(void *ctx, const struct gtp_flood_cfg *cfg)
{
struct gtp_flood *gtp_flood;
struct gtp_flood_cfg *c;
int i;
gtp_flood = talloc_zero(ctx, struct gtp_flood);
*gtp_flood = (struct gtp_flood){
.cfg = *cfg,
};
INIT_LLIST_HEAD(&gtp_flood->tx_workers);
INIT_LLIST_HEAD(&gtp_flood->rx_workers);
c = &gtp_flood->cfg;
c->workers = OSMO_MAX(1, c->workers);
for (i = 0; i < c->workers; i++) {
struct gtp_flood_worker *w = talloc_zero(gtp_flood, struct gtp_flood_worker);
gtp_flood_worker_init(gtp_flood, w);
llist_add(&w->entry, &gtp_flood->tx_workers);
}
LOGP(DLGLOBAL, LOGL_NOTICE, "tx workers: %u\n", llist_count(&gtp_flood->tx_workers));
for (i = 0; i < 1; i++) {
struct gtp_flood_worker *w = talloc_zero(gtp_flood, struct gtp_flood_worker);
gtp_flood_worker_init(gtp_flood, w);
llist_add(&w->entry, &gtp_flood->rx_workers);
}
LOGP(DLGLOBAL, LOGL_NOTICE, "rx workers: %u\n", llist_count(&gtp_flood->rx_workers));
return gtp_flood;
}
static void gtp_flood_flow_init_payload(struct gtp_flood_flow *flow)
{
struct gtp1u_hdr *gtp_hdr = (void *)flow->pkt_buf;
*gtp_hdr = (struct gtp1u_hdr) {
.pn = 0,
.s = 0,
.e = 0,
.spare = 0,
.pt = 1,
.version = 1,
.type = 0xff, /* G-PDU */
.length = 0, /* filled in later */
.tei = htonl(flow->cfg.gtp_remote_teid),
};
uint8_t *cur = flow->pkt_buf + sizeof(*gtp_hdr);
/* FIXME: randomize this */
unsigned int udp_len = 1024;
struct iphdr *iph;
struct ip6_hdr *ip6h;
struct udphdr *uh;
struct osmo_sockaddr *src = &flow->cfg.payload_src;
struct osmo_sockaddr *dst = &flow->cfg.payload_dst;
if (src->u.sa.sa_family == AF_INET) {
iph = (struct iphdr *) cur;
cur += sizeof(*iph);
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(udp_len + sizeof(struct udphdr) + sizeof(*iph));
iph->id = 0;
iph->frag_off = 0;
iph->ttl = 32;
iph->protocol = IPPROTO_UDP;
iph->saddr = src->u.sin.sin_addr.s_addr;
iph->daddr = dst->u.sin.sin_addr.s_addr;
iph->check = ip_fast_csum(iph, iph->ihl);
} else {
ip6h = (struct ip6_hdr *) cur;
cur += sizeof(*ip6h);
ip6h->ip6_flow = htonl((6 << 28));
ip6h->ip6_plen = htons(udp_len + sizeof(struct udphdr));
ip6h->ip6_nxt = IPPROTO_UDP;
ip6h->ip6_hlim = 32;
ip6h->ip6_src = src->u.sin6.sin6_addr;
ip6h->ip6_dst = dst->u.sin6.sin6_addr;
}
uh = (struct udphdr *) cur;
cur += sizeof(*uh);
uh->source = htons(osmo_sockaddr_port(&src->u.sa));
uh->dest = htons(osmo_sockaddr_port(&dst->u.sa));
uh->len = htons(udp_len);
uh->check = 0; // TODO
gtp_hdr->length = htons(udp_len + (cur - flow->pkt_buf) - sizeof(*gtp_hdr));
/* initialize this once, so we have it ready for each transmit */
flow->msgh.msg_name = &flow->cfg.gtp_remote.u.sa;
flow->msgh.msg_namelen = sizeof(flow->cfg.gtp_remote.u.sa);
flow->msgh.msg_iov = flow->iov;
flow->msgh.msg_iovlen = ARRAY_SIZE(flow->iov);
flow->msgh.msg_control = NULL;
flow->msgh.msg_controllen = 0;
flow->msgh.msg_flags = 0;
struct iovec *iov = &flow->iov[0];
iov->iov_base = flow->pkt_buf;
iov->iov_len = udp_len + (cur - flow->pkt_buf);
OSMO_ASSERT(iov->iov_len <= PKT_BUF_SIZE);
/* write some payload */
struct osmo_strbuf sb = { .buf = (void *)cur, .len = udp_len };
OSMO_STRBUF_PRINTF(sb, "osmo-pfcp-tool gtp flood, emitted from %s to %s teid 0x%08x flow %u\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_local->osa),
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_remote),
flow->cfg.gtp_remote_teid,
flow->id);
if (flow->cfg.append_payload_info) {
if (iov->iov_len < (sizeof(flow->cfg.payload_info) + 1)) {
OSMO_ASSERT(PKT_BUF_SIZE > sizeof(flow->cfg.payload_info));
iov->iov_len = sizeof(flow->cfg.payload_info) + 1;
}
uint8_t *len = ((uint8_t *)iov->iov_base) + iov->iov_len - 1;
struct gtp_flood_payload_info *info = (void *)(len - sizeof(flow->cfg.payload_info));
*len = sizeof(flow->cfg.payload_info);
*info = flow->cfg.payload_info;
memcpy(info->mark, "info", 4);
}
}
static void gtp_flood_flow_init_rxbuf(struct gtp_flood_flow *flow)
{
flow->msgh.msg_iov = flow->iov;
flow->msgh.msg_iovlen = ARRAY_SIZE(flow->iov);
flow->msgh.msg_control = NULL;
flow->msgh.msg_controllen = 0;
flow->msgh.msg_flags = 0;
flow->iov[0].iov_base = flow->pkt_buf;
flow->iov[0].iov_len = PKT_BUF_SIZE;
}
#define llist_round_robin(LIST, STATE, STRUCT, ENTRY_NAME) \
llist_entry(_llist_round_robin(LIST, STATE), STRUCT, ENTRY_NAME)
struct llist_head *_llist_round_robin(struct llist_head *list, void **state)
{
struct llist_head *e = *state;
if (!e || e->next == list)
e = list;
e = e->next;
if (e == list)
e = NULL;
*state = e;
return e;
}
struct gtp_flood_worker *gtp_flood_next_tx_worker(struct gtp_flood *gtp_flood)
{
return llist_round_robin(&gtp_flood->tx_workers, &gtp_flood->next_tx_worker,
struct gtp_flood_worker, entry);
}
struct gtp_flood_worker *gtp_flood_next_rx_worker(struct gtp_flood *gtp_flood)
{
return llist_round_robin(&gtp_flood->rx_workers, &gtp_flood->next_rx_worker,
struct gtp_flood_worker, entry);
}
void gtp_flood_worker_add_flow(struct gtp_flood *gtp_flood,
struct gtp_flood_worker *worker,
const struct gtp_flood_flow_cfg *flow_cfg)
{
static unsigned int next_flow_id = 0;
struct gtp_flood_flow *flow = talloc_zero(gtp_flood, struct gtp_flood_flow);
flow->cfg = *flow_cfg;
flow->id = next_flow_id++;
flow->pkt_buf = talloc_zero_size(flow, PKT_BUF_SIZE);
OSMO_ASSERT(flow->pkt_buf);
flow->worker = worker;
if (flow->cfg.rx == false)
gtp_flood_flow_init_payload(flow);
else
gtp_flood_flow_init_rxbuf(flow);
llist_add_tail(&flow->entry, &worker->flows);
}
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
const struct gtp_flood_flow_cfg *flow_cfg)
{
struct gtp_flood_worker *w;
if (flow_cfg->rx == false)
w = gtp_flood_next_tx_worker(gtp_flood);
else
w = gtp_flood_next_rx_worker(gtp_flood);
gtp_flood_worker_add_flow(gtp_flood, w, flow_cfg);
}
/* transmit one packet for a given flow */
static bool gtp_flow_tx_one(struct gtp_flood_flow *flow)
{
struct gtp_flood_worker *worker = flow->worker;
struct io_uring_sqe *sqe;
sqe = io_uring_get_sqe(&worker->ring);
if (!sqe)
return false;
io_uring_prep_sendmsg(sqe, flow->cfg.gtp_local->ofd.fd, &flow->msgh, 0);
io_uring_sqe_set_data(sqe, flow);
return true;
}
static void tx_completion(struct gtp_flood_worker *worker, struct io_uring_cqe *cqe, int *tx_flows_ended)
{
struct gtp_flood_flow *flow;
flow = io_uring_cqe_get_data(cqe);
if (cqe->res >= 0) {
flow->sent_gtp_packets++;
worker->tx.packets.count++;
worker->tx.bytes.count += cqe->res;
if (flow->cfg.num_packets
&& flow->sent_gtp_packets >= flow->cfg.num_packets) {
flow->stop = true;
(*tx_flows_ended)++;
}
} else {
flow->submitted_gtp_packets--;
}
io_uring_cqe_seen(&worker->ring, cqe);
}
static void *gtp_flood_tx_worker_thread(void *_worker)
{
struct gtp_flood_worker *worker = (struct gtp_flood_worker *)_worker;
struct gtp_flood *gtp_flood = worker->gtp_flood;
osmo_ctx_init(__func__);
int worker_id = gtp_flood->tx_workers_started++;
gtp_flood->tx_workers_running++;
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood tx worker %d starting (%u started, %u running)\n",
worker_id,
gtp_flood->tx_workers_started,
gtp_flood->tx_workers_running);
int tx_flows_count = llist_count(&worker->flows);
int tx_flows_ended = 0;
int num_submitted_total = 0;
int num_submitted2_total = 0;
struct __kernel_timespec ts_zero = {};
struct __kernel_timespec ts_timeout = { .tv_nsec = 0.5e9 };
while (tx_flows_ended < tx_flows_count) {
uint32_t num_submitted = 0;
int num_submitted2;
if (gtp_flood->cfg.slew_us)
usleep(gtp_flood->cfg.slew_us);
/* fill up sqe with transmit submissions */
bool keep_submitting = true;
while (keep_submitting) {
int submitted_was = num_submitted;
struct gtp_flood_flow *flow;
llist_for_each_entry (flow, &worker->flows, entry) {
if (flow->stop)
continue;
if (flow->cfg.num_packets
&& flow->submitted_gtp_packets >= flow->cfg.num_packets)
continue;
if (gtp_flow_tx_one(flow)) {
flow->submitted_gtp_packets++;
num_submitted++;
worker->submitted_packets.count++;
} else {
/* out of sqe. */
keep_submitting = false;
break;
}
}
/* No change in number of submitted PDUs, all flows are done submitting for this round. */
if (submitted_was == num_submitted)
keep_submitting = false;
}
/* actually submit */
num_submitted2 = io_uring_submit(&worker->ring);
worker->submitted_packets2.count += num_submitted2;
/* process all pending completions */
int completed = 0;
struct io_uring_cqe *cqe;
while (io_uring_wait_cqe_timeout(&worker->ring, &cqe, &ts_zero) == 0) {
tx_completion(worker, cqe, &tx_flows_ended);
completed++;
}
/* periodically log tx stats */
if (0) {
static time_t last_info_log = 0;
time_t now;
time_t diff_time;
now = time(NULL);
if (!last_info_log)
diff_time = 1;
else
diff_time = now - last_info_log;
/* the resolution is in seconds, output stats once per second. */
if (diff_time > 0) {
last_info_log = now;
uint64_t diff_tx_packets;
uint64_t diff_tx_bytes;
diff_tx_packets = counter_get(&worker->tx.packets);
diff_tx_bytes = counter_get(&worker->tx.bytes);
if (diff_tx_packets) {
LOGP(DLGLOBAL, LOGL_INFO,
"%d: tx %"PRIu64" packets %"PRIu64"Mbyte (%"PRIu64"Mbyte/s)"
" (pending %"PRIu64", %"PRIu64")\n",
worker_id,
worker->tx.packets.count, worker->tx.bytes.count / (1024*1024),
(diff_tx_bytes / diff_time) / (1024*1024),
worker->submitted_packets.count - worker->submitted_packets2.count,
worker->submitted_packets2.count - worker->tx.packets.count
);
/* mark that something happened to not enter the wait below */
completed++;
}
}
}
if (!num_submitted2 && !completed) {
/* There are currently no slots available for submitting more packets, wait until the next slot
* becomes available. After a timeout, re-check whether the worker should exit. */
if (io_uring_wait_cqe_timeout(&worker->ring, &cqe, &ts_timeout) == 0) {
tx_completion(worker, cqe, &tx_flows_ended);
completed++;
}
}
num_submitted_total += num_submitted;
if (num_submitted2 > 0)
num_submitted2_total += num_submitted2;
}
gtp_flood->tx_workers_running--;
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood tx worker %d done (%u started, %u running) (%"PRIu64" packets not submitted)\n",
worker_id,
gtp_flood->tx_workers_started,
gtp_flood->tx_workers_running,
worker->submitted_packets.count - worker->tx.packets.count);
return NULL;
}
/* receive one packet for a given flow */
static bool gtp_flow_rx_one(struct gtp_flood_flow *flow)
{
struct gtp_flood_worker *worker = flow->worker;
struct io_uring_sqe *sqe;
sqe = io_uring_get_sqe(&worker->ring);
if (!sqe)
return false;
io_uring_prep_recvmsg(sqe, flow->cfg.gtp_local->ofd.fd, &flow->msgh, 0);
io_uring_sqe_set_data(sqe, flow);
return true;
}
void rx_completion(struct gtp_flood_worker *worker, struct io_uring_cqe *cqe)
{
int len = cqe->res;
struct gtp_flood_flow *flow;
flow = io_uring_cqe_get_data(cqe);
if (len > 0) {
flow->received_udp_packets++;
flow->received_udp_bytes += len;
worker->rx.packets.count++;
worker->rx.bytes.count += len;
}
io_uring_cqe_seen(&worker->ring, cqe);
/* reschedule */
gtp_flow_rx_one(flow);
}
static void *gtp_flood_rx_worker_thread(void *_worker)
{
struct gtp_flood_worker *worker = (struct gtp_flood_worker *)_worker;
struct gtp_flood *gtp_flood = worker->gtp_flood;
osmo_ctx_init(__func__);
int worker_id = gtp_flood->rx_workers_started++;
gtp_flood->rx_workers_running++;
LOGP(DLGLOBAL, LOGL_INFO, "gtp rx worker starting (%u started, %u running)\n",
gtp_flood->rx_workers_started,
gtp_flood->rx_workers_running);
struct gtp_flood_flow *flow;
void *next_flow = NULL;
struct __kernel_timespec ts_zero = {};
struct __kernel_timespec ts_1s = { .tv_sec = 1 };
/* submit all rx flows N times until the queue is full */
do {
flow = llist_round_robin(&worker->flows, &next_flow, struct gtp_flood_flow, entry);
} while (gtp_flow_rx_one(flow));
/* service completions and resubmit sqe */
while (1) {
struct io_uring_cqe *cqe;
int submitted;
int completed = 0;
//usleep(1000);
/* submit batch of pending reads */
submitted = io_uring_submit(&worker->ring);
/* process all pending completions */
while (io_uring_wait_cqe_timeout(&worker->ring, &cqe, &ts_zero) == 0) {
rx_completion(worker, cqe);
completed++;
}
/* periodically log rx stats */
if (1) {
static time_t last_info_log = 0;
time_t now;
time_t diff_time;
now = time(NULL);
if (!last_info_log)
diff_time = 1;
else
diff_time = now - last_info_log;
/* the resolution is in seconds, output stats once per second. */
if (diff_time > 0) {
last_info_log = now;
uint64_t diff_rx_packets;
uint64_t diff_rx_bytes;
diff_rx_packets = counter_get(&worker->rx.packets);
diff_rx_bytes = counter_get(&worker->rx.bytes);
uint64_t count_tx_packets = 0;
uint64_t count_tx_bytes = 0;
uint64_t diff_tx_packets = 0;
uint64_t diff_tx_bytes = 0;
struct gtp_flood_worker *tx_w;
llist_for_each_entry (tx_w, &gtp_flood->tx_workers, entry) {
count_tx_packets += tx_w->tx.packets.count;
count_tx_bytes += tx_w->tx.bytes.count;
diff_tx_packets += counter_get(&tx_w->tx.packets);
diff_tx_bytes += counter_get(&tx_w->tx.bytes);
}
if (diff_rx_packets) {
LOGP(DLGLOBAL, LOGL_INFO,
"%d: tx %"PRIu64" packets %"PRIu64"Mbyte (%"PRIu64"Mbyte/s)"
" pending %"PRIu64
" rx %"PRIu64" packets %"PRIu64"Mbyte (%"PRIu64"Mbyte/s)\n",
worker_id,
count_tx_packets, count_tx_bytes / (1024*1024),
(diff_tx_bytes / diff_time) / (1024*1024),
count_tx_packets - worker->rx.packets.count,
worker->rx.packets.count, worker->rx.bytes.count / (1024*1024),
(diff_rx_bytes / diff_time) / (1024*1024));
/* mark that something happened to not enter the wait below */
completed++;
}
}
}
/* Nothing happened in this loop, wait for the next event */
if (!submitted && !completed) {
if (io_uring_wait_cqe_timeout(&worker->ring, &cqe, &ts_1s) == 0) {
rx_completion(worker, cqe);
completed++;
}
}
}
gtp_flood->rx_workers_running--;
return NULL;
}
static void gtp_flood_worker_start(struct gtp_flood_worker *worker,
void *(*worker_func)(void *))
{
int rc;
rc = io_uring_queue_init(worker->gtp_flood->cfg.queue_size, &worker->ring, 0);
OSMO_ASSERT(rc >= 0);
rc = pthread_create(&worker->worker, NULL, worker_func, worker);
OSMO_ASSERT(rc >= 0);
}
void gtp_flood_start(struct gtp_flood *gtp_flood)
{
struct gtp_flood_worker *w;
llist_for_each_entry (w, &gtp_flood->tx_workers, entry)
gtp_flood_worker_start(w, gtp_flood_tx_worker_thread);
llist_for_each_entry (w, &gtp_flood->rx_workers, entry)
gtp_flood_worker_start(w, gtp_flood_rx_worker_thread);
}
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood)
{
if (!gtp_flood)
return false;
return gtp_flood->tx_workers_started && gtp_flood->tx_workers_running;
}
#else /* HAVE_URING */
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers)
{
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot start GTP flood: built without liburing support\n");
return NULL;
}
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
const struct gtp_flood_flow_cfg *flow_cfg)
{
}
void gtp_flood_start(struct gtp_flood *gtp_flood)
{
}
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood)
{
return false;
}
#endif /* HAVE_URING */

View File

@@ -42,7 +42,8 @@
#include <osmocom/pfcp/pfcp_endpoint.h>
#include "pfcp_tool.h"
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/gtp_flood.h>
#define _GNU_SOURCE
#include <getopt.h>
@@ -206,7 +207,6 @@ static void signal_handler(int signum)
}
}
static struct vty_app_info pfcp_tool_vty_app_info = {
.name = "osmo-pfcp-tool",
.version = PACKAGE_VERSION,
@@ -216,6 +216,7 @@ static struct vty_app_info pfcp_tool_vty_app_info = {
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n",
.go_parent_cb = pfcp_tool_vty_go_parent,
};
static const struct log_info_cat pfcp_tool_default_categories[] = {
@@ -226,14 +227,15 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(pfcp_tool_default_categories),
};
int pfcp_tool_mainloop()
int pfcp_tool_mainloop(int poll)
{
int rc;
log_reset_context();
osmo_select_main_ctx(0);
rc = osmo_select_main_ctx(poll);
/* If the user hits Ctrl-C the third time, just terminate immediately. */
if (quit >= 3)
return 1;
return -1;
/* Has SIGTERM been received (and not yet been handled)? */
if (quit && !osmo_select_shutdown_requested()) {
@@ -243,7 +245,7 @@ int pfcp_tool_mainloop()
osmo_select_shutdown_request();
/* continue the main select loop until all write queues are serviced. */
}
return 0;
return rc;
}
int main(int argc, char **argv)
@@ -326,7 +328,7 @@ int main(int argc, char **argv)
}
}
pfcp_tool_mainloop();
pfcp_tool_mainloop(1);
pfcp_tool_vty_init_cmds();
@@ -338,17 +340,18 @@ int main(int argc, char **argv)
pfcp_tool_cmdline_config.command_file);
return 1;
}
printf("Done reading '%s', waiting for retransmission queue...\n",
printf("Done reading '%s', waiting for tasks to conclude...\n",
pfcp_tool_cmdline_config.command_file);
do {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
} while (osmo_pfcp_endpoint_retrans_queue_is_busy(g_pfcp_tool->ep));
printf("Done\n");
} while (osmo_pfcp_endpoint_retrans_queue_is_busy(g_pfcp_tool->ep)
|| gtp_flood_is_busy(g_pfcp_tool->gtp.flood.state));
LOGP(DLGLOBAL, LOGL_NOTICE, "Done\n");
} else {
printf("Listening for commands on VTY...\n");
do {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
} while (!osmo_select_shutdown_done());
}

View File

@@ -26,7 +26,8 @@
#include <osmocom/pfcp/pfcp_endpoint.h>
#include "pfcp_tool.h"
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/gtp_flood.h>
struct g_pfcp_tool *g_pfcp_tool = NULL;
@@ -45,9 +46,21 @@ void g_pfcp_tool_alloc(void *ctx)
.local_ip = talloc_strdup(g_pfcp_tool, "0.0.0.0"),
.local_port = OSMO_PFCP_PORT,
},
.next_teid_state = 23,
.gtp.flood = {
.cfg = {
.workers = 1,
.queue_size = 4096,
.slew_us = 0,
},
.flows_per_session = 1,
.packets_per_flow = 0,
},
};
INIT_LLIST_HEAD(&g_pfcp_tool->peers);
INIT_LLIST_HEAD(&g_pfcp_tool->gtp.gtp_local_addrs);
INIT_LLIST_HEAD(&g_pfcp_tool->gtp.gtp_core_addrs);
}
struct pfcp_tool_peer *pfcp_tool_peer_find(const struct osmo_sockaddr *remote_addr)
@@ -176,6 +189,8 @@ int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m)
else
copy_msg(&peer->last_req, m);
rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m);
if (rc)
LOGP(DLPFCP, LOGL_ERROR, "Failed to transmit PFCP: %s\n", strerror(-rc));
return rc;
}
@@ -183,3 +198,217 @@ uint64_t peer_new_seid(struct pfcp_tool_peer *peer)
{
return peer->next_seid_state++;
}
uint32_t pfcp_tool_new_teid(void)
{
return g_pfcp_tool->next_teid_state++;
}
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst)
{
range_next(&g_pfcp_tool->ue.ip_range);
if (g_pfcp_tool->ue.ip_range.wrapped) {
LOGP(DLGLOBAL, LOGL_ERROR, "insufficient UE IP addresses, wrapped back to first\n");
g_pfcp_tool->ue.ip_range.wrapped = false;
}
range_val_get_addr(dst, &g_pfcp_tool->ue.ip_range.next);
return 0;
}
struct udp_port *pfcp_tool_have_udp_port_by_osa(struct llist_head *list, const struct osmo_sockaddr *_osa,
uint16_t fallback_port)
{
struct udp_port *port;
/* copy osa and have a non-const pointer */
struct osmo_sockaddr osa_mutable = *_osa;
struct osmo_sockaddr *osa = &osa_mutable;
if (!osmo_sockaddr_port(&osa->u.sa))
osmo_sockaddr_set_port(&osa->u.sa, fallback_port);
OSMO_ASSERT(osmo_sockaddr_port(&osa->u.sa));
llist_for_each_entry (port, list, entry) {
if (osmo_sockaddr_cmp(&port->osa, osa) == 0)
return port;
}
port = talloc_zero(g_pfcp_tool, struct udp_port);
port->osa = *osa;
port->ofd.fd = -1;
llist_add_tail(&port->entry, list);
return port;
}
struct udp_port *pfcp_tool_have_udp_port_by_str(struct llist_head *list, const struct osmo_sockaddr_str *addr,
uint16_t fallback_port)
{
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_to_osa(addr, &osa))
return NULL;
return pfcp_tool_have_udp_port_by_osa(list, &osa, fallback_port);
}
struct udp_port *pfcp_tool_have_local_udp_port_by_osa(const struct osmo_sockaddr *osa, uint16_t fallback_port)
{
struct udp_port *port = pfcp_tool_have_udp_port_by_osa(&g_pfcp_tool->gtp.gtp_local_addrs, osa, fallback_port);
/* already bound? */
if (port->ofd.fd >= 0)
return port;
/* create and bind socket */
int rc;
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &port->osa, NULL, OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Failed to bind socket on UDP %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa));
exit(1);
}
port->ofd.fd = rc;
#if 0
int a_lot;
a_lot = 1024 * (1024*1024);
rc = setsockopt(port->ofd.fd, SOL_SOCKET, SO_SNDBUF, &a_lot, sizeof(a_lot));
a_lot = 1024 * (1024*1024);
rc = setsockopt(port->ofd.fd, SOL_SOCKET, SO_RCVBUF, &a_lot, sizeof(a_lot));
#endif
LOGP(DLGLOBAL, LOGL_NOTICE, "bound UDP %s fd=%d\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa), rc);
return port;
}
struct udp_port *pfcp_tool_have_local_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port)
{
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_to_osa(addr, &osa))
return NULL;
return pfcp_tool_have_local_udp_port_by_osa(&osa, fallback_port);
}
struct udp_port *pfcp_tool_have_core_udp_port_by_osa(const struct osmo_sockaddr *osa, uint16_t fallback_port)
{
return pfcp_tool_have_udp_port_by_osa(&g_pfcp_tool->gtp.gtp_core_addrs, osa, fallback_port);
}
struct udp_port *pfcp_tool_have_core_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port)
{
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_to_osa(addr, &osa))
return NULL;
return pfcp_tool_have_core_udp_port_by_osa(&osa, fallback_port);
}
static bool add_session_to_gtp_flow(struct gtp_flood *gf, struct pfcp_tool_session *session)
{
struct range *r;
struct gtp_flood_flow_cfg cfg = {};
const struct pfcp_tool_gtp_tun *tun_access;
const struct pfcp_tool_gtp_tun *tun_core = NULL;
const struct osmo_sockaddr_str *ue_local_addr = NULL;
switch (session->kind) {
case UP_GTP_U_TUNEND:
tun_access = &session->tunend.access;
ue_local_addr = &session->tunend.core.ue_local_addr;
break;
case UP_GTP_U_TUNMAP:
tun_access = &session->tunmap.access;
tun_core = &session->tunmap.core;
break;
default:
OSMO_ASSERT(false);
}
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
* of view: so the GTP port of osmo-pfcp-tool is 'remote'.
* Here we are setting up a port to emit GTP from osmo-pfcp-tool, the same port is called 'gtp_local'.
*/
cfg.gtp_local = pfcp_tool_have_local_udp_port_by_str(&tun_access->remote.addr, 2152);
if (!cfg.gtp_local) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to setup local GTP port\n");
return false;
}
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
* of view: so the GTP port of UPF is 'local'.
* Here we are reading the GTP port that the UPF has opened to receive GTP traffic; the same port is called
* 'gtp_remote' in cfg, which is remote from osmo-pfcp-tool's point of view.
*/
if (osmo_sockaddr_str_to_osa(&tun_access->local.addr, &cfg.gtp_remote)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UPF's GTP port\n");
return false;
}
if (!osmo_sockaddr_port(&cfg.gtp_remote.u.sa))
osmo_sockaddr_set_port(&cfg.gtp_remote.u.sa, 2152);
cfg.gtp_remote_teid = tun_access->local.teid;
if (ue_local_addr) {
osmo_sockaddr_str_to_osa(ue_local_addr, &cfg.payload_src);
} else if (pfcp_tool_next_ue_addr(&cfg.payload_src)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UE's IP address\n");
return false;
}
r = &g_pfcp_tool->gtp.payload.source.udp_port_range;
range_next(r);
osmo_sockaddr_set_port(&cfg.payload_src.u.sa, range_val_get_int(&r->next));
r = &g_pfcp_tool->gtp.payload.target.ip_range;
range_next(r);
range_val_get_addr(&cfg.payload_dst, &r->next);
r = &g_pfcp_tool->gtp.payload.target.udp_port_range;
range_next(r);
osmo_sockaddr_set_port(&cfg.payload_dst.u.sa, range_val_get_int(&r->next));
cfg.num_packets = g_pfcp_tool->gtp.flood.packets_per_flow;
if (g_pfcp_tool->gtp.payload.append_info) {
cfg.append_payload_info = true;
cfg.payload_info = (struct gtp_flood_payload_info){};
if (tun_core)
cfg.payload_info.return_teid = htonl(tun_core->local.teid);
}
for (int i = 0; i < g_pfcp_tool->gtp.flood.flows_per_session; i++)
gtp_flood_add_flow(gf, &cfg);
return true;
}
void pfcp_tool_gtp_flood_start(void)
{
struct gtp_flood *gf;
struct pfcp_tool_peer *peer;
struct udp_port *port;
if (g_pfcp_tool->gtp.flood.state) {
LOGP(DLGLOBAL, LOGL_ERROR,
"another 'gtp flood' is still running; currently we can run only one gtp flood at a time\n");
return;
}
gf = g_pfcp_tool->gtp.flood.state = gtp_flood_alloc(g_pfcp_tool, &g_pfcp_tool->gtp.flood.cfg);
if (!gf)
return;
/* add transmitter flows */
llist_for_each_entry (peer, &g_pfcp_tool->peers, entry) {
struct pfcp_tool_session *session;
llist_for_each_entry (session, &peer->sessions, entry) {
add_session_to_gtp_flow(gf, session);
}
}
/* add listener flows, one per local UDP port (like a GTP port from a 'gtp local 1.2.3.4' vty command) */
llist_for_each_entry (port, &g_pfcp_tool->gtp.gtp_local_addrs, entry) {
struct gtp_flood_flow_cfg cfg = {
.rx = true,
.gtp_local = port,
};
gtp_flood_add_flow(gf, &cfg);
}
gtp_flood_start(gf);
}

View File

@@ -34,12 +34,7 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include "pfcp_tool.h"
enum pfcp_tool_vty_node {
PEER_NODE = _LAST_OSMOVTY_NODE + 1,
SESSION_NODE,
};
#include <osmocom/pfcptool/pfcp_tool.h>
DEFUN(c_local_addr, c_local_addr_cmd,
"local-addr IP_ADDR",
@@ -97,9 +92,9 @@ DEFUN(c_listen, c_listen_cmd,
rc = osmo_pfcp_endpoint_bind(g_pfcp_tool->ep);
if (rc) {
vty_out(vty, "Failed to bind PFCP endpoint on %s: %s%s\n",
vty_out(vty, "Failed to bind PFCP endpoint on %s: %s%s",
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep)),
strerror(rc), VTY_NEWLINE);
strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
@@ -108,7 +103,7 @@ DEFUN(c_listen, c_listen_cmd,
DEFUN(c_sleep, c_sleep_cmd,
"sleep <0-999999> [<0-999>]",
"Let some time pass\n"
"Seconds to wait\n")
"Seconds to wait\n" "Additional milliseconds to wait\n")
{
int secs = atoi(argv[0]);
int msecs = 0;
@@ -124,7 +119,7 @@ DEFUN(c_sleep, c_sleep_cmd,
/* Still operate the message pump while waiting for time to pass */
while (t.active && !osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
}
@@ -134,6 +129,251 @@ DEFUN(c_sleep, c_sleep_cmd,
return CMD_SUCCESS;
}
DEFUN(c_date, c_date_cmd,
"date",
"print a timestamp\n")
{
struct timeval tv = {};
struct tm tm = {};
osmo_gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
vty_out(vty, "%04d-%02d-%02d,%02d:%02d:%02d.%03d%s",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000), VTY_NEWLINE);
vty_flush(vty);
return CMD_SUCCESS;
}
static bool parse_ip_to_range_val(struct vty *vty, struct range_val *val, const char *ip_addr_str)
{
struct osmo_sockaddr_str a;
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_from_str(&a, ip_addr_str, 0)
|| osmo_sockaddr_str_to_osa(&a, &osa)) {
vty_out(vty, "%% Error: invalid IP address: '%s'%s", ip_addr_str, VTY_NEWLINE);
return false;
}
range_val_set_addr(val, &osa);
return true;
}
static bool parse_ip_range(struct vty *vty, struct range *dst, const char *argv[])
{
return parse_ip_to_range_val(vty, &dst->first, argv[0])
&& parse_ip_to_range_val(vty, &dst->last, argv[1]);
}
#define IP_RANGE_STR \
"Set a range with first and last address\n" \
"First IP address in the range, 1.2.3.4 or 1:2:3::4\n" \
"Last IP address in the range, 1.2.3.4 or 1:2:3::4\n"
DEFUN(c_ue_ip_range, c_ue_ip_range_cmd,
"ue ip range IP_ADDR_FIRST IP_ADDR_LAST",
"UE\n"
"Set the IP address range used for the UE IP addresses\n"
IP_RANGE_STR)
{
if (!parse_ip_range(vty, &g_pfcp_tool->ue.ip_range, &argv[0]))
return CMD_WARNING;
return CMD_SUCCESS;
}
#define GTP_STR "Configure GTP\n"
#define GTP_IP_STR "Add a GTP IP address\n"
#define IP_ADDR_STR "IP address, 1.2.3.4 or 1:2:3:4::1\n"
DEFUN(c_gtp_local, c_gtp_local_cmd,
"gtp local IP_ADDR [<1-65535>]",
GTP_STR
"Add a local GTP port, to emit GTP traffic from\n"
IP_ADDR_STR
"Specific UDP port to use, 2152 if omitted\n")
{
const char *ip_str = argv[0];
uint16_t port_nr;
struct osmo_sockaddr_str addr_str;
struct udp_port *p;
if (argc > 1)
port_nr = atoi(argv[1]);
else
port_nr = 2152;
if (osmo_sockaddr_str_from_str(&addr_str, ip_str, port_nr))
goto invalid_ip;
vty_out(vty, "local GTP port: " OSMO_SOCKADDR_STR_FMT "%s", OSMO_SOCKADDR_STR_FMT_ARGS(&addr_str), VTY_NEWLINE);
p = pfcp_tool_have_local_udp_port_by_str(&addr_str, port_nr);
if (!p)
goto invalid_ip;
return CMD_SUCCESS;
invalid_ip:
vty_out(vty, "%% Error: invalid IP address: '%s'%s", ip_str, VTY_NEWLINE);
return CMD_WARNING;
}
DEFUN(c_gtp_core, c_gtp_core_cmd,
"gtp core IP_ADDR [<1-65535>]",
GTP_STR
"Add a core GTP port, to emit GTP traffic from\n"
IP_ADDR_STR
"Specific UDP port to use, 2152 if omitted\n")
{
const char *ip_str = argv[0];
uint16_t port_nr;
struct osmo_sockaddr_str addr_str;
struct udp_port *p;
if (argc > 1)
port_nr = atoi(argv[1]);
else
port_nr = 2152;
if (osmo_sockaddr_str_from_str(&addr_str, ip_str, port_nr))
goto invalid_ip;
vty_out(vty, "core GTP port: " OSMO_SOCKADDR_STR_FMT "%s", OSMO_SOCKADDR_STR_FMT_ARGS(&addr_str), VTY_NEWLINE);
p = pfcp_tool_have_core_udp_port_by_str(&addr_str, port_nr);
if (!p)
goto invalid_ip;
return CMD_SUCCESS;
invalid_ip:
vty_out(vty, "%% Error: invalid IP address: '%s'%s", ip_str, VTY_NEWLINE);
return CMD_WARNING;
}
static struct cmd_node gtp_flood_node = {
GTP_FLOOD_NODE,
"%s(gtp-flood)# ",
1,
};
DEFUN(gtp_flood, gtp_flood_cmd,
"gtp flood",
GTP_STR
"Setup GTP traffic\n")
{
vty->node = GTP_FLOOD_NODE;
return CMD_SUCCESS;
}
DEFUN(gtpf_workers, gtpf_workers_cmd,
"workers <1-999>",
"Number of worker threads to launch, to emit GTP traffic from\n"
"Number\n")
{
g_pfcp_tool->gtp.flood.cfg.workers = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(gtpf_io_uring_queue_size, gtpf_io_uring_queue_size_cmd,
"io-uring queue-size <1-65536>",
"Fine-tune io-uring usage for optimal GTP packet flooding\n"
"Maximum number of packets to submit for sending simultaneously.\n"
"Number\n")
{
g_pfcp_tool->gtp.flood.cfg.queue_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(gtpf_flows, gtpf_flows_cmd,
"flows-per-session <1-999999>",
"Set number of GTP packet flows emitted per PFCP session's GTP tunnel\n"
"Number\n")
{
g_pfcp_tool->gtp.flood.flows_per_session = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(gtpf_packets, gtpf_packets_cmd,
"packets-per-flow (<1-2000000000>|infinite)",
"Set number of GTP packet flows emitted per PFCP session's GTP tunnel\n"
"Number\n")
{
unsigned int val;
if (!strcmp("infinite", argv[0]))
val = 0;
else
val = atoi(argv[0]);
g_pfcp_tool->gtp.flood.packets_per_flow = val;
return CMD_SUCCESS;
}
#define PAYLOAD_STR "Configure GTP payload\n"
#define RANGE_STR \
"Set a range to cycle through\n" \
"First value of the range\n" \
"Last value of the range\n"
DEFUN(gtpf_payload_src_port, gtpf_payload_src_port_cmd,
"payload source port udp range <1-65535> <1-65535>",
PAYLOAD_STR
"Source port of payload packets. Note: the source IP will always be the UE IP for that session.\n"
"Port\n"
"UDP port\n"
RANGE_STR)
{
range_val_set_int(&g_pfcp_tool->gtp.payload.source.udp_port_range.first,
atoi(argv[0]));
range_val_set_int(&g_pfcp_tool->gtp.payload.source.udp_port_range.last,
atoi(argv[1]));
return CMD_SUCCESS;
}
#define PAYLOAD_TARGET_STR PAYLOAD_STR "Target of payload packets\n"
DEFUN(gtpf_payload_target_ip, gtpf_payload_target_ip_cmd,
"payload target ip range IP_ADDR_FIRST IP_ADDR_LAST",
PAYLOAD_TARGET_STR
"IP address\n"
RANGE_STR)
{
if (!parse_ip_range(vty, &g_pfcp_tool->gtp.payload.target.ip_range, &argv[0]))
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(gtpf_payload_target_port, gtpf_payload_target_port_cmd,
"payload target port udp range <1-65535> <1-65535>",
PAYLOAD_STR
"Target port of payload packets\n"
"Port\n"
"UDP port\n"
RANGE_STR)
{
range_val_set_int(&g_pfcp_tool->gtp.payload.target.udp_port_range.first,
atoi(argv[0]));
range_val_set_int(&g_pfcp_tool->gtp.payload.target.udp_port_range.last,
atoi(argv[1]));
return CMD_SUCCESS;
}
DEFUN(gtpf_payload_append_payload_info, gtpf_payload_append_payload_info_cmd,
"payload append-info",
PAYLOAD_STR
"Append info about the data stream to the emitted payload."
" In a tunmap scenario, append the UPF's core side's TEID to the end of the generated payload,"
" to allow the remote receiver to echo back payload using the correct TEID, without the need"
" for out-of-band communication (see osmo-udp-responder)\n")
{
g_pfcp_tool->gtp.payload.append_info = true;
return CMD_SUCCESS;
}
DEFUN(gtpf_slew, gtpf_slew_cmd,
"slew <0-1000000000>",
"Wait N microseconds after each io_uring transmission submission\n"
"microseconds to wait (1000000 == 1 second)\n")
{
g_pfcp_tool->gtp.flood.cfg.slew_us = atoi(argv[0]);
return CMD_SUCCESS;
}
static struct cmd_node peer_node = {
PEER_NODE,
"%s(peer)# ",
@@ -397,9 +637,16 @@ DEFUN(s_f_teid_choose, s_f_teid_choose_cmd,
return CMD_SUCCESS;
}
int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
enum pdr_id_fixed {
PDR_ID_CORE = 1,
PDR_ID_ACCESS = 2,
};
const char * const gtp_ip_core = "10.99.0.1";
const char * const fallback_gtp_ip_access = "10.99.0.2";
int session_tunend_tx_est_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -412,19 +659,19 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
OSMO_ASSERT(session->kind == UP_GTP_U_TUNEND);
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
#define STR_TO_ADDR(DST, SRC) do { \
if (osmo_sockaddr_str_to_sockaddr(&SRC, &DST.u.sas)) { \
vty_out(vty, "Error in " #SRC ": " OSMO_SOCKADDR_STR_FMT "%s", \
OSMO_SOCKADDR_STR_FMT_ARGS(&SRC), VTY_NEWLINE); \
LOGP(DLGLOBAL, LOGL_ERROR, "Error in " #SRC ": " OSMO_SOCKADDR_STR_FMT "\n", \
OSMO_SOCKADDR_STR_FMT_ARGS(&SRC)); \
return CMD_WARNING; \
} \
} while (0)
@@ -464,6 +711,10 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_EST_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
/* the UPF has yet to assign a SEID for itself, no matter what SEID we (the CPF) use for this session */
m->h.seid = 0;
@@ -475,7 +726,7 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
.create_pdr_count = 2,
.create_pdr = {
{
.pdr_id = 1,
.pdr_id = PDR_ID_CORE,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
@@ -492,7 +743,7 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
.far_id = 1,
},
{
.pdr_id = 2,
.pdr_id = PDR_ID_ACCESS,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
@@ -531,16 +782,13 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
int session_tunmap_tx_est_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -556,14 +804,14 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
struct osmo_pfcp_ie_apply_action aa = {};
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
if (session->tunmap.access.local.teid == 0) {
f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
@@ -625,6 +873,10 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_EST_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
m->h.seid = 0;
/* GTP tunmap: remove header from both directions, and add header in both directions */
@@ -635,7 +887,7 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
.create_pdr_count = 2,
.create_pdr = {
{
.pdr_id = 1,
.pdr_id = PDR_ID_CORE,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
@@ -650,7 +902,7 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
.far_id = 1,
},
{
.pdr_id = 2,
.pdr_id = PDR_ID_ACCESS,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
@@ -691,10 +943,8 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
@@ -705,24 +955,20 @@ DEFUN(session_tx_est_req, session_tx_est_req_cmd,
"Set FAR to DROP = 1\n")
{
struct pfcp_tool_session *session = vty->index;
bool forw = (argc == 0 || !strcmp("forw", argv[0]));
switch (session->kind) {
case UP_GTP_U_TUNEND:
return session_tunend_tx_est_req(vty, argv, argc);
return session_tunend_tx_est_req(session, forw, NULL);
case UP_GTP_U_TUNMAP:
return session_tunmap_tx_est_req(vty, argv, argc);
return session_tunmap_tx_est_req(session, forw, NULL);
default:
vty_out(vty, "unknown gtp action%s", VTY_NEWLINE);
return CMD_WARNING;
}
}
DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
"tx session-mod-req far [(forw|drop)]",
TX_STR "Send a Session Modification Request\n"
"Set FAR to FORW = 1\n"
"Set FAR to DROP = 1\n")
int session_tunmap_tx_mod_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -730,14 +976,14 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
struct osmo_pfcp_ie_f_seid cp_f_seid;
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
.seid = session->cp_seid,
@@ -745,6 +991,10 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_MOD_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
m->h.seid = session->up_f_seid.seid;
m->ies.session_mod_req = (struct osmo_pfcp_msg_session_mod_req){
@@ -766,13 +1016,25 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
"tx session-mod-req far [(forw|drop)]",
TX_STR "Send a Session Modification Request\n"
"Set FAR to FORW = 1\n"
"Set FAR to DROP = 1\n")
{
struct pfcp_tool_session *session = vty->index;
bool forw = (argc == 0 || !strcmp("forw", argv[0]));
int rc = session_tunmap_tx_mod_req(session, forw, NULL);
if (rc != CMD_SUCCESS)
vty_out(vty, "Failed to send Session Modification Request%s", VTY_NEWLINE);
return rc;
}
DEFUN(session_tx_del_req, session_tx_del_req_cmd,
"tx session-del-req",
TX_STR "Send a Session Deletion Request\n")
@@ -799,6 +1061,371 @@ DEFUN(session_tx_del_req, session_tx_del_req_cmd,
return CMD_SUCCESS;
}
/* N SESSIONS */
static int responses_pending = 0;
DEFUN(wait_responses, wait_responses_cmd,
"wait responses",
"Let some time pass until events have occurred\n"
"Wait for all PFCP responses for pending PFCP requests\n")
{
if (!responses_pending) {
vty_out(vty, "no responses pending, not waiting.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE);
vty_flush(vty);
/* Still operate the message pump while waiting for time to pass */
while (!osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop(0) == -1)
break;
if (responses_pending == 0)
break;
}
vty_out(vty, "...done waiting for responses%s", VTY_NEWLINE);
vty_flush(vty);
return CMD_SUCCESS;
}
/* N SESSIONS TUNEND */
int one_session_mod_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
responses_pending--;
return 0;
}
int one_session_mod_tunend(struct pfcp_tool_session *session)
{
int rc;
rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunend_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
void est_resp_get_created_f_teid(struct pfcp_tool_gtp_tun_ep *dst, const struct osmo_pfcp_msg *rx_resp, uint16_t pdr_id)
{
int i;
const struct osmo_pfcp_msg_session_est_resp *r;
if (rx_resp->h.message_type != OSMO_PFCP_MSGT_SESSION_EST_RESP)
return;
r = &rx_resp->ies.session_est_resp;
for (i = 0; i < r->created_pdr_count; i++) {
const struct osmo_pfcp_ie_created_pdr *p = &r->created_pdr[i];
if (p->pdr_id != pdr_id)
continue;
if (!p->local_f_teid_present)
continue;
osmo_sockaddr_str_from_sockaddr(&dst->addr,
&p->local_f_teid.fixed.ip_addr.v4.u.sas);
dst->teid = p->local_f_teid.fixed.teid;
}
}
int one_session_create_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
struct pfcp_tool_session *session = req->ctx.priv;
enum osmo_pfcp_cause *cause;
const struct osmo_pfcp_msg_session_est_resp *r = NULL;
if (rx_resp)
r = &rx_resp->ies.session_est_resp;
responses_pending--;
if (errmsg)
LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg);
cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL;
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
return 0;
/* store SEID */
if (r && r->up_f_seid_present)
session->up_f_seid = r->up_f_seid;
/* store access local F-TEID */
est_resp_get_created_f_teid(&session->tunend.access.local, rx_resp, PDR_ID_ACCESS);
/* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */
one_session_mod_tunend(session);
return 0;
}
static int set_access_ran_tunend(struct pfcp_tool_gtp_tun_ep *dst)
{
if (llist_empty(&g_pfcp_tool->gtp.gtp_local_addrs)) {
/* No local GTP ports configured, just set an arbitrary address. */
if (osmo_sockaddr_str_from_str2(&dst->addr, fallback_gtp_ip_access)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, fallback_gtp_ip_access, -1));
return CMD_WARNING;
}
return CMD_SUCCESS;
}
/* next local GTP address to emit from */
struct udp_port *next;
next = g_pfcp_tool->gtp.gtp_local_addrs_next;
if (next) {
/* Move on by one gtp_local_addr. If past the end of the list, wrap back to the start below. */
if (next->entry.next == &g_pfcp_tool->gtp.gtp_local_addrs)
next = NULL;
else
next = container_of(next->entry.next, struct udp_port, entry);
}
if (!next)
next = llist_first_entry_or_null(&g_pfcp_tool->gtp.gtp_local_addrs, struct udp_port,
entry);
OSMO_ASSERT(next);
g_pfcp_tool->gtp.gtp_local_addrs_next = next;
if (osmo_sockaddr_str_from_osa(&dst->addr, &next->osa)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &next->osa));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
LOGP(DLGLOBAL, LOGL_DEBUG, "session picked gtp local " OSMO_SOCKADDR_STR_FMT " TEID 0x%x\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&dst->addr), dst->teid);
return CMD_SUCCESS;
}
static int set_core_cn_tunend(struct pfcp_tool_gtp_tun_ep *dst)
{
if (llist_empty(&g_pfcp_tool->gtp.gtp_core_addrs)) {
/* No core GTP ports configured, just set an arbitrary address. */
if (osmo_sockaddr_str_from_str2(&dst->addr, fallback_gtp_ip_access)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, fallback_gtp_ip_access, -1));
return CMD_WARNING;
}
return CMD_SUCCESS;
}
/* next core GTP address to emit from */
struct udp_port *next;
next = g_pfcp_tool->gtp.gtp_core_addrs_next;
if (next) {
/* Move on by one gtp_core_addr. If past the end of the list, wrap back to the start below. */
if (next->entry.next == &g_pfcp_tool->gtp.gtp_core_addrs)
next = NULL;
else
next = container_of(next->entry.next, struct udp_port, entry);
}
if (!next)
next = llist_first_entry_or_null(&g_pfcp_tool->gtp.gtp_core_addrs, struct udp_port,
entry);
OSMO_ASSERT(next);
g_pfcp_tool->gtp.gtp_core_addrs_next = next;
if (osmo_sockaddr_str_from_osa(&dst->addr, &next->osa)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &next->osa));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
LOGP(DLGLOBAL, LOGL_DEBUG, "session picked gtp core " OSMO_SOCKADDR_STR_FMT " TEID 0x%x\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&dst->addr), dst->teid);
return CMD_SUCCESS;
}
static int one_session_create_tunend(struct pfcp_tool_peer *peer)
{
struct pfcp_tool_session *session;
struct pfcp_tool_tunend *te;
struct pfcp_tool_gtp_tun_ep *dst;
struct osmo_sockaddr osa;
int rc;
session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNEND);
te = &session->tunend;
/* Access: set remote GTP address from UPF's point of view.
* A local GTP port from osmo-pfcp-tool's point of view. */
dst = &te->access.remote;
rc = set_access_ran_tunend(dst);
if (rc != CMD_SUCCESS)
return rc;
/* Set UE address */
te->access.local = (struct pfcp_tool_gtp_tun_ep){};
pfcp_tool_next_ue_addr(&osa);
if (osmo_sockaddr_str_from_osa(&te->core.ue_local_addr, &osa)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting UE address from %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &osa));
return CMD_WARNING;
};
/* Send initial Session Establishment Request */
rc = session_tunend_tx_est_req(session, false, one_session_create_tunend_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
/* N SESSIONS TUNMAP */
int one_session_mod_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
responses_pending--;
return 0;
}
int one_session_mod_tunmap(struct pfcp_tool_session *session)
{
struct pfcp_tool_gtp_tun_ep *dst;
int rc;
dst = &session->tunmap.core.remote;
if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_core)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, gtp_ip_core, -1));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunmap_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
int one_session_create_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
struct pfcp_tool_session *session = req->ctx.priv;
enum osmo_pfcp_cause *cause;
responses_pending--;
if (errmsg)
LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg);
cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL;
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
return 0;
/* store SEID */
if (rx_resp->ies.session_est_resp.up_f_seid_present)
session->up_f_seid = rx_resp->ies.session_est_resp.up_f_seid;
/* store local F-TEIDs */
est_resp_get_created_f_teid(&session->tunmap.access.local, rx_resp, PDR_ID_ACCESS);
est_resp_get_created_f_teid(&session->tunmap.core.local, rx_resp, PDR_ID_CORE);
/* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */
one_session_mod_tunmap(session);
return 0;
}
static int one_session_create_tunmap(struct pfcp_tool_peer *peer)
{
struct pfcp_tool_session *session;
struct pfcp_tool_tunmap *tm;
struct pfcp_tool_gtp_tun_ep *dst;
int rc;
session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNMAP);
tm = &session->tunmap;
/* Access: set remote GTP address from UPF's point of view.
* A local GTP port from osmo-pfcp-tool's point of view. */
dst = &tm->access.remote;
rc = set_access_ran_tunend(dst);
if (rc != CMD_SUCCESS)
return rc;
/* Core: set remote GTP address */
dst = &tm->core.remote;
rc = set_core_cn_tunend(dst);
if (rc != CMD_SUCCESS)
return rc;
/* Set local F-TEIDs == CHOOSE */
tm->access.local = (struct pfcp_tool_gtp_tun_ep){};
tm->core.local = (struct pfcp_tool_gtp_tun_ep){};
/* Send initial Session Establishment Request */
rc = session_tunmap_tx_est_req(session, false, one_session_create_tunmap_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
return CMD_WARNING;
}
static void n_sessions_create(struct vty *vty, struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind)
{
int i;
for (i = 0; i < n; i++) {
int rc;
if (kind == UP_GTP_U_TUNMAP)
rc = one_session_create_tunmap(peer);
else
rc = one_session_create_tunend(peer);
if (rc != CMD_SUCCESS)
break;
/* handle any pending select work */
while (!osmo_select_shutdown_done()) {
rc = pfcp_tool_mainloop(1);
/* quit requested */
if (rc < 0)
return;
/* no fd needed service */
if (rc == 0)
break;
}
/* Every N created sessions, wait for pending responses */
if (!(i & 0x3f) && responses_pending) {
vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE);
vty_flush(vty);
while (!osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop(0) == -1)
break;
if (responses_pending == 0)
break;
}
}
}
}
static void n_sessions_delete(struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind)
{
}
DEFUN(n_sessions, n_sessions_cmd,
"n (<0-2147483647>|all) session (create|delete) (tunend|tunmap)",
"Batch run\n"
"Perform the action N times, or on all available entries\n"
"In a batch run, create and later delete a number of sessions at once.\n"
"Create N new sessions\n"
"Delete N sessions created earlier\n"
TUNEND_STR TUNMAP_STR)
{
struct pfcp_tool_peer *peer = vty->index;
int n = ((strcmp("all", argv[0]) == 0) ? -1 : atoi(argv[0]));
bool create = (strcmp("create", argv[1]) == 0);
enum up_gtp_action_kind kind;
if (!strcmp(argv[2], "tunmap"))
kind = UP_GTP_U_TUNMAP;
else
kind = UP_GTP_U_TUNEND;
if (create)
n_sessions_create(vty, peer, n, kind);
else
n_sessions_delete(peer, n, kind);
return CMD_SUCCESS;
}
static void install_ve_and_config(struct cmd_element *cmd)
{
install_element_ve(cmd);
@@ -818,23 +1445,58 @@ void pfcp_tool_vty_init_cmds()
OSMO_ASSERT(g_pfcp_tool != NULL);
install_ve_and_config(&c_sleep_cmd);
install_ve_and_config(&c_date_cmd);
install_ve_and_config(&wait_responses_cmd);
install_ve_and_config(&peer_cmd);
install_node(&peer_node, NULL);
install_element(PEER_NODE, &c_sleep_cmd);
install_element(PEER_NODE, &c_date_cmd);
install_element(PEER_NODE, &peer_tx_heartbeat_cmd);
install_element(PEER_NODE, &peer_tx_assoc_setup_req_cmd);
install_element(PEER_NODE, &peer_retrans_req_cmd);
install_element(PEER_NODE, &n_sessions_cmd);
install_element(PEER_NODE, &wait_responses_cmd);
install_element(PEER_NODE, &session_cmd);
install_element(PEER_NODE, &session_endecaps_cmd);
install_node(&session_node, NULL);
install_element(SESSION_NODE, &c_sleep_cmd);
install_element(SESSION_NODE, &c_date_cmd);
install_element(SESSION_NODE, &session_tx_est_req_cmd);
install_element(SESSION_NODE, &session_tx_mod_req_cmd);
install_element(SESSION_NODE, &session_tx_del_req_cmd);
install_element(SESSION_NODE, &s_ue_cmd);
install_element(SESSION_NODE, &s_f_teid_cmd);
install_element(SESSION_NODE, &s_f_teid_choose_cmd);
install_ve_and_config(&c_gtp_local_cmd);
install_ve_and_config(&c_gtp_core_cmd);
install_ve_and_config(&c_ue_ip_range_cmd);
install_ve_and_config(&gtp_flood_cmd);
install_node(&gtp_flood_node, NULL);
install_element(GTP_FLOOD_NODE, &gtpf_workers_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_io_uring_queue_size_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_flows_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_packets_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_src_port_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_target_ip_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_target_port_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_append_payload_info_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_slew_cmd);
}
int pfcp_tool_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case GTP_FLOOD_NODE:
/* exiting a 'gtp flood' configuration, start the flooding */
pfcp_tool_gtp_flood_start();
break;
default:
break;
}
return 0;
}

111
src/osmo-pfcp-tool/range.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/pfcptool/range.h>
void range_val_set_int(struct range_val *rv, uint32_t val)
{
*rv = (struct range_val){
.buf = { (uint64_t)val, 0 },
.size = sizeof(val),
};
}
uint32_t range_val_get_int(const struct range_val *rv)
{
return rv->buf[0];
}
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val)
{
*rv = (struct range_val){};
rv->size = osmo_sockaddr_to_octets((void *)rv->buf, sizeof(rv->buf), val);
#if !OSMO_IS_BIG_ENDIAN
for (int i = 0; i < rv->size / 2; i++) {
uint8_t *rvbuf = (void *)rv->buf;
uint8_t tmp = rvbuf[i];
rvbuf[i] = rvbuf[rv->size - 1 - i];
rvbuf[rv->size - 1 - i] = tmp;
}
#endif
}
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv)
{
void *buf;
#if OSMO_IS_BIG_ENDIAN
buf = rv->buf;
#else
int i;
uint8_t rev[sizeof(rv->buf)];
uint8_t *val = (void *)rv->buf;
for (i = 0; i < rv->size; i++)
rev[i] = val[rv->size - 1 - i];
buf = rev;
#endif
osmo_sockaddr_from_octets(dst, buf, rv->size);
}
void range_val_inc(struct range_val *rv)
{
uint64_t was = rv->buf[0];
rv->buf[0]++;
if (rv->buf[0] < was)
rv->buf[1]++;
}
int range_val_cmp(const struct range_val *a, const struct range_val *b)
{
int rc;
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
rc = OSMO_CMP(a->buf[0], b->buf[0]);
if (rc)
return rc;
rc = OSMO_CMP(a->buf[1], b->buf[1]);
if (rc)
return rc;
return OSMO_CMP(a->size, b->size);
}
void range_next(struct range *r)
{
if (range_val_cmp(&r->next, &r->first) < 0) {
r->next = r->first;
return;
}
if (range_val_cmp(&r->next, &r->last) >= 0) {
r->next = r->first;
r->wrapped = true;
return;
}
range_val_inc(&r->next);
}

View File

@@ -10,7 +10,6 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOGTLV_CFLAGS) \
$(LIBOSMOPFCP_CFLAGS) \
$(LIBGTPNL_CFLAGS) \
$(LIBNFTNL_CFLAGS) \
@@ -19,19 +18,15 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBGTPNL_LDFLAGS) \
$(LIBNFTNL_LDFLAGS) \
$(LIBNFTABLES_LDFLAGS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-upf \
noinst_LTLIBRARIES = \
libupf.la \
$(NULL)
osmo_upf_SOURCES = \
libupf_la_SOURCES = \
netinst.c \
osmo_upf_main.c \
up_endpoint.c \
up_gtp_action.c \
up_peer.c \
@@ -43,14 +38,24 @@ osmo_upf_SOURCES = \
upf_vty.c \
$(NULL)
osmo_upf_LDADD = \
libupf_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGTLV_LIBS) \
$(LIBOSMOPFCP_LIBS) \
$(LIBGTPNL_LIBS) \
$(LIBNFTNL_LIBS) \
$(LIBNFTABLES_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-upf \
$(NULL)
osmo_upf_SOURCES = \
osmo_upf_main.c \
$(NULL)
osmo_upf_LDADD = \
libupf.la \
$(NULL)

View File

@@ -233,9 +233,8 @@ static void up_endpoint_rx_cb(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_ms
}
}
struct up_endpoint *up_endpoint_init(void *ctx, const struct osmo_sockaddr *local_addr)
struct up_endpoint *up_endpoint_alloc(void *ctx, const struct osmo_sockaddr *local_addr)
{
int rc;
struct osmo_pfcp_endpoint_cfg cfg;
struct up_endpoint *up_ep;
up_ep = talloc_zero(ctx, struct up_endpoint);
@@ -252,14 +251,16 @@ struct up_endpoint *up_endpoint_init(void *ctx, const struct osmo_sockaddr *loca
up_ep->pfcp_ep = osmo_pfcp_endpoint_create(up_ep, &cfg);
OSMO_ASSERT(up_ep->pfcp_ep);
rc = osmo_pfcp_endpoint_bind(up_ep->pfcp_ep);
if (rc) {
talloc_free(up_ep);
return NULL;
}
return up_ep;
}
int up_endpoint_bind(struct up_endpoint *up_ep)
{
OSMO_ASSERT(up_ep);
OSMO_ASSERT(up_ep->pfcp_ep);
return osmo_pfcp_endpoint_bind(up_ep->pfcp_ep);
}
static struct up_session *up_endpoint_find_session(struct up_endpoint *ep, uint64_t up_seid)
{
struct up_peer *peer;
@@ -271,22 +272,11 @@ static struct up_session *up_endpoint_find_session(struct up_endpoint *ep, uint6
return NULL;
}
static struct up_session *up_endpoint_find_session_by_local_teid(struct up_endpoint *ep, uint32_t teid)
{
struct up_peer *peer;
llist_for_each_entry(peer, &ep->peers, entry) {
struct up_session *session = up_session_find_by_local_teid(peer, teid);
if (session)
return session;
}
return NULL;
}
uint64_t up_endpoint_next_seid(struct up_endpoint *ep)
uint64_t up_endpoint_next_up_seid(struct up_endpoint *ep)
{
uint64_t sanity;
for (sanity = 2342; sanity; sanity--) {
uint64_t next_seid = osmo_pfcp_next_seid(&ep->next_seid_state);
uint64_t next_seid = osmo_pfcp_next_seid(&ep->next_up_seid_state);
if (up_endpoint_find_session(ep, next_seid))
continue;
return next_seid;
@@ -294,26 +284,6 @@ uint64_t up_endpoint_next_seid(struct up_endpoint *ep)
return 0;
}
static uint32_t up_endpoint_inc_teid(struct up_endpoint *ep)
{
ep->next_teid_state++;
if (!ep->next_teid_state)
ep->next_teid_state++;
return ep->next_teid_state;
}
uint32_t up_endpoint_next_teid(struct up_endpoint *ep)
{
uint32_t sanity;
for (sanity = 2342; sanity; sanity--) {
uint32_t next_teid = up_endpoint_inc_teid(ep);
if (up_endpoint_find_session_by_local_teid(ep, next_teid))
continue;
return next_teid;
}
return 0;
}
void up_endpoint_free(struct up_endpoint **_ep)
{
struct up_peer *peer;

View File

@@ -48,11 +48,11 @@ int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action
switch (a->kind) {
case UP_GTP_U_TUNEND:
if ((cmp = CMP_MEMB(tunend.access.local_teid)))
if ((cmp = CMP_MEMB(tunend.access.local.teid)))
return cmp;
if ((cmp = CMP_MEMB(tunend.access.remote_teid)))
if ((cmp = CMP_MEMB(tunend.access.remote.teid)))
return cmp;
cmp = osmo_sockaddr_cmp(&a->tunend.access.gtp_remote_addr, &b->tunend.access.gtp_remote_addr);
cmp = osmo_sockaddr_cmp(&a->tunend.access.remote.addr, &b->tunend.access.remote.addr);
if (cmp)
return cmp;
cmp = osmo_sockaddr_cmp(&a->tunend.core.ue_local_addr, &b->tunend.core.ue_local_addr);
@@ -61,13 +61,13 @@ int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action
break;
case UP_GTP_U_TUNMAP:
if ((cmp = CMP_MEMB(tunmap.access.local_teid)))
if ((cmp = CMP_MEMB(tunmap.access.tun.local.teid)))
return cmp;
if ((cmp = CMP_MEMB(tunmap.access.remote_teid)))
if ((cmp = CMP_MEMB(tunmap.access.tun.remote.teid)))
return cmp;
if ((cmp = CMP_MEMB(tunmap.core.local_teid)))
if ((cmp = CMP_MEMB(tunmap.core.tun.local.teid)))
return cmp;
if ((cmp = CMP_MEMB(tunmap.core.remote_teid)))
if ((cmp = CMP_MEMB(tunmap.core.tun.remote.teid)))
return cmp;
break;
default:
@@ -84,14 +84,14 @@ static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable)
switch (a->kind) {
case UP_GTP_U_TUNEND:
if (g_upf->gtp.mockup) {
if (g_upf->tunend.mockup) {
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunend/mockup active, skipping GTP action %s\n",
enable ? "enable" : "disable");
return 0;
}
/* Pick GTP device matching the local F-TEID set up for the GTP tunnel (it is on the Access side) */
gtp_addr = &a->tunend.access.gtp_local_addr;
gtp_addr = &a->tunend.access.local.addr;
gtp_dev = upf_gtp_dev_find_by_local_addr(gtp_addr);
if (!gtp_dev) {
LOG_UP_GTP_ACTION(a, LOGL_ERROR, "No GTP device open for local address %s, cannot %s"
@@ -116,7 +116,7 @@ static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable)
return 0;
case UP_GTP_U_TUNMAP:
if (g_upf->nft.mockup) {
if (g_upf->tunmap.mockup) {
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunmap/mockup active, skipping nftables ruleset %s\n",
enable ? "enable" : "disable");
return 0;
@@ -158,26 +158,26 @@ int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_actio
switch (a->kind) {
case UP_GTP_U_TUNEND:
OSMO_STRBUF_PRINTF(sb, "GTP:tunend GTP-access-r:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.gtp_remote_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunend.access.remote_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.remote.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunend.access.remote.teid);
OSMO_STRBUF_PRINTF(sb, " GTP-access-l:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.gtp_local_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32" IP-core-l:", a->tunend.access.local_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.local.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32" IP-core-l:", a->tunend.access.local.teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.core.ue_local_addr);
break;
case UP_GTP_U_TUNMAP:
OSMO_STRBUF_PRINTF(sb, "GTP:tunmap GTP-access-r:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.gtp_remote_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunmap.access.remote_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.remote.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunmap.access.tun.remote.teid);
OSMO_STRBUF_PRINTF(sb, " GTP-access-l:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.gtp_local_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32, a->tunmap.access.local_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.local.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32, a->tunmap.access.tun.local.teid);
OSMO_STRBUF_PRINTF(sb, " GTP-core-r:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.gtp_remote_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-core-r:0x%"PRIx32, a->tunmap.core.remote_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.remote.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-core-r:0x%"PRIx32, a->tunmap.core.tun.remote.teid);
OSMO_STRBUF_PRINTF(sb, " GTP-core-l:");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.gtp_local_addr);
OSMO_STRBUF_PRINTF(sb, " TEID-core-l:0x%"PRIx32, a->tunmap.core.local_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.local.addr);
OSMO_STRBUF_PRINTF(sb, " TEID-core-l:0x%"PRIx32, a->tunmap.core.tun.local.teid);
break;
case UP_GTP_DROP:
OSMO_STRBUF_PRINTF(sb, "GTP:drop");

View File

@@ -299,7 +299,7 @@ static void up_peer_rx_session_est_req(struct up_peer *peer, struct osmo_pfcp_ms
{
enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
struct osmo_pfcp_msg *resp;
struct up_session *session = up_session_find_or_add(peer, &m->ies.session_est_req.cp_f_seid, NULL);
struct up_session *session = up_session_find_or_add(peer, &m->ies.session_est_req.cp_f_seid);
if (!session) {
cause = OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE;

View File

@@ -185,7 +185,6 @@ static enum osmo_pfcp_cause up_session_choose_f_teid(struct up_session *session,
bool choose_id_present, uint8_t choose_id,
const char *netinst_name)
{
struct up_endpoint *up_ep = session->up_peer->up_endpoint;
struct chosen_f_teid *chosen = NULL;
if (choose_id_present)
@@ -206,7 +205,7 @@ static enum osmo_pfcp_cause up_session_choose_f_teid(struct up_session *session,
return rc;
/* Choose a new TEID */
dst->fixed.teid = up_endpoint_next_teid(up_ep);
dst->fixed.teid = upf_next_local_teid();
if (dst->fixed.teid == 0) {
LOGPFSML(session->fi, LOGL_ERROR, "Failed to allocate an unused TEID\n");
return OSMO_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION;
@@ -557,8 +556,7 @@ static struct pdr *pdr_upd(struct pdr *pdr,
return pdr;
nack_resp:
if (pdr)
pdr_del(pdr);
pdr_del(pdr);
if (!*offending_ie_present) {
*offending_ie = OSMO_PFCP_IEI_UPD_PDR;
*offending_ie_present = true;
@@ -987,7 +985,7 @@ static inline uint64_t up_session_key(uint64_t cp_seid, uint64_t up_seid)
static struct up_session *up_session_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
{
struct up_session *session;
uint64_t up_seid = up_endpoint_next_seid(peer->up_endpoint);
uint64_t up_seid = up_endpoint_next_up_seid(peer->up_endpoint);
if (!up_seid)
return NULL;
@@ -1022,16 +1020,11 @@ static struct up_session *up_session_add(struct up_peer *peer, const struct osmo
return session;
}
struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid,
const struct osmo_pfcp_ie_f_seid *up_f_seid)
struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
{
struct up_session *session;
if (cp_f_seid)
session = up_session_find_by_cp_f_seid(peer, cp_f_seid);
else if (up_f_seid)
session = up_session_find_by_up_seid(peer, up_f_seid->seid);
else
return NULL;
OSMO_ASSERT(cp_f_seid);
session = up_session_find_by_cp_f_seid(peer, cp_f_seid);
if (session)
return session;
@@ -1227,10 +1220,14 @@ static void add_gtp_action_tunend(void *ctx, struct llist_head *dst, struct pdr
.kind = UP_GTP_U_TUNEND,
.tunend = {
.access = {
.gtp_local_addr = pdr->local_f_teid->fixed.ip_addr.v4,
.local_teid = pdr->local_f_teid->fixed.teid,
.gtp_remote_addr = rfar_forw->outer_header_creation.ip_addr.v4,
.remote_teid = rfar_forw->outer_header_creation.teid,
.local = {
.addr = pdr->local_f_teid->fixed.ip_addr.v4,
.teid = pdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = rfar_forw->outer_header_creation.ip_addr.v4,
.teid = rfar_forw->outer_header_creation.teid,
},
},
.core = {
.ue_local_addr = rpdr->desc.pdi.ue_ip_address.ip_addr.v4,
@@ -1336,17 +1333,25 @@ static void add_gtp_action_tunmap(void *ctx, struct llist_head *dst, struct pdr
.pdr_core = rpdr->desc.pdr_id,
.kind = UP_GTP_U_TUNMAP,
.tunmap = {
.access = {
.gtp_local_addr = pdr->local_f_teid->fixed.ip_addr.v4,
.local_teid = pdr->local_f_teid->fixed.teid,
.gtp_remote_addr = rfar_forw->outer_header_creation.ip_addr.v4,
.remote_teid = rfar_forw->outer_header_creation.teid,
.access.tun = {
.local = {
.addr = pdr->local_f_teid->fixed.ip_addr.v4,
.teid = pdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = rfar_forw->outer_header_creation.ip_addr.v4,
.teid = rfar_forw->outer_header_creation.teid,
},
},
.core = {
.gtp_local_addr = rpdr->local_f_teid->fixed.ip_addr.v4,
.local_teid = rpdr->local_f_teid->fixed.teid,
.gtp_remote_addr = far_forw->outer_header_creation.ip_addr.v4,
.remote_teid = far_forw->outer_header_creation.teid,
.core.tun = {
.local = {
.addr = rpdr->local_f_teid->fixed.ip_addr.v4,
.teid = rpdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = far_forw->outer_header_creation.ip_addr.v4,
.teid = far_forw->outer_header_creation.teid,
},
},
},
};
@@ -1523,11 +1528,16 @@ static enum osmo_pfcp_cause up_session_setup_gtp(struct up_session *session)
return cause;
}
/* Return true when the session is in Established state and has active GTP actions. */
bool up_session_is_active(struct up_session *session)
{
return session && (session->fi->state == UP_SESSION_ST_ESTABLISHED) && !llist_empty(&session->active_gtp_actions);
}
/* Return true when up_session_is_active() == true *and* it has only active PDR/FAR pairs.
* A PDR/FAR is inactive when it is not part of an active GTP action. Reasons may be that it has no PDR-to-FAR relation,
* there is no matching reverse PDR/FAR, that a FAR is not set to FORW, an ignored Source/Destination Interface, ...
*/
bool up_session_is_fully_active(struct up_session *session, int *active_p, int *inactive_p)
{
struct pdr *pdr;

View File

@@ -29,6 +29,9 @@
#include <osmocom/upf/upf.h>
#include <osmocom/upf/up_endpoint.h>
#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/upf/upf_gtp.h>
struct g_upf *g_upf = NULL;
@@ -50,22 +53,22 @@ void g_upf_alloc(void *ctx)
.local_port = OSMO_PFCP_PORT,
},
},
.nft = {
.tunmap = {
.priority_pre = -300,
.priority_post = 400,
},
.gtp = {
.tunend = {
/* TODO: recovery count state file; use lower byte of current time, poor person's random. */
.recovery_count = time(NULL),
},
};
INIT_LLIST_HEAD(&g_upf->gtp.vty_cfg.devs);
INIT_LLIST_HEAD(&g_upf->gtp.devs);
INIT_LLIST_HEAD(&g_upf->tunend.vty_cfg.devs);
INIT_LLIST_HEAD(&g_upf->tunend.devs);
INIT_LLIST_HEAD(&g_upf->netinst);
}
int upf_pfcp_listen()
int upf_pfcp_init(void)
{
struct osmo_sockaddr_str local_addr_str;
struct osmo_sockaddr local_addr;
@@ -77,9 +80,8 @@ int upf_pfcp_listen()
* osmo_sockaddr. */
osmo_sockaddr_str_from_str(&local_addr_str, g_upf->pfcp.vty_cfg.local_addr, g_upf->pfcp.vty_cfg.local_port);
osmo_sockaddr_str_to_sockaddr(&local_addr_str, &local_addr.u.sas);
LOGP(DLPFCP, LOGL_NOTICE, "PFCP: Listening on %s\n", osmo_sockaddr_to_str_c(OTC_SELECT, &local_addr));
g_upf->pfcp.ep = up_endpoint_init(g_upf, &local_addr);
g_upf->pfcp.ep = up_endpoint_alloc(g_upf, &local_addr);
if (!g_upf->pfcp.ep) {
fprintf(stderr, "Failed to allocate PFCP endpoint.\n");
return -1;
@@ -87,9 +89,29 @@ int upf_pfcp_listen()
return 0;
}
int upf_pfcp_listen(void)
{
int rc;
if (!g_upf->pfcp.ep) {
rc = upf_pfcp_init();
if (rc)
return rc;
}
rc = up_endpoint_bind(g_upf->pfcp.ep);
if (rc) {
LOGP(DLPFCP, LOGL_ERROR, "PFCP: failed to listen on %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_endpoint_get_local_addr(g_upf->pfcp.ep->pfcp_ep)));
return rc;
}
LOGP(DLPFCP, LOGL_NOTICE, "PFCP: Listening on %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_endpoint_get_local_addr(g_upf->pfcp.ep->pfcp_ep)));
return 0;
}
int upf_gtp_devs_open()
{
struct tunend_vty_cfg *c = &g_upf->gtp.vty_cfg;
struct tunend_vty_cfg *c = &g_upf->tunend.vty_cfg;
struct tunend_vty_cfg_dev *d;
llist_for_each_entry(d, &c->devs, entry) {
@@ -98,3 +120,85 @@ int upf_gtp_devs_open()
}
return 0;
}
static bool upf_is_local_teid_in_use(uint32_t teid)
{
struct up_peer *peer;
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
struct up_session *session = up_session_find_by_local_teid(peer, teid);
if (session)
return true;
}
return false;
}
static uint32_t upf_next_local_teid_inc(void)
{
g_upf->gtp.next_local_teid_state++;
if (!g_upf->gtp.next_local_teid_state)
g_upf->gtp.next_local_teid_state++;
return g_upf->gtp.next_local_teid_state;
}
uint32_t upf_next_local_teid(void)
{
uint32_t sanity;
for (sanity = 2342; sanity; sanity--) {
uint32_t next_teid = upf_next_local_teid_inc();
if (upf_is_local_teid_in_use(next_teid))
continue;
return next_teid;
}
return 0;
}
static uint32_t upf_next_chain_id_inc(void)
{
g_upf->tunmap.next_chain_id_state++;
if (!g_upf->tunmap.next_chain_id_state)
g_upf->tunmap.next_chain_id_state++;
return g_upf->tunmap.next_chain_id_state;
}
/* Return an unused chain_id, or 0 if none is found with sane effort. */
uint32_t upf_next_chain_id(void)
{
uint32_t sanity;
/* Make sure the new chain_id is not used anywhere */
for (sanity = 2342; sanity; sanity--) {
struct up_peer *peer;
uint32_t chain_id = upf_next_chain_id_inc();
bool taken = false;
if (!g_upf->pfcp.ep)
return chain_id;
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
struct up_session *session;
int bkt;
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
struct up_gtp_action *a;
llist_for_each_entry(a, &session->active_gtp_actions, entry) {
if (a->kind != UP_GTP_U_TUNMAP)
continue;
if (a->tunmap.access.chain_id == chain_id
|| a->tunmap.core.chain_id == chain_id) {
taken = true;
break;
}
}
if (taken)
break;
}
if (taken)
break;
}
if (!taken)
return chain_id;
}
/* finding a chain_id became insane, return invalid = 0 */
return 0;
}

View File

@@ -66,7 +66,7 @@ char *upf_gtp_dev_to_str_c(void *ctx, const struct upf_gtp_dev *dev)
struct upf_gtp_dev *upf_gtp_dev_find_by_name(const char *name)
{
struct upf_gtp_dev *dev;
llist_for_each_entry(dev, &g_upf->gtp.devs, entry) {
llist_for_each_entry(dev, &g_upf->tunend.devs, entry) {
if (!strcmp(name, dev->name))
return dev;
}
@@ -79,7 +79,7 @@ struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *l
struct upf_gtp_dev *dev_any = NULL;
struct osmo_sockaddr needle = *local_addr;
llist_for_each_entry(dev, &g_upf->gtp.devs, entry) {
llist_for_each_entry(dev, &g_upf->tunend.devs, entry) {
/* To leave the port number out of the cmp, set the needle's port to match */
osmo_sockaddr_set_port(&needle.u.sa, osmo_sockaddr_port(&dev->gtpv1.local_addr.u.sa));
@@ -95,7 +95,7 @@ struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *l
struct upf_gtp_dev *upf_gtp_dev_first()
{
return llist_first_entry_or_null(&g_upf->gtp.devs, struct upf_gtp_dev, entry);
return llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry);
}
/* Tell the kernel to remove the GTP device. Called implicitly by talloc_free() (see upf_gtp_dev_destruct()). */
@@ -116,7 +116,7 @@ static int upf_gtp_dev_delete(struct upf_gtp_dev *dev)
static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev);
/* Allocate state for one GTP device, add to g_upf->gtp.devs and return the created device. If state for the device of
/* Allocate state for one GTP device, add to g_upf->tunend.devs and return the created device. If state for the device of
* that name already exists, do nothing and return NULL. */
static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local_addr)
{
@@ -144,7 +144,7 @@ static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local
/* Need to add to list before setting up the destructor. A talloc_free() does automagically remove from the
* list. */
llist_add(&dev->entry, &g_upf->gtp.devs);
llist_add(&dev->entry, &g_upf->tunend.devs);
talloc_set_destructor(dev, upf_gtp_dev_destruct);
@@ -163,7 +163,7 @@ static int dev_resolve_ifidx(struct upf_gtp_dev *dev)
}
/* Let's try something to see if talking to the device works. */
errno = 0;
rc = gtp_list_tunnel(g_upf->gtp.genl_id, g_upf->gtp.nl);
rc = gtp_list_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl);
if (errno)
rc = -errno;
else if (rc)
@@ -192,7 +192,7 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
int rc;
struct upf_gtp_dev *dev;
if (g_upf->gtp.mockup) {
if (g_upf->tunend.mockup) {
LOGP(DGTP, LOGL_NOTICE, "tunend/mockup active: not opening GTP device '%s'\n", name);
return 0;
}
@@ -265,17 +265,17 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
void upf_gtp_devs_close()
{
struct upf_gtp_dev *dev;
while ((dev = llist_first_entry_or_null(&g_upf->gtp.devs, struct upf_gtp_dev, entry)))
while ((dev = llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry)))
talloc_free(dev);
}
void upf_gtp_genl_close()
{
if (!g_upf->gtp.nl)
if (!g_upf->tunend.nl)
return;
genl_socket_close(g_upf->gtp.nl);
g_upf->gtp.nl = NULL;
g_upf->gtp.genl_id = -1;
genl_socket_close(g_upf->tunend.nl);
g_upf->tunend.nl = NULL;
g_upf->tunend.genl_id = -1;
LOGP(DGTP, LOGL_NOTICE, "Closed mnl_socket\n");
}
@@ -284,21 +284,21 @@ void upf_gtp_genl_close()
int upf_gtp_genl_ensure_open()
{
/* Already open? */
if (g_upf->gtp.nl && g_upf->gtp.genl_id >= 0)
if (g_upf->tunend.nl && g_upf->tunend.genl_id >= 0)
return 0;
/* sanity / paranoia: if re-opening, make sure the previous socket is closed */
if (g_upf->gtp.nl)
if (g_upf->tunend.nl)
upf_gtp_genl_close();
g_upf->gtp.nl = genl_socket_open();
if (!g_upf->gtp.nl) {
g_upf->tunend.nl = genl_socket_open();
if (!g_upf->tunend.nl) {
LOGP(DGTP, LOGL_ERROR, "Cannot open mnl_socket: %s\n", strerror(errno));
return -EIO;
}
g_upf->gtp.genl_id = genl_lookup_family(g_upf->gtp.nl, "gtp");
if (g_upf->gtp.genl_id < 0) {
g_upf->tunend.genl_id = genl_lookup_family(g_upf->tunend.nl, "gtp");
if (g_upf->tunend.genl_id < 0) {
LOGP(DGTP, LOGL_ERROR, "genl family 'gtp' not found\n");
return -ENOTSUP;
}
@@ -311,7 +311,7 @@ struct upf_gtp_tunend {
struct llist_head entry;
struct upf_gtp_dev *dev;
struct upf_gtp_tunend_desc desc;
struct upf_tunend desc;
bool active;
};
@@ -320,9 +320,9 @@ static int upf_gtp_tunend_to_str_buf(char *buf, size_t buflen, const struct upf_
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
/* "tunend{dev=apn0 access(GTP-r=1.2.3.4 TEID:l=0x1234,r=0x5678) core(UE-l=10.9.8.7)}" */
OSMO_STRBUF_PRINTF(sb, "tunend{dev=%s access(GTP-r=", tun->dev->name);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.access.gtp_remote_addr);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.access.remote.addr);
OSMO_STRBUF_PRINTF(sb, " TEID:l=0x%x,r=0x%x) core(UE-l=",
tun->desc.access.local_teid, tun->desc.access.remote_teid);
tun->desc.access.local.teid, tun->desc.access.remote.teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.core.ue_local_addr);
OSMO_STRBUF_PRINTF(sb, ")}");
return sb.chars_needed;
@@ -343,18 +343,18 @@ static int upf_gtp_tunend_destruct(struct upf_gtp_tunend *tun)
return 0;
}
#define tunend_desc_validate(TUN_DESC) \
#define tunend_validate(TUNEND) \
do { \
OSMO_ASSERT(osmo_sockaddr_port(&(TUN_DESC)->access.gtp_local_addr.u.sa) == 0); \
OSMO_ASSERT(osmo_sockaddr_port(&(TUN_DESC)->access.gtp_remote_addr.u.sa) == 0); \
OSMO_ASSERT(osmo_sockaddr_port(&(TUN_DESC)->core.ue_local_addr.u.sa) == 0); \
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->access.local.addr.u.sa) == 0); \
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->access.remote.addr.u.sa) == 0); \
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->core.ue_local_addr.u.sa) == 0); \
} while (0)
static struct upf_gtp_tunend *upf_gtp_tunend_alloc(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *desc)
static struct upf_gtp_tunend *upf_gtp_tunend_alloc(struct upf_gtp_dev *dev, const struct upf_tunend *desc)
{
struct upf_gtp_tunend *tun = talloc(dev, struct upf_gtp_tunend);
OSMO_ASSERT(tun);
tunend_desc_validate(desc);
tunend_validate(desc);
*tun = (struct upf_gtp_tunend){
.dev = dev,
.desc = *desc,
@@ -369,7 +369,7 @@ static struct gtp_tunnel *upf_gtp_tunend_to_gtp_tunnel(struct upf_gtp_tunend *tu
struct gtp_tunnel *t;
if (tun->desc.core.ue_local_addr.u.sas.ss_family != AF_INET
|| tun->desc.access.gtp_remote_addr.u.sas.ss_family != AF_INET) {
|| tun->desc.access.remote.addr.u.sas.ss_family != AF_INET) {
LOG_GTP_TUN(tun, LOGL_ERROR, "Only capabale of IPv4\n");
return NULL;
}
@@ -378,9 +378,9 @@ static struct gtp_tunnel *upf_gtp_tunend_to_gtp_tunnel(struct upf_gtp_tunend *tu
OSMO_ASSERT(t);
gtp_tunnel_set_ifidx(t, tun->dev->ifidx);
gtp_tunnel_set_version(t, GTP_V1);
gtp_tunnel_set_i_tei(t, tun->desc.access.local_teid);
gtp_tunnel_set_o_tei(t, tun->desc.access.remote_teid);
gtp_tunnel_set_sgsn_ip4(t, &tun->desc.access.gtp_remote_addr.u.sin.sin_addr);
gtp_tunnel_set_i_tei(t, tun->desc.access.local.teid);
gtp_tunnel_set_o_tei(t, tun->desc.access.remote.teid);
gtp_tunnel_set_sgsn_ip4(t, &tun->desc.access.remote.addr.u.sin.sin_addr);
gtp_tunnel_set_ms_ip4(t, &tun->desc.core.ue_local_addr.u.sin.sin_addr);
return t;
}
@@ -398,7 +398,7 @@ int upf_gtp_tunend_activate(struct upf_gtp_tunend *tun)
return -ENOTSUP;
errno = 0;
rc = gtp_add_tunnel(g_upf->gtp.genl_id, g_upf->gtp.nl, t);
rc = gtp_add_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl, t);
if (errno) {
rc = -errno;
} else if (rc) {
@@ -411,36 +411,36 @@ int upf_gtp_tunend_activate(struct upf_gtp_tunend *tun)
return rc;
}
static struct upf_gtp_tunend *upf_gtp_dev_tunend_find(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *tun_desc)
static struct upf_gtp_tunend *upf_gtp_dev_tunend_find(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
{
struct upf_gtp_tunend *tun;
tunend_desc_validate(tun_desc);
tunend_validate(tunend);
llist_for_each_entry(tun, &dev->tunnels, entry) {
if (upf_gtp_tunend_desc_cmp(tun_desc, &tun->desc))
if (upf_gtp_tunend_cmp(tunend, &tun->desc))
continue;
return tun;
}
return NULL;
}
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *tun_desc)
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
{
struct upf_gtp_tunend *tun;
tunend_desc_validate(tun_desc);
tun = upf_gtp_dev_tunend_find(dev, tun_desc);
tunend_validate(tunend);
tun = upf_gtp_dev_tunend_find(dev, tunend);
if (!tun)
tun = upf_gtp_tunend_alloc(dev, tun_desc);
tun = upf_gtp_tunend_alloc(dev, tunend);
if (tun->active)
return 0;
return upf_gtp_tunend_activate(tun);
}
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_gtp_tunend_desc *tun_desc)
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
{
struct upf_gtp_tunend *tun;
int rc;
tunend_desc_validate(tun_desc);
tun = upf_gtp_dev_tunend_find(dev, tun_desc);
tunend_validate(tunend);
tun = upf_gtp_dev_tunend_find(dev, tunend);
if (!tun)
return 0;
if (tun->active) {
@@ -466,7 +466,7 @@ static int upf_gtp_tunend_deactivate(struct upf_gtp_tunend *tun)
if (!t)
return -EINVAL;
rc = gtp_del_tunnel(g_upf->gtp.genl_id, g_upf->gtp.nl, t);
rc = gtp_del_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl, t);
if (rc)
LOG_GTP_TUN(tun, LOGL_ERROR, "Failed to delete tunnel\n");
else
@@ -491,7 +491,7 @@ static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev)
return 0;
}
int upf_gtp_tunend_desc_cmp(const struct upf_gtp_tunend_desc *a, const struct upf_gtp_tunend_desc *b)
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b)
{
int r;
@@ -503,9 +503,9 @@ int upf_gtp_tunend_desc_cmp(const struct upf_gtp_tunend_desc *a, const struct up
return 1;
#define CMP_MEMB(MEMB) OSMO_CMP(a->MEMB, b->MEMB)
if ((r = CMP_MEMB(access.local_teid)))
if ((r = CMP_MEMB(access.local.teid)))
return r;
if ((r = CMP_MEMB(access.remote_teid)))
if ((r = CMP_MEMB(access.remote.teid)))
return r;
return osmo_sockaddr_cmp(&a->access.gtp_remote_addr, &b->access.gtp_remote_addr);
return osmo_sockaddr_cmp(&a->access.remote.addr, &b->access.remote.addr);
}

View File

@@ -31,6 +31,7 @@ struct gtp1u_hdr {
pt:1, /*< Protocol Type: GTP=1, GTP'=0 */
version:3; /*< Version: 1 */
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
#endif
uint8_t msg_type;
@@ -49,23 +50,43 @@ struct gtp1u_hdr {
static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr);
static int rx_echo_req(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, const struct gtp1u_hdr *rx_h)
static int rx_echo_req(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, const struct gtp1u_hdr *rx_h,
size_t msg_len)
{
if (!rx_h->s) {
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTPv1-U ECHO REQ without sequence nr\n");
return -1;
}
uint16_t seq_nr = 0;
uint8_t recovery_count = 0;
if (msg_len >= (sizeof(*rx_h) + 2) && rx_h->data2[0] == GTP1U_IEI_RECOVERY)
recovery_count = rx_h->data2[1];
seq_nr = rx_h->s;
LOG_GTP_DEV(dev, LOGL_INFO, "<- %s: rx GTPv1-U Echo Request: seq_nr=%u recovery_count=%u\n",
osmo_sockaddr_to_str(remote), seq_nr, recovery_count);
return tx_echo_resp(dev, remote, rx_h->ext.seq_nr);
}
static void rx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, const struct gtp1u_hdr *rx_h,
size_t msg_len)
{
if (msg_len < (sizeof(*rx_h) + 2)) {
LOG_GTP_DEV(dev, LOGL_ERROR,
"<- %s: rx GTPv1-U Echo Response, but message is too short (%zu < %zu)\n",
osmo_sockaddr_to_str_c(OTC_SELECT, remote), msg_len, (sizeof(*rx_h) + 2));
return;
}
uint8_t recovery_count = rx_h->data2[1];
LOG_GTP_DEV(dev, LOGL_INFO, "<- %s: rx GTPv1-U Echo Response: seq_nr=%u recovery_count=%u\n",
osmo_sockaddr_to_str(remote), rx_h->ext.seq_nr, recovery_count);
}
static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr)
{
struct msgb *msg;
struct gtp1u_hdr *tx_h;
int rc;
msg = msgb_alloc_headroom(1024, 128, "GTP-echo-resp");
msg = msgb_alloc_headroom(1024, 128, "GTPv1-U-echo-resp");
tx_h = (void *)msgb_put(msg, sizeof(*tx_h));
*tx_h = (struct gtp1u_hdr){
@@ -83,16 +104,21 @@ static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *rem
/* ECHO RESPONSE shall contain a recovery counter */
msgb_put_u8(msg, GTP1U_IEI_RECOVERY);
msgb_put_u8(msg, g_upf->gtp.recovery_count);
msgb_put_u8(msg, g_upf->tunend.recovery_count);
osmo_store16be(msg->tail - tx_h->data1, &tx_h->length);
rc = sendto(dev->gtpv1.ofd.fd, msgb_data(msg), msgb_length(msg), 0, &remote->u.sa, sizeof(*remote));
if (rc < 0) {
int err = errno;
LOG_GTP_DEV(dev, LOGL_ERROR, "GTP1-U sendto(len=%d, to=%s): %s\n", msgb_length(msg),
osmo_sockaddr_to_str(remote), strerror(err));
rc = -errno;
LOG_GTP_DEV(dev, LOGL_ERROR, "-> %s: tx GTPv1-U Echo Response: sendto(len=%d): %s\n",
osmo_sockaddr_to_str(remote), msgb_length(msg), strerror(-rc));
} else {
LOG_GTP_DEV(dev, LOGL_INFO, "-> %s: tx GTPv1-U Echo Response: seq_nr=%u recovery_count=%u\n",
osmo_sockaddr_to_str(remote), seq_nr, g_upf->tunend.recovery_count);
rc = 0;
}
msgb_free(msg);
return rc;
}
@@ -119,39 +145,42 @@ int upf_gtpu_echo_read_cb(struct osmo_fd *ofd, unsigned int what)
/* A GTPv1-U header of size 8 is valid, but this code expects to handle only ECHO REQUEST messages. These are
* required to have a sequence number, hence this check here consciously uses the full sizeof(*h) == 12. */
if (sz < sizeof(*h)) {
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP packet smaller than the GTPv1-U header + sequence nr: %zd < %zu\n",
sz, sizeof(*h));
LOG_GTP_DEV(dev, LOGL_ERROR,
"<- %s: rx GTPv1-U packet smaller than the GTPv1-U header + sequence nr: %zd < %zu\n",
osmo_sockaddr_to_str(&remote), sz, sizeof(*h));
return -1;
}
h = (const struct gtp1u_hdr *)buf;
if (h->version != 1) {
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP v%u: only GTP version 1 supported\n", h->version);
LOG_GTP_DEV(dev, LOGL_ERROR, "<- %s: rx GTPv1-U v%u: only GTP version 1 supported\n",
osmo_sockaddr_to_str(&remote), h->version);
return -1;
}
h_length = osmo_load16be(&h->length);
if (offsetof(struct gtp1u_hdr, data1) + h_length > sz) {
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP: header + h.length = %zu > received bytes = %zd\n",
offsetof(struct gtp1u_hdr, data1) + h_length, sz);
LOG_GTP_DEV(dev, LOGL_ERROR, "<- %s: rx GTPv1-U: header + h.length = %zu > received bytes = %zd\n",
osmo_sockaddr_to_str(&remote), offsetof(struct gtp1u_hdr, data1) + h_length, sz);
return -1;
}
switch (h->msg_type) {
case GTP1U_MSGTYPE_ECHO_REQ:
return rx_echo_req(dev, &remote, h);
return rx_echo_req(dev, &remote, h, sz);
case GTP1U_MSGTYPE_ECHO_RSP:
rx_echo_resp(dev, &remote, h, sz);
return 0;
default:
LOG_GTP_DEV(dev, LOGL_ERROR, "rx: GTPv1-U message type %u not supported\n", h->msg_type);
return -1;
}
return 0;
}
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev)
{
if (dev->gtpv1.ofd.fd == -1) {
LOGP(DGTP, LOGL_ERROR, "Cannot setup GTP-U ECHO: GTP-v1 socket not initialized\n");
LOGP(DGTP, LOGL_ERROR, "Cannot setup GTPv1-U ECHO: socket not initialized\n");
return -EINVAL;
}

View File

@@ -63,25 +63,25 @@ static int upf_nft_run(const char *ruleset)
{
int rc;
if (g_upf->nft.mockup) {
if (g_upf->tunmap.mockup) {
LOGP(DNFT, LOGL_NOTICE, "tunmap/mockup active: not running nft ruleset: '%s'\n", ruleset);
return 0;
}
if (!g_upf->nft.nft_ctx) {
if (!g_upf->tunmap.nft_ctx) {
rc = upf_nft_init();
if (rc)
return rc;
}
rc = nft_run_cmd_from_buffer(g_upf->nft.nft_ctx, ruleset);
rc = nft_run_cmd_from_buffer(g_upf->tunmap.nft_ctx, ruleset);
if (rc < 0) {
LOGP(DNFT, LOGL_ERROR, "error running nft ruleset: rc=%d ruleset=%s\n",
rc, osmo_quote_str_c(OTC_SELECT, ruleset, -1));
return -EIO;
}
LOGP(DNFT, LOGL_DEBUG, "set up nft ruleset: %s\n", osmo_quote_str_c(OTC_SELECT, ruleset, -1));
LOGP(DNFT, LOGL_DEBUG, "run nft ruleset: %s\n", osmo_quote_str_c(OTC_SELECT, ruleset, -1));
return 0;
}
@@ -90,18 +90,18 @@ int upf_nft_init()
int rc;
/* Always set up the default settings, also in mockup mode, so that the VTY reflects sane values */
if (!g_upf->nft.table_name)
g_upf->nft.table_name = talloc_strdup(g_upf, "osmo-upf");
if (!g_upf->tunmap.table_name)
g_upf->tunmap.table_name = talloc_strdup(g_upf, "osmo-upf");
/* When in mockup mode, do not set up nft_ctx and netfilter table */
if (g_upf->nft.mockup) {
if (g_upf->tunmap.mockup) {
LOGP(DNFT, LOGL_NOTICE,
"tunmap/mockup active: not allocating libnftables nft_ctx. FOR TESTING PURPOSES ONLY.\n");
return 0;
}
g_upf->nft.nft_ctx = nft_ctx_new(NFT_CTX_DEFAULT);
if (!g_upf->nft.nft_ctx) {
g_upf->tunmap.nft_ctx = nft_ctx_new(NFT_CTX_DEFAULT);
if (!g_upf->tunmap.nft_ctx) {
LOGP(DNFT, LOGL_ERROR, "cannot allocate libnftables nft_ctx\n");
return -EIO;
}
@@ -109,14 +109,14 @@ int upf_nft_init()
rc = upf_nft_run(upf_nft_tunmap_get_table_init_str(OTC_SELECT));
if (rc) {
LOGP(DNFT, LOGL_ERROR, "Failed to create nft table %s\n",
osmo_quote_str_c(OTC_SELECT, g_upf->nft.table_name, -1));
osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
return rc;
}
LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->nft.table_name, -1));
LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
rc = upf_nft_run(upf_nft_tunmap_get_vmap_init_str(OTC_SELECT));
if (rc) {
LOGP(DNFT, LOGL_ERROR, "Failed to initialize nft verdict map in table %s\n", g_upf->nft.table_name);
LOGP(DNFT, LOGL_ERROR, "Failed to initialize nft verdict map in table %s\n", g_upf->tunmap.table_name);
return rc;
}
return 0;
@@ -124,10 +124,10 @@ int upf_nft_init()
int upf_nft_free()
{
if (!g_upf->nft.nft_ctx)
if (!g_upf->tunmap.nft_ctx)
return 0;
nft_ctx_free(g_upf->nft.nft_ctx);
g_upf->nft.nft_ctx = NULL;
nft_ctx_free(g_upf->tunmap.nft_ctx);
g_upf->tunmap.nft_ctx = NULL;
return 0;
}
@@ -177,7 +177,7 @@ static int tunmap_add_single_direction(char *buf, size_t buflen,
* # add chain for verdict map in postrouting
* add chain inet osmo-upf tunmap-post-123
* # mangle source address and GTP TID at postrouting
* add rule inet osmo-upf tunmap-post-123 ip saddr set 2.2.2.1 @ih,32,32 set 0x00000102 counter accept
* add rule inet osmo-upf tunmap-post-123 ip saddr set 2.2.2.1 udp sport set 2152 @ih,32,32 set 0x00000102 counter accept
*
* # add elements to verdict map, jump to chain
* add element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x00000203 : jump tunmap-pre-123 }
@@ -200,6 +200,7 @@ static int tunmap_add_single_direction(char *buf, size_t buflen,
args->table_name, from_peer->chain_id);
OSMO_STRBUF_PRINTF(sb, " ip saddr set ");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_local);
OSMO_STRBUF_PRINTF(sb, " udp sport set 2152");
OSMO_STRBUF_PRINTF(sb, " @ih,32,32 set 0x%x", to_peer->teid_remote);
OSMO_STRBUF_PRINTF(sb, " counter accept;\n");
@@ -284,49 +285,49 @@ static char *upf_nft_ruleset_tunmap_delete_c(void *ctx, const struct upf_nft_arg
OSMO_NAME_C_IMPL(ctx, 512, "ERROR", upf_nft_ruleset_tunmap_delete_buf, args)
}
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_nft_tunmap_desc *tunmap)
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
/* ACCESS 1.1.1.2:0x102 <---> 2.2.2.1:0x201 UPF 2.2.2.3:0x203 <---> 3.3.3.2:0x302 CORE */
OSMO_STRBUF_PRINTF(sb, "ACCESS ");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.gtp_remote_addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->access.remote_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.gtp_local_addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x UPF ", tunmap->access.local_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.gtp_local_addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->core.local_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.gtp_remote_addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x CORE", tunmap->core.remote_teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.tun.remote.addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->access.tun.remote.teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.tun.local.addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x UPF ", tunmap->access.tun.local.teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.tun.local.addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->core.tun.local.teid);
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.tun.remote.addr);
OSMO_STRBUF_PRINTF(sb, ":0x%x CORE", tunmap->core.tun.remote.teid);
return sb.chars_needed;
}
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_nft_tunmap_desc *tunmap)
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_tunmap *tunmap)
{
OSMO_NAME_C_IMPL(ctx, 128, "ERROR", upf_nft_tunmap_to_str_buf, tunmap)
}
static void upf_nft_args_from_tunmap_desc(struct upf_nft_args *args, const struct upf_nft_tunmap_desc *tunmap)
static void upf_nft_args_from_tunmap(struct upf_nft_args *args, const struct upf_tunmap *tunmap)
{
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.gtp_remote_addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.gtp_local_addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.gtp_remote_addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.gtp_local_addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.tun.remote.addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.tun.local.addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.tun.remote.addr.u.sa) == 0);
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.tun.local.addr.u.sa) == 0);
*args = (struct upf_nft_args){
.table_name = g_upf->nft.table_name,
.table_name = g_upf->tunmap.table_name,
.peer_a = {
.addr_remote = &tunmap->access.gtp_remote_addr,
.teid_remote = tunmap->access.remote_teid,
.addr_local = &tunmap->access.gtp_local_addr,
.teid_local = tunmap->access.local_teid,
.addr_remote = &tunmap->access.tun.remote.addr,
.teid_remote = tunmap->access.tun.remote.teid,
.addr_local = &tunmap->access.tun.local.addr,
.teid_local = tunmap->access.tun.local.teid,
.chain_id = tunmap->access.chain_id,
},
.peer_b = {
.addr_remote = &tunmap->core.gtp_remote_addr,
.teid_remote = tunmap->core.remote_teid,
.addr_local = &tunmap->core.gtp_local_addr,
.teid_local = tunmap->core.local_teid,
.addr_remote = &tunmap->core.tun.remote.addr,
.teid_remote = tunmap->core.tun.remote.teid,
.addr_local = &tunmap->core.tun.local.addr,
.teid_local = tunmap->core.tun.local.teid,
.chain_id = tunmap->core.chain_id,
},
};
@@ -334,49 +335,48 @@ static void upf_nft_args_from_tunmap_desc(struct upf_nft_args *args, const struc
char *upf_nft_tunmap_get_table_init_str(void *ctx)
{
return upf_nft_ruleset_table_create(ctx, g_upf->nft.table_name);
return upf_nft_ruleset_table_create(ctx, g_upf->tunmap.table_name);
}
char *upf_nft_tunmap_get_vmap_init_str(void *ctx)
{
return upf_nft_ruleset_vmap_init(ctx, g_upf->nft.table_name, g_upf->nft.priority_pre,
g_upf->nft.priority_post);
return upf_nft_ruleset_vmap_init(ctx, g_upf->tunmap.table_name, g_upf->tunmap.priority_pre,
g_upf->tunmap.priority_post);
}
static uint32_t chain_id_next(void)
{
g_upf->nft.next_chain_id_state++;
if (!g_upf->nft.next_chain_id_state)
g_upf->nft.next_chain_id_state++;
return g_upf->nft.next_chain_id_state;
}
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_nft_tunmap_desc *tunmap)
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap)
{
struct upf_nft_args args;
if (!tunmap->access.chain_id)
tunmap->access.chain_id = chain_id_next();
if (!tunmap->core.chain_id)
tunmap->core.chain_id = chain_id_next();
upf_nft_args_from_tunmap_desc(&args, tunmap);
upf_nft_args_from_tunmap(&args, tunmap);
return upf_nft_ruleset_tunmap_create_c(ctx, &args);
}
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_nft_tunmap_desc *tunmap)
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap)
{
struct upf_nft_args args;
upf_nft_args_from_tunmap_desc(&args, tunmap);
upf_nft_args_from_tunmap(&args, tunmap);
return upf_nft_ruleset_tunmap_delete_c(ctx, &args);
}
int upf_nft_tunmap_create(struct upf_nft_tunmap_desc *tunmap)
static int upf_nft_tunmap_ensure_chain_id(struct upf_nft_tun *tun)
{
if (tun->chain_id)
return 0;
tun->chain_id = upf_next_chain_id();
if (!tun->chain_id)
return -ENOSPC;
return 0;
}
int upf_nft_tunmap_create(struct upf_tunmap *tunmap)
{
if (upf_nft_tunmap_ensure_chain_id(&tunmap->access)
|| upf_nft_tunmap_ensure_chain_id(&tunmap->core))
return -ENOSPC;
return upf_nft_run(upf_nft_tunmap_get_ruleset_str(OTC_SELECT, tunmap));
}
int upf_nft_tunmap_delete(struct upf_nft_tunmap_desc *tunmap)
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap)
{
return upf_nft_run(upf_nft_tunmap_get_ruleset_del_str(OTC_SELECT, tunmap));
}

View File

@@ -53,7 +53,7 @@ static struct cmd_node cfg_pfcp_node = {
};
#define pfcp_vty (g_upf->pfcp.vty_cfg)
#define tunend_vty (g_upf->gtp.vty_cfg)
#define tunend_vty (g_upf->tunend.vty_cfg)
DEFUN(cfg_pfcp, cfg_pfcp_cmd,
"pfcp",
@@ -102,7 +102,7 @@ static int config_write_tunend(struct vty *vty)
struct tunend_vty_cfg_dev *d;
vty_out(vty, "tunend%s", VTY_NEWLINE);
if (g_upf->gtp.mockup)
if (g_upf->tunend.mockup)
vty_out(vty, " mockup%s", VTY_NEWLINE);
llist_for_each_entry(d, &tunend_vty.devs, entry) {
@@ -124,7 +124,7 @@ DEFUN(cfg_tunend_mockup, cfg_tunend_mockup_cmd,
"mockup",
"don't actually send commands to the GTP kernel module, just return success\n")
{
g_upf->gtp.mockup = true;
g_upf->tunend.mockup = true;
return CMD_SUCCESS;
}
@@ -133,7 +133,7 @@ DEFUN(cfg_tunend_no_mockup, cfg_tunend_no_mockup_cmd,
NO_STR
"operate GTP kernel module normally\n")
{
g_upf->gtp.mockup = false;
g_upf->tunend.mockup = false;
return CMD_SUCCESS;
}
@@ -224,11 +224,11 @@ static int config_write_tunmap(struct vty *vty)
{
vty_out(vty, "tunmap%s", VTY_NEWLINE);
if (g_upf->nft.mockup)
if (g_upf->tunmap.mockup)
vty_out(vty, " mockup%s", VTY_NEWLINE);
if (g_upf->nft.table_name && strcmp(g_upf->nft.table_name, "osmo-upf"))
vty_out(vty, " table-name %s%s", g_upf->nft.table_name, VTY_NEWLINE);
if (g_upf->tunmap.table_name && strcmp(g_upf->tunmap.table_name, "osmo-upf"))
vty_out(vty, " table-name %s%s", g_upf->tunmap.table_name, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -237,7 +237,7 @@ DEFUN(cfg_tunmap_mockup, cfg_tunmap_mockup_cmd,
"mockup",
"don't actually send rulesets to nftables, just return success\n")
{
g_upf->nft.mockup = true;
g_upf->tunmap.mockup = true;
return CMD_SUCCESS;
}
@@ -246,7 +246,7 @@ DEFUN(cfg_tunmap_no_mockup, cfg_tunmap_no_mockup_cmd,
NO_STR
"operate nftables rulesets normally\n")
{
g_upf->nft.mockup = false;
g_upf->tunmap.mockup = false;
return CMD_SUCCESS;
}
@@ -258,7 +258,7 @@ DEFUN(cfg_tunmap_table_name, cfg_tunmap_table_name_cmd,
" The default table name is \"osmo-upf\".\n"
"nft inet table name\n")
{
osmo_talloc_replace_string(g_upf, &g_upf->nft.table_name, argv[0]);
osmo_talloc_replace_string(g_upf, &g_upf->tunmap.table_name, argv[0]);
return CMD_SUCCESS;
}
@@ -296,39 +296,43 @@ DEFUN(show_nft_rule_tunmap_example, show_nft_rule_tunmap_example_cmd,
"Print a complete nftables ruleset for a tunmap filled with example IP addresses and TEIDs\n")
{
struct osmo_sockaddr_str str = {};
struct upf_nft_tunmap_desc d = {
struct upf_tunmap tunmap = {
.access = {
.local_teid = 0x201,
.remote_teid = 0x102,
.tun = {
.local.teid = 0x201,
.remote.teid = 0x102,
},
.chain_id = 123,
},
.core = {
.local_teid = 0x203,
.remote_teid = 0x302,
.tun = {
.local.teid = 0x203,
.remote.teid = 0x302,
},
.chain_id = 321,
},
};
osmo_sockaddr_str_from_str2(&str, "1.1.1.1");
osmo_sockaddr_str_to_sockaddr(&str, &d.access.gtp_remote_addr.u.sas);
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.access.tun.remote.addr.u.sas);
osmo_sockaddr_str_from_str2(&str, "2.2.2.1");
osmo_sockaddr_str_to_sockaddr(&str, &d.access.gtp_local_addr.u.sas);
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.access.tun.local.addr.u.sas);
osmo_sockaddr_str_from_str2(&str, "2.2.2.3");
osmo_sockaddr_str_to_sockaddr(&str, &d.core.gtp_local_addr.u.sas);
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.core.tun.local.addr.u.sas);
osmo_sockaddr_str_from_str2(&str, "3.3.3.3");
osmo_sockaddr_str_to_sockaddr(&str, &d.core.gtp_remote_addr.u.sas);
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.core.tun.remote.addr.u.sas);
vty_out(vty, "%% init verdict map:%s", VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_table_init_str(OTC_SELECT), VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_vmap_init_str(OTC_SELECT), VTY_NEWLINE);
vty_out(vty, "%% add tunmap:%s", VTY_NEWLINE);
vty_out(vty, "%% %s%s", upf_nft_tunmap_to_str_c(OTC_SELECT, &d), VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_str(OTC_SELECT, &d), VTY_NEWLINE);
vty_out(vty, "%% %s%s", upf_nft_tunmap_to_str_c(OTC_SELECT, &tunmap), VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_str(OTC_SELECT, &tunmap), VTY_NEWLINE);
vty_out(vty, "%% delete tunmap:%s", VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_del_str(OTC_SELECT, &d), VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_del_str(OTC_SELECT, &tunmap), VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -477,7 +481,7 @@ DEFUN(show_session, show_session_cmd,
}
}
}
vty_out(vty, "(%d fully-active + %d partially active + %d inactive)%s",
vty_out(vty, "(%d fully-active + %d active with some PDR/FAR ignored + %d inactive)%s",
fully_active_count, active_count, inactive_count, VTY_NEWLINE);
return CMD_SUCCESS;
}

View File

@@ -1,3 +1,7 @@
SUBDIRS = \
unique_ids \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -17,7 +21,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
} >'$(srcdir)/package.m4'
EXTRA_DIST = \
upf.vty \
$(srcdir)/*.vty \
testsuite.at \
$(srcdir)/package.m4 \
$(TESTSUITE) \

View File

@@ -18,13 +18,13 @@ add rule inet osmo-upf post meta mark vmap @tunmap-post;
add chain inet osmo-upf tunmap-pre-123;
add rule inet osmo-upf tunmap-pre-123 ip daddr set 3.3.3.3 meta mark set 123 counter accept;
add chain inet osmo-upf tunmap-post-123;
add rule inet osmo-upf tunmap-post-123 ip saddr set 2.2.2.3 @ih,32,32 set 0x302 counter accept;
add rule inet osmo-upf tunmap-post-123 ip saddr set 2.2.2.3 udp sport set 2152 @ih,32,32 set 0x302 counter accept;
add element inet osmo-upf tunmap-pre { 2.2.2.1 . 0x201 : jump tunmap-pre-123 };
add element inet osmo-upf tunmap-post { 123 : jump tunmap-post-123 };
add chain inet osmo-upf tunmap-pre-321;
add rule inet osmo-upf tunmap-pre-321 ip daddr set 1.1.1.1 meta mark set 321 counter accept;
add chain inet osmo-upf tunmap-post-321;
add rule inet osmo-upf tunmap-post-321 ip saddr set 2.2.2.1 @ih,32,32 set 0x102 counter accept;
add rule inet osmo-upf tunmap-post-321 ip saddr set 2.2.2.1 udp sport set 2152 @ih,32,32 set 0x102 counter accept;
add element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x203 : jump tunmap-pre-321 };
add element inet osmo-upf tunmap-post { 321 : jump tunmap-post-321 };

View File

@@ -1,2 +1,9 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([unique_ids_test])
AT_KEYWORDS([unique_ids_test])
cat $abs_srcdir/unique_ids/unique_ids_test.ok > expout
cat $abs_srcdir/unique_ids/unique_ids_test.err > experr
AT_CHECK([$abs_top_builddir/tests/unique_ids/unique_ids_test], [], [expout], [experr])
AT_CLEANUP

View File

@@ -0,0 +1,41 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOGTLV_CFLAGS) \
$(LIBOSMOPFCP_CFLAGS) \
$(LIBGTPNL_CFLAGS) \
$(LIBNFTNL_CFLAGS) \
$(LIBNFTABLES_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
EXTRA_DIST = \
unique_ids_test.ok \
unique_ids_test.err \
$(NULL)
check_PROGRAMS = \
unique_ids_test \
$(NULL)
unique_ids_test_SOURCES = \
unique_ids_test.c \
$(NULL)
unique_ids_test_LDADD = \
$(top_builddir)/src/osmo-upf/libupf.la \
$(NULL)
unique_ids_test_LDFLAGS = \
-no-install \
$(NULL)
.PHONY: update_exp
update_exp:
$(builddir)/unique_ids_test >$(srcdir)/unique_ids_test.ok 2>$(srcdir)/unique_ids_test.err

View File

@@ -0,0 +1,575 @@
/* OsmoUPF: Verify that skipping used ids works for: UP-SEID, GTP local TEID, nft ruleset chain_id. */
/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <getopt.h>
#include <nftables/libnftables.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/application.h>
#include <osmocom/pfcp/pfcp_endpoint.h>
#include <osmocom/upf/upf.h>
#include <osmocom/upf/netinst.h>
#include <osmocom/upf/up_endpoint.h>
#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>
#define log(FMT, ARGS...) fprintf(stderr, FMT, ##ARGS)
#define log_assert(COND) do { \
log("assert(" #COND ")\n"); \
OSMO_ASSERT(COND); \
} while (0)
#define log_assert_expect_failure(COND) do { \
log("assert(" #COND ") <-- EXPECTED TO FAIL (known error)\n"); \
OSMO_ASSERT(!(COND)); \
} while (0)
void *main_ctx;
void *ctx;
/* The override of osmo_pfcp_endpoint_tx() stores any Session Establishment Response's UP-SEID here, so that this test
* can reference specific sessions later.
*/
uint64_t last_up_seid = 0;
void select_poll(void)
{
while (osmo_select_main_ctx(1));
}
static void setup(const char *name)
{
log("\n===== START of %s\n", name);
ctx = talloc_named_const(main_ctx, 0, name);
g_upf_alloc(ctx);
osmo_talloc_replace_string(g_upf, &g_upf->pfcp.vty_cfg.local_addr, "1.1.1.1");
OSMO_ASSERT(netinst_add(g_upf, &g_upf->netinst, "default", "1.1.1.1", NULL));
/* PFCP endpoint recovery timestamp overridden by time() below */
upf_pfcp_init();
/* but do not upf_pfcp_listen() */
upf_nft_init();
select_poll();
log("\n");
}
static void cleanup(void)
{
up_endpoint_free(&g_upf->pfcp.ep);
upf_gtp_devs_close();
upf_gtp_genl_close();
upf_nft_free();
log("\n===== END of %s\n", talloc_get_name(ctx));
talloc_free(ctx);
}
static struct osmo_sockaddr *str2addr(const char *addr, uint16_t port)
{
static struct osmo_sockaddr osa;
struct osmo_sockaddr_str str;
osmo_sockaddr_str_from_str(&str, addr, port);
osmo_sockaddr_str_to_sockaddr(&str, &osa.u.sas);
return &osa;
}
static struct up_peer *have_peer(const char *remote_addr, uint16_t port)
{
return up_peer_find_or_add(g_upf->pfcp.ep, str2addr(remote_addr, port));
}
static struct osmo_pfcp_msg *new_pfcp_msg_for_osmo_upf_rx(struct up_peer *from_peer, enum osmo_pfcp_message_type msg_type)
{
/* pfcp_endpoint discards received messages immediately after dispatching; in this test, allocate them in
* OTC_SELECT so they get discarded on the next select_poll().
* osmo_pfcp_msg_alloc_rx() is not useful here, it creates a blank struct to be decoded from raw data; instead,
* use osmo_pfcp_msg_alloc_tx_req() which properly sets up the internal structures to match the given msg_type,
* and when that is done set m->rx = true to indicate it is a message received by osmo-upf. */
struct osmo_pfcp_msg *m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &from_peer->remote_addr, msg_type);
m->rx = true;
return m;
}
static void peer_assoc(struct up_peer *peer)
{
struct osmo_pfcp_msg *m = new_pfcp_msg_for_osmo_upf_rx(peer, OSMO_PFCP_MSGT_ASSOC_SETUP_REQ);
m->ies.assoc_setup_req.recovery_time_stamp = 1234;
osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_ASSOC_SETUP_REQ, m);
select_poll();
}
static int next_teid = 0x100;
static int next_cp_seid = 0x100;
/* Send a PFCP Session Establishment Request, and return the created session */
static struct up_session *session_est_tunmap(struct up_peer *peer)
{
struct osmo_pfcp_msg *m;
struct osmo_pfcp_ie_f_seid cp_f_seid;
struct osmo_pfcp_ie_f_teid f_teid_access_local;
struct osmo_pfcp_ie_outer_header_creation ohc_access;
struct osmo_pfcp_ie_f_teid f_teid_core_local;
struct osmo_pfcp_ie_outer_header_creation ohc_core;
struct osmo_pfcp_ie_apply_action aa = {};
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
.choose_flag = true,
.choose = {
.ipv4_addr = true,
},
};
ohc_access = (struct osmo_pfcp_ie_outer_header_creation){
.teid_present = true,
.teid = next_teid++,
.ip_addr = {
.v4_present = true,
.v4 = *str2addr("5.6.7.8", 0),
},
};
osmo_pfcp_bits_set(ohc_access.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
f_teid_core_local = (struct osmo_pfcp_ie_f_teid){
.choose_flag = true,
.choose = {
.ipv4_addr = true,
},
};
ohc_core = (struct osmo_pfcp_ie_outer_header_creation){
.teid_present = true,
.teid = next_teid++,
.ip_addr = {
.v4_present = true,
.v4 = *str2addr("13.14.15.16", 0),
},
};
osmo_pfcp_bits_set(ohc_core.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
.seid = next_cp_seid++,
};
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_upf->pfcp.ep->pfcp_ep));
m = new_pfcp_msg_for_osmo_upf_rx(peer, OSMO_PFCP_MSGT_SESSION_EST_REQ);
m->h.seid_present = true;
m->h.seid = 0;
/* GTP tunmap: remove header from both directions, and add header in both directions */
m->ies.session_est_req = (struct osmo_pfcp_msg_session_est_req){
.node_id = m->ies.session_est_req.node_id,
.cp_f_seid_present = true,
.cp_f_seid = cp_f_seid,
.create_pdr_count = 2,
.create_pdr = {
{
.pdr_id = 1,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
.local_f_teid_present = true,
.local_f_teid = f_teid_core_local,
},
.outer_header_removal_present = true,
.outer_header_removal = {
.desc = OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4,
},
.far_id_present = true,
.far_id = 1,
},
{
.pdr_id = 2,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
.local_f_teid_present = true,
.local_f_teid = f_teid_access_local,
},
.outer_header_removal_present = true,
.outer_header_removal = {
.desc = OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4,
},
.far_id_present = true,
.far_id = 2,
},
},
.create_far_count = 2,
.create_far = {
{
.far_id = 1,
.forw_params_present = true,
.forw_params = {
.destination_iface = OSMO_PFCP_DEST_IFACE_ACCESS,
.outer_header_creation_present = true,
.outer_header_creation = ohc_access,
},
.apply_action = aa,
},
{
.far_id = 2,
.forw_params_present = true,
.forw_params = {
.destination_iface = OSMO_PFCP_DEST_IFACE_CORE,
.outer_header_creation_present = true,
.outer_header_creation = ohc_core,
},
.apply_action = aa,
},
},
};
osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_SESSION_EST_REQ, m);
select_poll();
return up_session_find_by_up_seid(peer, last_up_seid);
}
static void session_del(struct up_session *session)
{
struct osmo_pfcp_msg *m;
log_assert(session);
m = new_pfcp_msg_for_osmo_upf_rx(session->up_peer, OSMO_PFCP_MSGT_SESSION_DEL_REQ);
m->h.seid_present = true;
m->h.seid = session->up_seid;
osmo_fsm_inst_dispatch(session->fi, UP_SESSION_EV_RX_SESSION_DEL_REQ, m);
select_poll();
}
static void dump_state(void)
{
struct up_peer *peer;
log("\n state:\n");
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
struct up_session *session;
int bkt;
log(" | peer %s %s\n", peer->fi->name, osmo_fsm_inst_state_name(peer->fi));
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
struct up_gtp_action *a;
llist_for_each_entry(a, &session->active_gtp_actions, entry) {
if (a->kind != UP_GTP_U_TUNMAP)
continue;
log(" | session[%s]: UP-SEID 0x%"PRIx64"; chain_id access=%u core=%u;"
" local TEID access=0x%x core=0x%x\n",
osmo_fsm_inst_state_name(session->fi),
session->up_seid,
a->tunmap.access.chain_id, a->tunmap.core.chain_id,
a->tunmap.access.tun.local.teid, a->tunmap.core.tun.local.teid);
}
}
}
log("\n");
}
static void test_skip_used_id(void)
{
struct up_peer *peer;
struct up_session *s1;
uint64_t s1_up_seid;
struct up_session *s2;
struct up_session *s3;
struct up_session *s4;
struct up_gtp_action *a;
setup(__func__);
log("PFCP Associate peer\n");
peer = have_peer("1.2.3.4", 1234);
peer_assoc(peer);
dump_state();
/* Make sure to start out all IDs with 1 */
g_upf->pfcp.ep->next_up_seid_state = 0;
g_upf->gtp.next_local_teid_state = 0;
g_upf->tunmap.next_chain_id_state = 0;
log("set up tunmap, which assigns first UP-SEID 0x1, local-TEID 0x1 and 0x2, chain_ids 1 and 2\n");
s1 = session_est_tunmap(peer);
dump_state();
log_assert(s1->up_seid == 1);
a = llist_first_entry_or_null(&s1->active_gtp_actions, struct up_gtp_action, entry);
log_assert(a);
log_assert(a->kind == UP_GTP_U_TUNMAP);
log_assert(a->tunmap.core.tun.local.teid == 1);
log_assert(a->tunmap.access.tun.local.teid == 2);
log_assert(a->tunmap.access.chain_id == 1);
log_assert(a->tunmap.core.chain_id == 2);
log("\n");
log("simulate wrapping of IDs back to 1\n");
g_upf->pfcp.ep->next_up_seid_state = 0;
g_upf->gtp.next_local_teid_state = 0;
g_upf->tunmap.next_chain_id_state = 0;
log("set up second tunmap, should use distinct IDs\n");
s2 = session_est_tunmap(peer);
dump_state();
log_assert(s2->up_seid == 2);
a = llist_first_entry_or_null(&s2->active_gtp_actions, struct up_gtp_action, entry);
log_assert(a);
log_assert(a->kind == UP_GTP_U_TUNMAP);
log_assert(a->tunmap.core.tun.local.teid == 3);
log_assert(a->tunmap.access.tun.local.teid == 4);
log_assert(a->tunmap.access.chain_id == 3);
log_assert(a->tunmap.core.chain_id == 4);
log("\n");
log("drop first tunmap (%s)\n", s1->fi->name);
s1_up_seid = s1->up_seid;
session_del(s1);
dump_state();
log_assert(up_session_find_by_up_seid(peer, s1_up_seid) == NULL);
log("\n");
log("again wrap all ID state back to 1\n");
g_upf->pfcp.ep->next_up_seid_state = 0;
g_upf->gtp.next_local_teid_state = 0;
g_upf->tunmap.next_chain_id_state = 0;
log("set up third tunmap, should now re-use same IDs as the first session\n");
s3 = session_est_tunmap(peer);
dump_state();
log_assert(s3->up_seid == 1);
a = llist_first_entry_or_null(&s3->active_gtp_actions, struct up_gtp_action, entry);
log_assert(a);
log_assert(a->kind == UP_GTP_U_TUNMAP);
log_assert(a->tunmap.core.tun.local.teid == 1);
log_assert(a->tunmap.access.tun.local.teid == 2);
log_assert(a->tunmap.access.chain_id == 1);
log_assert(a->tunmap.core.chain_id == 2);
log("\n");
log("set up 4th tunmap; chain_id state would use 3 and 4, but they are in use, so should assign 5 and 6\n");
s4 = session_est_tunmap(peer);
dump_state();
log_assert(s4->up_seid == 3);
a = llist_first_entry_or_null(&s4->active_gtp_actions, struct up_gtp_action, entry);
log_assert(a);
log_assert(a->kind == UP_GTP_U_TUNMAP);
log_assert(a->tunmap.core.tun.local.teid == 5);
log_assert(a->tunmap.access.tun.local.teid == 6);
log_assert(a->tunmap.access.chain_id == 5);
log_assert(a->tunmap.core.chain_id == 6);
log("\n");
cleanup();
}
static const struct log_info_cat test_default_categories[] = {
[DREF] = {
.name = "DREF",
.description = "Reference Counting",
.enabled = 1, .loglevel = LOGL_DEBUG,
.color = OSMO_LOGCOLOR_DARKGREY,
},
[DPEER] = {
.name = "DPEER",
.description = "PFCP peer association",
.enabled = 1, .loglevel = LOGL_DEBUG,
.color = OSMO_LOGCOLOR_YELLOW,
},
[DSESSION] = {
.name = "DSESSION",
.description = "PFCP sessions",
.enabled = 1, .loglevel = LOGL_DEBUG,
.color = OSMO_LOGCOLOR_BLUE,
},
[DGTP] = {
.name = "DGTP",
.description = "GTP tunneling",
.enabled = 1, .loglevel = LOGL_DEBUG,
.color = OSMO_LOGCOLOR_PURPLE,
},
[DNFT] = {
.name = "DNFT",
.description = "GTP forwarding rules via linux netfilter",
.enabled = 1, .loglevel = LOGL_DEBUG,
.color = OSMO_LOGCOLOR_PURPLE,
},
};
const struct log_info log_info = {
.cat = test_default_categories,
.num_cat = ARRAY_SIZE(test_default_categories),
};
static struct {
bool verbose;
} cmdline_opts = {
.verbose = false,
};
static void print_help(const char *program)
{
printf("Usage:\n"
" %s [-v]\n"
"Options:\n"
" -h --help show this text.\n"
" -v --verbose print source file and line numbers\n",
program
);
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"verbose", 1, 0, 'v'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hv",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_help(argv[0]);
exit(0);
case 'v':
cmdline_opts.verbose = true;
break;
default:
/* catch unknown options *as well as* missing arguments. */
fprintf(stderr, "Error in command line options. Exiting.\n");
exit(-1);
break;
}
}
}
int main(int argc, char **argv)
{
handle_options(argc, argv);
main_ctx = talloc_named_const(NULL, 0, "main");
msgb_talloc_ctx_init(main_ctx, 0);
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
osmo_init_logging2(main_ctx, &log_info);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_print_extended_timestamp(osmo_stderr_target, 0);
log_set_all_filter(osmo_stderr_target, 1);
if (cmdline_opts.verbose) {
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
log_set_use_color(osmo_stderr_target, 1);
} else {
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_use_color(osmo_stderr_target, 0);
}
osmo_fsm_log_timeouts(true);
osmo_fsm_log_addr(false);
/* actual tests */
test_skip_used_id();
log_fini();
talloc_free(main_ctx);
return 0;
}
/* overrides */
int osmo_pfcp_endpoint_tx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m)
{
enum osmo_pfcp_cause *cause;
log("\n[test override] PFCP tx:\n%s\n\n", osmo_pfcp_msg_to_str_c(OTC_SELECT, m));
last_up_seid = 0;
cause = osmo_pfcp_msg_cause(m);
switch (m->h.message_type) {
case OSMO_PFCP_MSGT_SESSION_EST_RESP:
if (*cause == OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) {
last_up_seid = m->ies.session_est_resp.up_f_seid.seid;
log("osmo-upf created session 0x%"PRIx64"\n\n", last_up_seid);
}
break;
default:
break;
};
osmo_pfcp_msg_free(m);
return 0;
}
static void *fake_nft_ctx = (void *)0x1;
struct nft_ctx *nft_ctx_new(uint32_t flags)
{
log("[test override] %s()\n", __func__);
return fake_nft_ctx;
}
void nft_ctx_free(struct nft_ctx *ctx)
{
log("[test override] %s()\n", __func__);
log_assert(ctx == fake_nft_ctx);
}
int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
{
log("\n[test override] %s():\n%s\n", __func__, buf);
return 0;
}
/* for deterministic recovery_time_stamp */
time_t time(time_t *tloc)
{
log("[test override] %s()\n", __func__);
return 0;
}

View File

@@ -0,0 +1,412 @@
===== START of test_skip_used_id
[test override] time()
[test override] time()
DLPFCP NOTICE PFCP endpoint: recovery timestamp = 0x83aa7e80 (0 seconds since UNIX epoch, which is 2208988800 seconds since NTP era 0; IETF RFC 5905)
[test override] nft_ctx_new()
[test override] nft_run_cmd_from_buffer():
add table inet osmo-upf { flags owner; };
DNFT DEBUG run nft ruleset: "add table inet osmo-upf { flags owner; };\n"
DNFT NOTICE Created nft table "osmo-upf"
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; };
add chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; };
add map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; };
add map inet osmo-upf tunmap-post { typeof meta mark : verdict; };
add rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre;
add rule inet osmo-upf post meta mark vmap @tunmap-post;
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; };\nadd chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; };\nadd map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; };\nadd map inet osmo-upf tunmap-post { typeof meta mark : verdict; };\nadd rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre;\nadd rule inet osmo-upf post meta mark vmap @tunmap-post;\n"
PFCP Associate peer
DPEER DEBUG up_peer{NOT_ASSOCIATED}: Allocated
DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: Updated id
DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: Received Event UP_PEER_EV_RX_ASSOC_SETUP_REQ
DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: State change to ASSOCIATED (no timeout)
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)
[test override] PFCP tx:
PFCPv1 ASSOC_SETUP_RESP hdr={seq=0} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'Recovery Time Stamp'=2208988800 'UP Function Features'=FTUP+BUNDL+RTTL }
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
DPEER NOTICE up_peer(1-2-3-4){ASSOCIATED}: Peer associated, Node-Id=v4:unsupported family 0. Local UP features: [FTUP+BUNDL+RTTL]; Peer CP features: [-]
state:
| peer up_peer(1-2-3-4) ASSOCIATED
set up tunmap, which assigns first UP-SEID 0x1, local-TEID 0x1 and 0x2, chain_ids 1 and 2
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Updated id
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x1){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x1,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x100,v4:5.6.7.8}
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x2,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-2{src:Access TEID-0x2,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x101,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: PDR-2{src:Access TEID-0x2,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x101,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: + PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x100,v4:5.6.7.8}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: want: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: enabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-1;
add rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;
add chain inet osmo-upf tunmap-post-1;
add rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x101 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };
add element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };
add chain inet osmo-upf tunmap-pre-2;
add rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;
add chain inet osmo-upf tunmap-post-2;
add rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x100 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };
add element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x101 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };\nadd element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };\nadd chain inet osmo-upf tunmap-pre-2;\nadd rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;\nadd chain inet osmo-upf tunmap-post-2;\nadd rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x100 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };\nadd element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1: Enabled tunmap, nft chain IDs: access--1-> <-2--core
[test override] PFCP tx:
PFCPv1 SESSION_EST_RESP hdr={seq=0 SEID=0x100} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'F-SEID'=0x1,v4:1.1.1.1 'Created PDR'={ { 'PDR ID'=1 'F-TEID'=TEID-0x1,v4:1.1.1.1 }, { 'PDR ID'=2 'F-TEID'=TEID-0x2,v4:1.1.1.1 } } }
osmo-upf created session 0x1
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: - msg-tx: now used by 1 (msg-rx)
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: State change to ESTABLISHED (no timeout)
DSESSION NOTICE up_session(1-2-3-4-0x1){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x100 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
state:
| peer up_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
assert(s1->up_seid == 1)
assert(a)
assert(a->kind == UP_GTP_U_TUNMAP)
assert(a->tunmap.core.tun.local.teid == 1)
assert(a->tunmap.access.tun.local.teid == 2)
assert(a->tunmap.access.chain_id == 1)
assert(a->tunmap.core.chain_id == 2)
simulate wrapping of IDs back to 1
set up second tunmap, should use distinct IDs
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x2
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Updated id
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x2){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x2){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: Allocated new local F-TEID TEID-0x3,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: New PDR-1{src:Core TEID-0x3,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x102,v4:5.6.7.8}
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: Allocated new local F-TEID TEID-0x4,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: New PDR-2{src:Access TEID-0x4,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x103,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Active PDR set: PDR-2{src:Access TEID-0x4,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x103,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Active PDR set: + PDR-1{src:Core TEID-0x3,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x102,v4:5.6.7.8}
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: want: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: enabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-3;
add rule inet osmo-upf tunmap-pre-3 ip daddr set 13.14.15.16 meta mark set 3 counter accept;
add chain inet osmo-upf tunmap-post-3;
add rule inet osmo-upf tunmap-post-3 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x103 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 : jump tunmap-pre-3 };
add element inet osmo-upf tunmap-post { 3 : jump tunmap-post-3 };
add chain inet osmo-upf tunmap-pre-4;
add rule inet osmo-upf tunmap-pre-4 ip daddr set 5.6.7.8 meta mark set 4 counter accept;
add chain inet osmo-upf tunmap-post-4;
add rule inet osmo-upf tunmap-post-4 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x102 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 : jump tunmap-pre-4 };
add element inet osmo-upf tunmap-post { 4 : jump tunmap-post-4 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-3;\nadd rule inet osmo-upf tunmap-pre-3 ip daddr set 13.14.15.16 meta mark set 3 counter accept;\nadd chain inet osmo-upf tunmap-post-3;\nadd rule inet osmo-upf tunmap-post-3 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x103 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 : jump tunmap-pre-3 };\nadd element inet osmo-upf tunmap-post { 3 : jump tunmap-post-3 };\nadd chain inet osmo-upf tunmap-pre-4;\nadd rule inet osmo-upf tunmap-pre-4 ip daddr set 5.6.7.8 meta mark set 4 counter accept;\nadd chain inet osmo-upf tunmap-post-4;\nadd rule inet osmo-upf tunmap-post-4 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x102 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 : jump tunmap-pre-4 };\nadd element inet osmo-upf tunmap-post { 4 : jump tunmap-post-4 };\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1: Enabled tunmap, nft chain IDs: access--3-> <-4--core
[test override] PFCP tx:
PFCPv1 SESSION_EST_RESP hdr={seq=0 SEID=0x101} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'F-SEID'=0x2,v4:1.1.1.1 'Created PDR'={ { 'PDR ID'=1 'F-TEID'=TEID-0x3,v4:1.1.1.1 }, { 'PDR ID'=2 'F-TEID'=TEID-0x4,v4:1.1.1.1 } } }
osmo-upf created session 0x2
DREF DEBUG up_session(1-2-3-4-0x2){INIT}: - msg-tx: now used by 1 (msg-rx)
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: State change to ESTABLISHED (no timeout)
DSESSION NOTICE up_session(1-2-3-4-0x2){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x101 SEID-l:0x2 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x2){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
state:
| peer up_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3
assert(s2->up_seid == 2)
assert(a)
assert(a->kind == UP_GTP_U_TUNMAP)
assert(a->tunmap.core.tun.local.teid == 3)
assert(a->tunmap.access.tun.local.teid == 4)
assert(a->tunmap.access.chain_id == 3)
assert(a->tunmap.core.chain_id == 4)
drop first tunmap (up_session(1-2-3-4-0x1))
assert(session)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_RX_SESSION_DEL_REQ
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: + msg-tx: now used by 1 (msg-tx)
[test override] PFCP tx:
PFCPv1 SESSION_DEL_RESP hdr={seq=0 SEID=0x100} ies={ 'Cause'=Request accepted (success) }
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-tx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
DSESSION NOTICE up_session(1-2-3-4-0x1){ESTABLISHED}: Session releasing: peer:1.2.3.4 SEID-r:0x100 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };
delete element inet osmo-upf tunmap-post { 1 };
delete chain inet osmo-upf tunmap-pre-1;
delete chain inet osmo-upf tunmap-post-1;
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };
delete element inet osmo-upf tunmap-post { 2 };
delete chain inet osmo-upf tunmap-pre-2;
delete chain inet osmo-upf tunmap-post-2;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete element inet osmo-upf tunmap-post { 2 };\ndelete chain inet osmo-upf tunmap-pre-2;\ndelete chain inet osmo-upf tunmap-post-2;\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1: Disabled tunmap, nft chain IDs: access--1-> <-2--core
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: State change to WAIT_USE_COUNT (no timeout)
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: GTP actions: 0 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Terminating (cause = OSMO_FSM_TERM_REGULAR)
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Removing from parent up_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: GTP actions: 0 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Deallocated
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_SESSION_TERM
state:
| peer up_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3
assert(up_session_find_by_up_seid(peer, s1_up_seid) == NULL)
again wrap all ID state back to 1
set up third tunmap, should now re-use same IDs as the first session
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Updated id
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x1){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x1,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x104,v4:5.6.7.8}
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x2,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-2{src:Access TEID-0x2,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x105,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: PDR-2{src:Access TEID-0x2,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x105,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: + PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x104,v4:5.6.7.8}
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: want: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: enabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-1;
add rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;
add chain inet osmo-upf tunmap-post-1;
add rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x105 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };
add element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };
add chain inet osmo-upf tunmap-pre-2;
add rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;
add chain inet osmo-upf tunmap-post-2;
add rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x104 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };
add element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x105 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };\nadd element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };\nadd chain inet osmo-upf tunmap-pre-2;\nadd rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;\nadd chain inet osmo-upf tunmap-post-2;\nadd rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x104 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };\nadd element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1: Enabled tunmap, nft chain IDs: access--1-> <-2--core
[test override] PFCP tx:
PFCPv1 SESSION_EST_RESP hdr={seq=0 SEID=0x102} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'F-SEID'=0x1,v4:1.1.1.1 'Created PDR'={ { 'PDR ID'=1 'F-TEID'=TEID-0x1,v4:1.1.1.1 }, { 'PDR ID'=2 'F-TEID'=TEID-0x2,v4:1.1.1.1 } } }
osmo-upf created session 0x1
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: - msg-tx: now used by 1 (msg-rx)
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: State change to ESTABLISHED (no timeout)
DSESSION NOTICE up_session(1-2-3-4-0x1){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x102 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
state:
| peer up_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3
assert(s3->up_seid == 1)
assert(a)
assert(a->kind == UP_GTP_U_TUNMAP)
assert(a->tunmap.core.tun.local.teid == 1)
assert(a->tunmap.access.tun.local.teid == 2)
assert(a->tunmap.access.chain_id == 1)
assert(a->tunmap.core.chain_id == 2)
set up 4th tunmap; chain_id state would use 3 and 4, but they are in use, so should assign 5 and 6
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x3
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Updated id
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x3){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x3){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: Allocated new local F-TEID TEID-0x5,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: New PDR-1{src:Core TEID-0x5,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x106,v4:5.6.7.8}
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: Allocated new local F-TEID TEID-0x6,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: New PDR-2{src:Access TEID-0x6,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x107,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Active PDR set: PDR-2{src:Access TEID-0x6,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-2{FORW dst:Core,GTP_U_UDP_IPV4,TEID:0x107,v4:13.14.15.16}
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Active PDR set: + PDR-1{src:Core TEID-0x5,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x106,v4:5.6.7.8}
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: want: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: enabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-5;
add rule inet osmo-upf tunmap-pre-5 ip daddr set 13.14.15.16 meta mark set 5 counter accept;
add chain inet osmo-upf tunmap-post-5;
add rule inet osmo-upf tunmap-post-5 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x107 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 : jump tunmap-pre-5 };
add element inet osmo-upf tunmap-post { 5 : jump tunmap-post-5 };
add chain inet osmo-upf tunmap-pre-6;
add rule inet osmo-upf tunmap-pre-6 ip daddr set 5.6.7.8 meta mark set 6 counter accept;
add chain inet osmo-upf tunmap-post-6;
add rule inet osmo-upf tunmap-post-6 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x106 counter accept;
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 : jump tunmap-pre-6 };
add element inet osmo-upf tunmap-post { 6 : jump tunmap-post-6 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-5;\nadd rule inet osmo-upf tunmap-pre-5 ip daddr set 13.14.15.16 meta mark set 5 counter accept;\nadd chain inet osmo-upf tunmap-post-5;\nadd rule inet osmo-upf tunmap-post-5 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x107 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 : jump tunmap-pre-5 };\nadd element inet osmo-upf tunmap-post { 5 : jump tunmap-post-5 };\nadd chain inet osmo-upf tunmap-pre-6;\nadd rule inet osmo-upf tunmap-pre-6 ip daddr set 5.6.7.8 meta mark set 6 counter accept;\nadd chain inet osmo-upf tunmap-post-6;\nadd rule inet osmo-upf tunmap-post-6 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x106 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 : jump tunmap-pre-6 };\nadd element inet osmo-upf tunmap-post { 6 : jump tunmap-post-6 };\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1: Enabled tunmap, nft chain IDs: access--5-> <-6--core
[test override] PFCP tx:
PFCPv1 SESSION_EST_RESP hdr={seq=0 SEID=0x103} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'F-SEID'=0x3,v4:1.1.1.1 'Created PDR'={ { 'PDR ID'=1 'F-TEID'=TEID-0x5,v4:1.1.1.1 }, { 'PDR ID'=2 'F-TEID'=TEID-0x6,v4:1.1.1.1 } } }
osmo-upf created session 0x3
DREF DEBUG up_session(1-2-3-4-0x3){INIT}: - msg-tx: now used by 1 (msg-rx)
DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: State change to ESTABLISHED (no timeout)
DSESSION NOTICE up_session(1-2-3-4-0x3){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x103 SEID-l:0x3 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x3){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
state:
| peer up_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x3; chain_id access=5 core=6; local TEID access=0x6 core=0x5
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3
assert(s4->up_seid == 3)
assert(a)
assert(a->kind == UP_GTP_U_TUNMAP)
assert(a->tunmap.core.tun.local.teid == 5)
assert(a->tunmap.access.tun.local.teid == 6)
assert(a->tunmap.access.chain_id == 5)
assert(a->tunmap.core.chain_id == 6)
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Terminating (cause = OSMO_FSM_TERM_REGULAR)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 };
delete element inet osmo-upf tunmap-post { 5 };
delete chain inet osmo-upf tunmap-pre-5;
delete chain inet osmo-upf tunmap-post-5;
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 };
delete element inet osmo-upf tunmap-post { 6 };
delete chain inet osmo-upf tunmap-pre-6;
delete chain inet osmo-upf tunmap-post-6;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 };\ndelete element inet osmo-upf tunmap-post { 5 };\ndelete chain inet osmo-upf tunmap-pre-5;\ndelete chain inet osmo-upf tunmap-post-5;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 };\ndelete element inet osmo-upf tunmap-post { 6 };\ndelete chain inet osmo-upf tunmap-pre-6;\ndelete chain inet osmo-upf tunmap-post-6;\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1: Disabled tunmap, nft chain IDs: access--5-> <-6--core
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Deallocated
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };
delete element inet osmo-upf tunmap-post { 1 };
delete chain inet osmo-upf tunmap-pre-1;
delete chain inet osmo-upf tunmap-post-1;
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };
delete element inet osmo-upf tunmap-post { 2 };
delete chain inet osmo-upf tunmap-pre-2;
delete chain inet osmo-upf tunmap-post-2;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete element inet osmo-upf tunmap-post { 2 };\ndelete chain inet osmo-upf tunmap-pre-2;\ndelete chain inet osmo-upf tunmap-post-2;\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1: Disabled tunmap, nft chain IDs: access--1-> <-2--core
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Deallocated
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 };
delete element inet osmo-upf tunmap-post { 3 };
delete chain inet osmo-upf tunmap-pre-3;
delete chain inet osmo-upf tunmap-post-3;
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 };
delete element inet osmo-upf tunmap-post { 4 };
delete chain inet osmo-upf tunmap-pre-4;
delete chain inet osmo-upf tunmap-post-4;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 };\ndelete element inet osmo-upf tunmap-post { 3 };\ndelete chain inet osmo-upf tunmap-pre-3;\ndelete chain inet osmo-upf tunmap-post-3;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 };\ndelete element inet osmo-upf tunmap-post { 4 };\ndelete chain inet osmo-upf tunmap-pre-4;\ndelete chain inet osmo-upf tunmap-post-4;\n"
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1: Disabled tunmap, nft chain IDs: access--3-> <-4--core
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Deallocated
DPEER NOTICE up_peer(1-2-3-4){ASSOCIATED}: Peer removed
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Freeing instance
DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Deallocated
[test override] nft_ctx_free()
assert(ctx == fake_nft_ctx)
===== END of test_skip_used_id

View File