mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
synced 2025-11-02 21:13:46 +00:00
Compare commits
85 Commits
neels/demo
...
neels/gtpl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07c95ce770 | ||
|
|
a01dd18276 | ||
|
|
570cfc9a43 | ||
|
|
4dbf2e7189 | ||
|
|
85dd1f64a6 | ||
|
|
ac52bbbffb | ||
|
|
a75e42f210 | ||
|
|
429686528e | ||
|
|
6aa0a7c97b | ||
|
|
53408d0437 | ||
|
|
1c3bc6b531 | ||
|
|
9cb575b920 | ||
|
|
34427ab995 | ||
|
|
6428a9bf78 | ||
|
|
033a21b5ff | ||
|
|
c60686199f | ||
|
|
bb4b6c1855 | ||
|
|
245cf0ceed | ||
|
|
c73263f85f | ||
|
|
bb96fbb1c2 | ||
|
|
f6e3f3023b | ||
|
|
b2ab19b894 | ||
|
|
f137c01491 | ||
|
|
7cbf627329 | ||
|
|
30ce4a47c1 | ||
|
|
ad730f410f | ||
|
|
1d12b2c01a | ||
|
|
5b32f65a95 | ||
|
|
f324fa5512 | ||
|
|
b5e89d9f98 | ||
|
|
f235ca2228 | ||
|
|
1418f3fb9a | ||
|
|
2bc25789c9 | ||
|
|
eddc387d37 | ||
|
|
7d5c405e12 | ||
|
|
32772f3846 | ||
|
|
7a31d130ff | ||
|
|
a7a3a012b4 | ||
|
|
ac46306cc5 | ||
|
|
fc8b905a7b | ||
|
|
968666caab | ||
|
|
2b3e46d3b1 | ||
|
|
6859de09d2 | ||
|
|
5ac599ce17 | ||
|
|
33061164f3 | ||
|
|
62e0f7e135 | ||
|
|
58a9c167b3 | ||
|
|
7bd92c13aa | ||
|
|
3efa019656 | ||
|
|
4738fc2014 | ||
|
|
afe7a51c8f | ||
|
|
7713d784c9 | ||
|
|
0d96ea1730 | ||
|
|
a21bcec358 | ||
|
|
9395752540 | ||
|
|
0b83ceb20d | ||
|
|
77806ea88b | ||
|
|
60cbef5885 | ||
|
|
8ec1871914 | ||
|
|
aa6eabf766 | ||
|
|
203ce0b34f | ||
|
|
4f4163032f | ||
|
|
166bba4532 | ||
|
|
e6c65defac | ||
|
|
edb94b3f8a | ||
|
|
8b85851d22 | ||
|
|
1d422d6283 | ||
|
|
6a2763cfdf | ||
|
|
5db469aa7e | ||
|
|
21dae5cd35 | ||
|
|
5bd84491b8 | ||
|
|
40a30fce4a | ||
|
|
27a90869c7 | ||
|
|
4e4315c2ba | ||
|
|
8e5fa9ef7b | ||
|
|
1670321cdc | ||
|
|
a3e85aefcb | ||
|
|
6007cb92d0 | ||
|
|
36cca044c4 | ||
|
|
8e17c9933c | ||
|
|
1961cf90b5 | ||
|
|
0a87f42f10 | ||
|
|
75c07af406 | ||
|
|
a2f2650786 | ||
|
|
4633c23471 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ config.h
|
||||
config.h.in
|
||||
*.pc
|
||||
*~
|
||||
*.png
|
||||
|
||||
*.*~
|
||||
*.sw?
|
||||
|
||||
@@ -19,7 +19,6 @@ SUBDIRS = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-upf.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
@@ -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
|
||||
|
||||
16
configure.ac
16
configure.ac
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
35
contrib/gtpflood/overview_only_tunend.dot
Normal file
35
contrib/gtpflood/overview_only_tunend.dot
Normal 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];
|
||||
|
||||
}
|
||||
37
contrib/gtpflood/overview_only_tunmap.dot
Normal file
37
contrib/gtpflood/overview_only_tunmap.dot
Normal 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];
|
||||
|
||||
}
|
||||
43
contrib/gtpflood/overview_tunmap_and_tunend.dot
Normal file
43
contrib/gtpflood/overview_tunmap_and_tunend.dot
Normal 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];
|
||||
|
||||
}
|
||||
55
contrib/gtpflood/tunend/0.gtp_flood.vty
Normal file
55
contrib/gtpflood/tunend/0.gtp_flood.vty
Normal 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
|
||||
5
contrib/gtpflood/tunend/0.run_gtp_flood.sh
Executable file
5
contrib/gtpflood/tunend/0.run_gtp_flood.sh
Executable 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
|
||||
3
contrib/gtpflood/tunend/1.enable_ip_forwarding.sh
Executable file
3
contrib/gtpflood/tunend/1.enable_ip_forwarding.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
|
||||
13
contrib/gtpflood/tunend/1.eupf.yaml
Normal file
13
contrib/gtpflood/tunend/1.eupf.yaml
Normal 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
|
||||
26
contrib/gtpflood/tunend/1.osmo-upf.cfg
Normal file
26
contrib/gtpflood/tunend/1.osmo-upf.cfg
Normal 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
|
||||
3
contrib/gtpflood/tunend/1.run_eupf.sh
Executable file
3
contrib/gtpflood/tunend/1.run_eupf.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
sudo eupf --config 1.eupf.yaml
|
||||
3
contrib/gtpflood/tunend/1.run_osmo-upf.sh
Executable file
3
contrib/gtpflood/tunend/1.run_osmo-upf.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
osmo-upf -c 1.osmo-upf.cfg
|
||||
3
contrib/gtpflood/tunend/1.ue_ip_route.sh
Executable file
3
contrib/gtpflood/tunend/1.ue_ip_route.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
sudo ip addr add 192.168.1.1/16 dev apn-flood
|
||||
3
contrib/gtpflood/tunend/2.udp-responder.sh
Executable file
3
contrib/gtpflood/tunend/2.udp-responder.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
osmo-udp-responder -l 172.16.32.2 -p 23000
|
||||
2
contrib/gtpflood/tunend/2.ue_ip_route.sh
Executable file
2
contrib/gtpflood/tunend/2.ue_ip_route.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
sudo ip route add 192.168.0.0/16 via 172.16.32.1
|
||||
12
contrib/gtpflood/tunend/osmo-pfcp-tool.cfg
Normal file
12
contrib/gtpflood/tunend/osmo-pfcp-tool.cfg
Normal 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
|
||||
54
contrib/gtpflood/tunmap/0.gtp_flood.vty
Normal file
54
contrib/gtpflood/tunmap/0.gtp_flood.vty
Normal 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
|
||||
3
contrib/gtpflood/tunmap/0.run_gtp_flood.sh
Executable file
3
contrib/gtpflood/tunmap/0.run_gtp_flood.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
osmo-pfcp-tool 0.gtp_flood.vty -c osmo-pfcp-tool.cfg
|
||||
13
contrib/gtpflood/tunmap/1.eupf.yaml
Normal file
13
contrib/gtpflood/tunmap/1.eupf.yaml
Normal 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
|
||||
26
contrib/gtpflood/tunmap/1.osmo-upf.cfg
Normal file
26
contrib/gtpflood/tunmap/1.osmo-upf.cfg
Normal 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
|
||||
3
contrib/gtpflood/tunmap/1.run_eupf.sh
Executable file
3
contrib/gtpflood/tunmap/1.run_eupf.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
sudo eupf --config 1.eupf.yaml
|
||||
3
contrib/gtpflood/tunmap/1.run_osmo-upf.sh
Executable file
3
contrib/gtpflood/tunmap/1.run_osmo-upf.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
osmo-upf -c 1.osmo-upf.cfg
|
||||
3
contrib/gtpflood/tunmap/2.udp-responder.sh
Executable file
3
contrib/gtpflood/tunmap/2.udp-responder.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
osmo-udp-responder -l 172.16.32.2 -p 2152
|
||||
12
contrib/gtpflood/tunmap/osmo-pfcp-tool.cfg
Normal file
12
contrib/gtpflood/tunmap/osmo-pfcp-tool.cfg
Normal 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
|
||||
@@ -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
|
||||
|
||||
51
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty
Normal file
51
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty
Normal 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).
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
144
contrib/udp_flood_test.c
Normal 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
641
contrib/udp_responder.c
Normal 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
137
contrib/udp_rx_test.c
Normal 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
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
9
|
||||
10
|
||||
|
||||
6
debian/control
vendored
6
debian/control
vendored
@@ -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
38
debian/postinst
vendored
Executable 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#
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
SUBDIRS = \
|
||||
upf \
|
||||
pfcptool \
|
||||
$(NULL)
|
||||
|
||||
6
include/osmocom/pfcptool/Makefile.am
Normal file
6
include/osmocom/pfcptool/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
noinst_HEADERS = \
|
||||
checksum.h \
|
||||
gtp_flood.h \
|
||||
pfcp_tool.h \
|
||||
range.h \
|
||||
$(NULL)
|
||||
13
include/osmocom/pfcptool/checksum.h
Normal file
13
include/osmocom/pfcptool/checksum.h
Normal 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);
|
||||
58
include/osmocom/pfcptool/gtp_flood.h
Normal file
58
include/osmocom/pfcptool/gtp_flood.h
Normal 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);
|
||||
@@ -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);
|
||||
38
include/osmocom/pfcptool/range.h
Normal file
38
include/osmocom/pfcptool/range.h
Normal 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);
|
||||
@@ -7,5 +7,6 @@ noinst_HEADERS = \
|
||||
upf_gtp.h \
|
||||
upf_gtpu_echo.h \
|
||||
upf_nft.h \
|
||||
upf_tun.h \
|
||||
up_gtp_action.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
38
include/osmocom/upf/upf_tun.h
Normal file
38
include/osmocom/upf/upf_tun.h
Normal 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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
211
src/osmo-pfcp-tool/checksum.c
Normal file
211
src/osmo-pfcp-tool/checksum.c
Normal 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;
|
||||
}
|
||||
689
src/osmo-pfcp-tool/gtp_flood.c
Normal file
689
src/osmo-pfcp-tool/gtp_flood.c
Normal 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(>p_flood->tx_workers);
|
||||
INIT_LLIST_HEAD(>p_flood->rx_workers);
|
||||
c = >p_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, >p_flood->tx_workers);
|
||||
}
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "tx workers: %u\n", llist_count(>p_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, >p_flood->rx_workers);
|
||||
}
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "rx workers: %u\n", llist_count(>p_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(>p_flood->tx_workers, >p_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(>p_flood->rx_workers, >p_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, >p_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, >p_flood->tx_workers, entry)
|
||||
gtp_flood_worker_start(w, gtp_flood_tx_worker_thread);
|
||||
llist_for_each_entry (w, >p_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 */
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(>p_flood_cmd);
|
||||
install_node(>p_flood_node, NULL);
|
||||
install_element(GTP_FLOOD_NODE, >pf_workers_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_io_uring_queue_size_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_flows_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_packets_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_payload_src_port_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_payload_target_ip_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_payload_target_port_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_payload_append_payload_info_cmd);
|
||||
install_element(GTP_FLOOD_NODE, >pf_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
111
src/osmo-pfcp-tool/range.c
Normal 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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
41
tests/unique_ids/Makefile.am
Normal file
41
tests/unique_ids/Makefile.am
Normal 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
|
||||
575
tests/unique_ids/unique_ids_test.c
Normal file
575
tests/unique_ids/unique_ids_test.c
Normal 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;
|
||||
}
|
||||
412
tests/unique_ids/unique_ids_test.err
Normal file
412
tests/unique_ids/unique_ids_test.err
Normal 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
|
||||
0
tests/unique_ids/unique_ids_test.ok
Normal file
0
tests/unique_ids/unique_ids_test.ok
Normal file
Reference in New Issue
Block a user