mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
				synced 2025-11-03 21:43:34 +00:00 
			
		
		
		
	Compare commits
	
		
			127 Commits
		
	
	
		
			0.1.1
			...
			pespin/mas
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					528ca8cd78 | ||
| 
						 | 
					4ec29cf904 | ||
| 
						 | 
					a2289c0730 | ||
| 
						 | 
					d2241da833 | ||
| 
						 | 
					68c0e96a81 | ||
| 
						 | 
					b54baeff98 | ||
| 
						 | 
					6fe425cd47 | ||
| 
						 | 
					b6d689d677 | ||
| 
						 | 
					bede7f130f | ||
| 
						 | 
					c27e317b21 | ||
| 
						 | 
					d4a4f0ce63 | ||
| 
						 | 
					63c98c38f1 | ||
| 
						 | 
					a306a1aa01 | ||
| 
						 | 
					81ca6a0d16 | ||
| 
						 | 
					12d7cf87a9 | ||
| 
						 | 
					9c904c185a | ||
| 
						 | 
					b3d4ca6d80 | ||
| 
						 | 
					e16bb2826b | ||
| 
						 | 
					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 | ||
| 
						 | 
					d8742f79ca | ||
| 
						 | 
					6c01708438 | ||
| 
						 | 
					4e1c680e59 | ||
| 
						 | 
					fbe70076eb | ||
| 
						 | 
					091603c4a4 | ||
| 
						 | 
					fae0ed6d24 | ||
| 
						 | 
					6cb4231383 | ||
| 
						 | 
					4e2b367d89 | ||
| 
						 | 
					374fd1eab4 | ||
| 
						 | 
					3c0fc60c3c | ||
| 
						 | 
					e7f812cf18 | ||
| 
						 | 
					c3bf187588 | ||
| 
						 | 
					24030881be | ||
| 
						 | 
					eaf2d153a8 | ||
| 
						 | 
					341e2ff692 | ||
| 
						 | 
					d2f02df613 | ||
| 
						 | 
					cd345bd6cd | ||
| 
						 | 
					52f9da22ff | ||
| 
						 | 
					b9d4ac8379 | ||
| 
						 | 
					4832e932e6 | ||
| 
						 | 
					2a2884fbbe | ||
| 
						 | 
					c4eb92d211 | ||
| 
						 | 
					95ab35035a | ||
| 
						 | 
					8525c49c5d | ||
| 
						 | 
					0e66d699ed | ||
| 
						 | 
					eb8361f4c5 | ||
| 
						 | 
					9c6a8e32a0 | ||
| 
						 | 
					08af1f15f8 | ||
| 
						 | 
					bd737c14fa | ||
| 
						 | 
					2a9d91792e | ||
| 
						 | 
					95e56eaecb | ||
| 
						 | 
					341e130841 | ||
| 
						 | 
					feeaf35e44 | ||
| 
						 | 
					629647a535 | ||
| 
						 | 
					1a341ee418 | ||
| 
						 | 
					d059391125 | ||
| 
						 | 
					3572241df5 | ||
| 
						 | 
					e68eca0e8f | ||
| 
						 | 
					8e842b890c | ||
| 
						 | 
					d7f683a66c | ||
| 
						 | 
					65788ed64e | ||
| 
						 | 
					88b3b63987 | ||
| 
						 | 
					391259bd8c | ||
| 
						 | 
					6d17c43c42 | ||
| 
						 | 
					54ebc4772b | ||
| 
						 | 
					0575e9bad9 | ||
| 
						 | 
					28180a6246 | ||
| 
						 | 
					0fca3412d8 | ||
| 
						 | 
					b183aa84af | ||
| 
						 | 
					527f1b3b94 | ||
| 
						 | 
					cd3f25cc20 | ||
| 
						 | 
					701bb8addc | ||
| 
						 | 
					95eb2c6a89 | ||
| 
						 | 
					80aefa42c6 | ||
| 
						 | 
					a3b5488b69 | ||
| 
						 | 
					6730f104d8 | ||
| 
						 | 
					2d2fcd81bc | ||
| 
						 | 
					ffc461ab38 | ||
| 
						 | 
					361ecd8cd0 | ||
| 
						 | 
					7c3eeb0760 | ||
| 
						 | 
					c88dc7866f | ||
| 
						 | 
					f95bd5b895 | ||
| 
						 | 
					114277cff7 | ||
| 
						 | 
					d186d59aa2 | ||
| 
						 | 
					803c1968e6 | ||
| 
						 | 
					436c165f1c | 
@@ -19,7 +19,6 @@ SUBDIRS = \
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     contrib/osmo-upf.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,9 @@ GIT Repository
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-upf.git repository using
 | 
			
		||||
 | 
			
		||||
	git clone git://git.osmocom.org/osmo-upf.git
 | 
			
		||||
	git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
 | 
			
		||||
 | 
			
		||||
There is a cgit interface at https://git.osmocom.org/osmo-upf/
 | 
			
		||||
There is a web interface at https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.
 | 
			
		||||
 | 
			
		||||
To submit patches, see "Contributing" below.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
			
		||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# In short:
 | 
			
		||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
 | 
			
		||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# LIBVERSION=c:r:a
 | 
			
		||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
 | 
			
		||||
# 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
 | 
			
		||||
#library	what			description / commit summary line
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								configure.ac
									
									
									
									
									
								
							@@ -36,11 +36,11 @@ fi
 | 
			
		||||
PKG_PROG_PKG_CONFIG([0.20])
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.1.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBGTPNL, libgtpnl >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBGTPNL, libgtpnl >= 1.3.2)
 | 
			
		||||
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
 | 
			
		||||
 | 
			
		||||
dnl checks for header files
 | 
			
		||||
@@ -123,7 +123,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
 | 
			
		||||
	fi
 | 
			
		||||
	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
			
		||||
	 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
 | 
			
		||||
		AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
 | 
			
		||||
		AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
			
		||||
@@ -203,10 +203,10 @@ AC_OUTPUT(
 | 
			
		||||
    src/osmo-pfcp-tool/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/unique_ids/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    doc/charts/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
timer pfcp x23 0
 | 
			
		||||
pfcp-peer 127.0.0.1
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session endecaps
 | 
			
		||||
  tx session-est-req forw
 | 
			
		||||
  sleep 5
 | 
			
		||||
  tx session-del-req
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -21,7 +21,5 @@ ctrl
 | 
			
		||||
timer pfcp x24 5000
 | 
			
		||||
pfcp
 | 
			
		||||
 local-addr 127.0.0.11
 | 
			
		||||
gtp
 | 
			
		||||
tunend
 | 
			
		||||
 dev create apn11 127.0.0.11
 | 
			
		||||
nft
 | 
			
		||||
 table-name osmo-upf-11
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -21,7 +21,9 @@ ctrl
 | 
			
		||||
timer pfcp x24 5000
 | 
			
		||||
pfcp
 | 
			
		||||
 local-addr 127.0.0.12
 | 
			
		||||
gtp
 | 
			
		||||
 dev create apn12 127.0.0.12
 | 
			
		||||
nft
 | 
			
		||||
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,4 +1,6 @@
 | 
			
		||||
timer pfcp x23 0
 | 
			
		||||
pfcp-peer 127.0.0.1
 | 
			
		||||
 session endecaps
 | 
			
		||||
 session tunend
 | 
			
		||||
  ue ip 127.127.127.127
 | 
			
		||||
  gtp access remote f-teid 127.0.0.127 127
 | 
			
		||||
  tx session-est-req
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,14 @@ pfcp-peer 127.0.0.1
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session
 | 
			
		||||
  ue ip 127.127.127.127
 | 
			
		||||
  gtp access remote f-teid 127.0.0.127 127
 | 
			
		||||
  tx session-est-req drop
 | 
			
		||||
  sleep 3
 | 
			
		||||
  tx session-mod-req forw
 | 
			
		||||
  tx session-mod-req far forw
 | 
			
		||||
  sleep 5
 | 
			
		||||
  tx session-mod-req drop
 | 
			
		||||
  tx session-mod-req far drop
 | 
			
		||||
  sleep 3
 | 
			
		||||
  tx session-mod-req forw
 | 
			
		||||
  tx session-mod-req far forw
 | 
			
		||||
  sleep 3
 | 
			
		||||
  tx session-del-req
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# ACCESS                HOP                           CORE
 | 
			
		||||
# ACCESS                UPF tunmap                    UPF tunend (CORE)
 | 
			
		||||
#                       session 23 = tunmap           session 42 = encaps/decaps
 | 
			
		||||
# GTP 127.0.0.13        127.0.0.12                    127.0.0.11
 | 
			
		||||
# TEID l:23 r:123 <---> r:23 l:123 | l:142 r:42 <---> r:142 l:42 | 192.168.100.42
 | 
			
		||||
@@ -11,10 +11,10 @@ timer pfcp x23 0
 | 
			
		||||
pfcp-peer 127.0.0.11
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session endecaps 42
 | 
			
		||||
 session tunend 42
 | 
			
		||||
  ue ip 192.168.100.42
 | 
			
		||||
  gtp access ip 127.0.0.12
 | 
			
		||||
  gtp access teid local 42 remote 142
 | 
			
		||||
  gtp access local f-teid 127.0.0.11 42
 | 
			
		||||
  gtp access remote f-teid 127.0.0.12 142
 | 
			
		||||
  tx session-est-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 | 
			
		||||
@@ -22,9 +22,9 @@ pfcp-peer 127.0.0.12
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session tunmap 23
 | 
			
		||||
  gtp core ip 127.0.0.11
 | 
			
		||||
  gtp core teid local 142 remote 42
 | 
			
		||||
  gtp access ip 127.0.0.13
 | 
			
		||||
  gtp access teid local 123 remote 23
 | 
			
		||||
  gtp core remote f-teid 127.0.0.11 42
 | 
			
		||||
  gtp core local f-teid 127.0.0.12 142
 | 
			
		||||
  gtp access local f-teid 127.0.0.12 123
 | 
			
		||||
  gtp access remote f-teid 127.0.0.13 23
 | 
			
		||||
  tx session-est-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
							
								
								
									
										11
									
								
								contrib/osmo-pfcp-tool-scripts/tunend_session_est.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/osmo-pfcp-tool-scripts/tunend_session_est.vty
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
timer pfcp x23 0
 | 
			
		||||
pfcp-peer 127.0.0.11
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session tunend
 | 
			
		||||
  ue ip 127.127.127.127
 | 
			
		||||
  gtp access local f-teid choose
 | 
			
		||||
  gtp access remote f-teid 127.0.0.12 142
 | 
			
		||||
  tx session-est-req forw
 | 
			
		||||
  sleep 5
 | 
			
		||||
  tx session-del-req
 | 
			
		||||
@@ -3,6 +3,10 @@ pfcp-peer 127.0.0.1
 | 
			
		||||
 tx assoc-setup-req
 | 
			
		||||
 sleep 1
 | 
			
		||||
 session tunmap
 | 
			
		||||
  gtp core remote f-teid 127.0.0.11 42
 | 
			
		||||
  gtp core local f-teid choose
 | 
			
		||||
  gtp access local f-teid choose
 | 
			
		||||
  gtp access remote f-teid 127.0.0.13 23
 | 
			
		||||
  tx session-est-req
 | 
			
		||||
  sleep 5
 | 
			
		||||
  tx session-del-req
 | 
			
		||||
 
 | 
			
		||||
@@ -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,11 +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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										145
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										145
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,148 @@
 | 
			
		||||
osmo-upf (0.3.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * upf_gtp: automatically delete old gtp devices
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Improve logging reading packets from gtp device
 | 
			
		||||
  * Remove unused file up_session_to_gtp.c
 | 
			
		||||
  * Simplify up_session_choose_f_teid() with early returns
 | 
			
		||||
  * Introduce hashtable to lookup session by F-TEID
 | 
			
		||||
  * Introduce hashtable to lookup chain_id
 | 
			
		||||
  * Introduce hashtable to look up gtp_tundev by local TEID
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Tue, 18 Feb 2025 12:20:35 +0100
 | 
			
		||||
 | 
			
		||||
osmo-upf (0.2.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * improve manual: PFCP and GTP interfaces
 | 
			
		||||
  * example cfg: tweak logging
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * osmo_pfcp_tool: fix osmo_pfcp_tool_copyright error
 | 
			
		||||
  * debian: add osmo-pfcp-tool to osmo-upf package
 | 
			
		||||
  * Run struct_endianness.py
 | 
			
		||||
  * contrib/jenkins: build nftables without python
 | 
			
		||||
  * contrib/jenkins: clone netfilter repos with https
 | 
			
		||||
  * contrib/jenkins: netfilter: use PARALLEL_MAKE
 | 
			
		||||
  * debian: set compat level to 10
 | 
			
		||||
  * systemd: depend on networking-online.target
 | 
			
		||||
  * .deb/.rpm: various fixes related to non-root
 | 
			
		||||
  * contrib/systemd: AmbientCapabilities=CAP_NET_ADMIN
 | 
			
		||||
  * contrib: remove rpm spec file
 | 
			
		||||
  * debian/postinst: add checks, be verbose
 | 
			
		||||
 | 
			
		||||
  [ Neels Janosch Hofmeyr ]
 | 
			
		||||
  * Allow running without a GTP dev
 | 
			
		||||
  * drop charts/, duplicated from libosmo-pfcp.git
 | 
			
		||||
  * manual: add/fix running.adoc
 | 
			
		||||
  * manual: add 'Configure Primary Links'
 | 
			
		||||
  * vty doc: indicate default nft table name
 | 
			
		||||
  * silence misleading error: "HEARTBEAT_REQ: Unknown message type"
 | 
			
		||||
  * drop unused enum up_session_kind
 | 
			
		||||
  * tests/upf.vty: add some missing nodes to the test
 | 
			
		||||
  * VTY: rename 'gtp' to 'tunend'
 | 
			
		||||
  * VTY: rename 'nft' to 'tunmap'
 | 
			
		||||
  * osmo-pfcp-tool VTY: rename 'endecaps' to 'tunend'
 | 
			
		||||
  * cosmetic: in code, rename 'endecaps' to 'tunend'
 | 
			
		||||
  * cosmetic: rename upf_gtp_tun to upf_gtp_tunend
 | 
			
		||||
  * drop unused upf_gtp_dev_is_tunnel_active()
 | 
			
		||||
  * cosmetic: rename upf_gtp_dev_tunnel_* to upf_gtp_dev_tunend_*
 | 
			
		||||
  * vty: revert rename of 'show gtp'
 | 
			
		||||
  * VTY: show gtp: still list tunmap if no tunend device is open
 | 
			
		||||
  * up_gtp_action_to_str_buf(): always print PDR IDs
 | 
			
		||||
  * use osmo_pfcp_ie_outer_header_creation_to_str_buf()
 | 
			
		||||
  * gtpu_echo: do not osmo_fd_register twice
 | 
			
		||||
  * VTY 'show gtp': more accurately identify local/remote IP
 | 
			
		||||
  * fix access/core mixup of PDR IDs / tunmap FAR
 | 
			
		||||
  * clarify comments and naming around PDR+FAR classification
 | 
			
		||||
  * tunmap: choose local GTP addr by Network Instance IEs
 | 
			
		||||
  * log: add missing sep in far_to_str
 | 
			
		||||
  * fix PFCP Session Mod: Update FAR
 | 
			
		||||
  * in GTP actions, also store local GTP addrs
 | 
			
		||||
  * nft: rename addr to addr_remote, add addr_local
 | 
			
		||||
  * nft: incoming GTP-U: match on local IP, not remote IP
 | 
			
		||||
  * nft: rewrite source IP in outgoing GTP-U
 | 
			
		||||
  * nft: log nft rulesets on debug log
 | 
			
		||||
  * nft: end each rule in semicolon
 | 
			
		||||
  * nft: ensure to assign rule id only once
 | 
			
		||||
  * GTP,UE addrs in osmo_sockaddr: assert( port == 0 )
 | 
			
		||||
  * nft: allow to get the ruleset string without running
 | 
			
		||||
  * vty: add: show nft-rule tunmap example
 | 
			
		||||
  * add cfg: tunmap / nft-rule append
 | 
			
		||||
  * nft: append 'accept' to each rule
 | 
			
		||||
  * tunend: choose local GTP addr by Network Instance IEs
 | 
			
		||||
  * manual: use 'tunend' and 'tunmap'
 | 
			
		||||
  * manual: explain new netinst cfg
 | 
			
		||||
  * manual: tweak 'running' for new netinst feature
 | 
			
		||||
  * manual: add charts explaining tunend and tunmap
 | 
			
		||||
  * manual: some tweaks in overview
 | 
			
		||||
  * manual: fix broken reference to netinst section
 | 
			
		||||
  * fix copy-paste bug in up_endpoint.c
 | 
			
		||||
  * fix deprecation: use telnet_init_default()
 | 
			
		||||
  * check rc of osmo_use_count_get_put()
 | 
			
		||||
  * error log: fix msg for gtp_del_tunnel() failure
 | 
			
		||||
  * fix various crashes on osmo_pfcp_endpoint_tx() err handling
 | 
			
		||||
  * osmo-pfcp-tool: avoid stale pointers on msg copy
 | 
			
		||||
  * fix some PFCP peer,session error handling paths
 | 
			
		||||
  * drop unused function up_peer_tx
 | 
			
		||||
  * move GTP port definitions to upf.h
 | 
			
		||||
  * deprecate cfg 'nft rule tunmap append'
 | 
			
		||||
  * tunmap: prep new nft ruleset: log only mapping id
 | 
			
		||||
  * tunmap: refactor nft ruleset: fix "martians" and "1024"
 | 
			
		||||
  * tunmap: ensure nft table is removed on program exit
 | 
			
		||||
  * osmo_pfcp_tool: make usable again
 | 
			
		||||
  * minor api doc
 | 
			
		||||
  * cosmetic: simplify naming: struct upf_tunmap, struct upf_tunend
 | 
			
		||||
  * cosmetic: reduce dup in tunnel struct definitions
 | 
			
		||||
  * cosmetic: rename g_upf->gtp to tunend, ->nft to tunmap
 | 
			
		||||
  * move next_teid from up_endpoint to g_upf
 | 
			
		||||
  * cosmetic: rename next_seid to next_up_seid
 | 
			
		||||
  * build: drop LIBOSMO_GTLV
 | 
			
		||||
  * build: add libupf.la (noinst)
 | 
			
		||||
  * add unique_ids_test.c
 | 
			
		||||
  * tunmap: ensure assigned chain_id is unused
 | 
			
		||||
  * cosmetic: clarify session active / partially active semantics
 | 
			
		||||
  * unique_ids_test.c: fix coverity ASSERT_SIDE_EFFECT
 | 
			
		||||
  * manual: fix typo in running.adoc
 | 
			
		||||
  * manual: 'Running': flatten section depths a bit
 | 
			
		||||
  * manual: 'Running': tweak, mention 'tunmap' and 'tunend'
 | 
			
		||||
  * manual: 'Running': tweak word, fix ws at line end
 | 
			
		||||
  * manual: explain GTP Echo workaround for tunmap
 | 
			
		||||
  * manual: explain IP forwarding
 | 
			
		||||
  * tunmap: always set GTP-U source port to 2152 when forwarding
 | 
			
		||||
  * vty doc fix
 | 
			
		||||
  * pfcp-tool: fix extra newline in vty_out
 | 
			
		||||
  * fix msgb memleak on GTP echo response
 | 
			
		||||
  * fix EXTRA_DIST for vty test scripts
 | 
			
		||||
  * drop unreachable statement
 | 
			
		||||
  * contrib/pfcp-tool-scripts: adjust tunend_session_est.vty and upf cfg to match up
 | 
			
		||||
  * upf gtp-u echo: improve loging
 | 
			
		||||
  * upf gtp-u echo: rx Echo Response messages
 | 
			
		||||
  * pfcp-tool: always use specific PDR ids for access and core
 | 
			
		||||
  * nft: batch nftables commands
 | 
			
		||||
  * osmo-upf: add VTY 'gtp-echo' command
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Set working directory in systemd service file
 | 
			
		||||
  * ctrl: take both address and port from vty config
 | 
			
		||||
  * .deb/.rpm: add osmocom user during package install
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * update git URLs (git -> https; gitea)
 | 
			
		||||
  * contrib/jenkins.sh: clone libnftnl and libnftables via git://
 | 
			
		||||
  * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
 | 
			
		||||
 | 
			
		||||
  [ arehbein ]
 | 
			
		||||
  * osmo-pfcp-tool: Fix call to strerror
 | 
			
		||||
  * up_session: Silence coverity warning
 | 
			
		||||
 | 
			
		||||
  [ Andreas Eversberg ]
 | 
			
		||||
  * Use uniform log format for default config files
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Wed, 11 Dec 2024 16:37:06 +0100
 | 
			
		||||
 | 
			
		||||
osmo-upf (0.1.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
			
		||||
9
 | 
			
		||||
10
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,7 +2,7 @@ Source: osmo-upf
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: extra
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>=9),
 | 
			
		||||
Build-Depends: debhelper (>= 10),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               autoconf,
 | 
			
		||||
@@ -12,14 +12,14 @@ Build-Depends: debhelper (>=9),
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               python3-minimal,
 | 
			
		||||
               libtalloc-dev,
 | 
			
		||||
               libgtpnl-dev (>= 1.2.0),
 | 
			
		||||
               libgtpnl-dev (>= 1.3.2),
 | 
			
		||||
               libnftables-dev (>= 1.0.2),
 | 
			
		||||
               libosmocore-dev (>= 1.6.0),
 | 
			
		||||
               libosmo-pfcp-dev (>= 0.1.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.2.0)
 | 
			
		||||
               libosmocore-dev (>= 1.11.0),
 | 
			
		||||
               libosmo-pfcp-dev (>= 0.5.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.6.0)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-upf.git
 | 
			
		||||
Vcs-Browser: https://git.osmocom.org/osmo-upf/
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
 | 
			
		||||
Homepage: https://projects.osmocom.org/projects/osmo-upf
 | 
			
		||||
 | 
			
		||||
Package: osmo-upf
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
			
		||||
Upstream-Name: osmo-upf
 | 
			
		||||
Source: git://git.osmocom.org/osmo-upf
 | 
			
		||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
 | 
			
		||||
 | 
			
		||||
Files:     *
 | 
			
		||||
Copyright: 2021-2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-upf.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-upf.install
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,5 @@
 | 
			
		||||
etc/osmocom/osmo-upf.cfg
 | 
			
		||||
lib/systemd/system/osmo-upf.service
 | 
			
		||||
usr/bin/osmo-pfcp-tool
 | 
			
		||||
usr/bin/osmo-upf
 | 
			
		||||
usr/share/doc/osmo-upf/examples/osmo-upf/osmo-upf.cfg usr/share/doc/osmo-upf/examples
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,5 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	examples \
 | 
			
		||||
	manuals \
 | 
			
		||||
	charts \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
msc: \
 | 
			
		||||
	$(builddir)/pfcp_msgs.png \
 | 
			
		||||
	$(builddir)/pfcp_msgs_gtp.png \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
dot: \
 | 
			
		||||
	$(builddir)/pfcp_overview.png \
 | 
			
		||||
	$(builddir)/pfcp_cp_peer_fsm.png \
 | 
			
		||||
	$(builddir)/pfcp_up_peer_fsm.png \
 | 
			
		||||
	$(builddir)/pfcp_heartbeat_fsm.png \
 | 
			
		||||
	$(builddir)/pfcp_cp_session_fsm.png \
 | 
			
		||||
	$(builddir)/pfcp_up_session_fsm.png \
 | 
			
		||||
	$(builddir)/pfcp_and_gtp.png \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
$(builddir)/%.png: $(srcdir)/%.msc
 | 
			
		||||
	mscgen -T png -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(builddir)/%.png: $(srcdir)/%.dot
 | 
			
		||||
	dot -Tpng $< > $@
 | 
			
		||||
 | 
			
		||||
.PHONY: poll
 | 
			
		||||
poll:
 | 
			
		||||
	while true; do $(MAKE) msc dot; sleep 1; done
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=LR
 | 
			
		||||
labelloc=t; label="PFCP and GTP"
 | 
			
		||||
 | 
			
		||||
SGSN [label="SGSN\n123.44.0.9"]
 | 
			
		||||
SGWC [label="SGW-C\n123.44.05"]
 | 
			
		||||
subgraph cluster_UPF {
 | 
			
		||||
 label="OsmoUPF";
 | 
			
		||||
 SGWU [label="SGW-U\n123.44.0.6"];
 | 
			
		||||
 GTPk [label="kernel GTP\n123.44.0.6"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SGSN -> SGWC [label="S4\nGTPv2-C"]
 | 
			
		||||
SGWC -> SGWU [label="Sxa\nPFCP\nSession Establishment:\n"]
 | 
			
		||||
SGSN -> GTPk [label="S4\nGTPv1-U",dir=both]
 | 
			
		||||
 | 
			
		||||
MS [label="MS\n192.168.104.176"]
 | 
			
		||||
MS -> SGSN [dir=both]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP CP peer FSM\nControl Plane side, managing association with remote UP peer"
 | 
			
		||||
 | 
			
		||||
cp [label="CP function",shape="box"]
 | 
			
		||||
 | 
			
		||||
cp -> WAIT_ASSOC_SETUP_RESP [label="cp_peer_associate()"]
 | 
			
		||||
 | 
			
		||||
txrx [label="PFCP socket",shape="box"]
 | 
			
		||||
WAIT_ASSOC_SETUP_RESP -> txrx [label="tx_assoc_setup_req()",style=dotted]
 | 
			
		||||
txrx -> WAIT_ASSOC_SETUP_RESP [label="EV_RX_ASSOC_SETUP_RESP",style=dotted]
 | 
			
		||||
WAIT_ASSOC_SETUP_RESP -> ASSOCIATED [label="Assoc Setup Resp"]
 | 
			
		||||
 | 
			
		||||
WAIT_ASSOC_SETUP_RESP -> WAIT_ASSOC_SETUP_RESP [label="retry"]
 | 
			
		||||
 | 
			
		||||
heartbeat [label="PFCP heartbeat FSM",shape=box3d]
 | 
			
		||||
ASSOCIATED -> heartbeat [label="alloc()",style=dotted]
 | 
			
		||||
heartbeat -> ASSOCIATED [label="EV_HEARTBEAT_FAILURE",style=dotted]
 | 
			
		||||
 | 
			
		||||
txrx2 [label="PFCP socket",shape="box"]
 | 
			
		||||
txrx2 -> ASSOCIATED [label="EV_RX_ASSOC_UPDATE_REQ\n3GPP TS 29.244 6.2.7.3.1",style=dotted]
 | 
			
		||||
GRACEFUL_RELEASE -> txrx2 [label="tx_assoc_update_resp()",style=dotted]
 | 
			
		||||
 | 
			
		||||
cp_session [label="PFCP CP session FSM",shape=box3d]
 | 
			
		||||
cp -> ASSOCIATED [label="cp_peer_session_create()",style=dotted]
 | 
			
		||||
ASSOCIATED -> cp_session [label="cp_session_create()",style=dotted]
 | 
			
		||||
cp -> cp_session [style=invisible,arrowhead=none]
 | 
			
		||||
 | 
			
		||||
ASSOCIATED -> GRACEFUL_RELEASE [label="Association Update\nindicating graceful release"]
 | 
			
		||||
 | 
			
		||||
cp -> ASSOCIATED [label="cp_peer_release()",style=dotted]
 | 
			
		||||
ASSOCIATED -> term [label="cp_peer_release()\nHeartbeat failure"]
 | 
			
		||||
 | 
			
		||||
ASSOCIATED -> WAIT_ASSOC_SETUP_RESP [label="Heartbeat failure"]
 | 
			
		||||
 | 
			
		||||
GRACEFUL_RELEASE -> term
 | 
			
		||||
term [shape="octagon"]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP CP session FSM"
 | 
			
		||||
 | 
			
		||||
cp [label="CP function",shape=box]
 | 
			
		||||
cp -> WAIT_ESTABLISHMENT_RESP [label="cp_session_create(cp_peer)\niff cp_peer in state ASSOCIATED"]
 | 
			
		||||
 | 
			
		||||
txrx [label="PFCP socket",shape=box]
 | 
			
		||||
 | 
			
		||||
WAIT_ESTABLISHMENT_RESP -> txrx [label="tx_session_est_req()",style=dotted]
 | 
			
		||||
txrx -> WAIT_ESTABLISHMENT_RESP [label="EV_RX_SESSION_EST_RESP",style=dotted]
 | 
			
		||||
 | 
			
		||||
WAIT_ESTABLISHMENT_RESP -> ESTABLISHED [label="Est Resp"]
 | 
			
		||||
 | 
			
		||||
cp -> ESTABLISHED [label="cp_session_modify()",style=dotted]
 | 
			
		||||
ESTABLISHED -> WAIT_MODIFICATION_RESP [label="cp_session_modify()"]
 | 
			
		||||
WAIT_MODIFICATION_RESP -> txrx [label="tx_session_mod_req()",style=dotted]
 | 
			
		||||
txrx -> WAIT_MODIFICATION_RESP [label="EV_RX_SESSION_MOD_RESP",style=dotted,constraint=false]
 | 
			
		||||
WAIT_MODIFICATION_RESP -> ESTABLISHED [label="Mod Resp"]
 | 
			
		||||
 | 
			
		||||
cp -> ESTABLISHED [label="cp_session_delete()",style=dotted]
 | 
			
		||||
ESTABLISHED -> WAIT_DELETION_RESP [label="cp_session_delete()"]
 | 
			
		||||
WAIT_DELETION_RESP -> txrx [label="tx_session_del_req()",style=dotted]
 | 
			
		||||
txrx -> WAIT_DELETION_RESP [label="EV_RX_SESSION_DEL_RESP",style=dotted,constraint=false]
 | 
			
		||||
WAIT_DELETION_RESP -> term
 | 
			
		||||
term [shape="octagon"]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP heartbeat FSM"
 | 
			
		||||
 | 
			
		||||
peer [label="PFCP CP/UP peer FSM",shape=box3d]
 | 
			
		||||
txrx [label="PFCP socket",shape=box]
 | 
			
		||||
 | 
			
		||||
peer -> IDLE [label="alloc()"]
 | 
			
		||||
IDLE -> WAIT_HEARTBEAT_RESP -> IDLE
 | 
			
		||||
WAIT_HEARTBEAT_RESP -> term
 | 
			
		||||
term [shape="octagon"]
 | 
			
		||||
 | 
			
		||||
WAIT_HEARTBEAT_RESP -> txrx [label="tx_heartbeat_req()",style=dotted]
 | 
			
		||||
txrx -> WAIT_HEARTBEAT_RESP [label="HEARTBEAT_EV_RX_RESP",style=dotted]
 | 
			
		||||
 | 
			
		||||
term -> peer [label="PEER_EV_HEARTBEAT_FAILURE",style=dotted]
 | 
			
		||||
 | 
			
		||||
txrx2 [label="PFCP socket",shape=box]
 | 
			
		||||
txrx2 -> txrx2 [label="rx Heartbeat Req\ntx Heartbeat Resp",style=dotted]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP Overview\n3GPP TS 29.244 3.1, 5.8.1"
 | 
			
		||||
 | 
			
		||||
subgraph cluster_N1_CP {
 | 
			
		||||
	label="Node: Control Plane function";style=dotted
 | 
			
		||||
	N1_E_CP [label="CP Entity"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subgraph cluster_N2_UP {
 | 
			
		||||
	label="Node: User Plane function\nNode ID: my-userplane.com\n(FQDN may provide multiple PFCP Entities)";style=dotted
 | 
			
		||||
	N2_E_UP [label="UP Entity\n8.7.6.1"]
 | 
			
		||||
	N2_E_UP2 [label="UP Entity\n8.7.6.2"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subgraph cluster_N3_UP {
 | 
			
		||||
	label="Node: User Plane function\nNode ID: 1.2.3.4\n(IP address means only one PFCP Entity)";style=dotted
 | 
			
		||||
	N3_E_UP [label="UP Entity\n1.2.3.4\n(osmo-upf)"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
N1_E_CP -> N3_E_UP [label="PFCP Request"]
 | 
			
		||||
N1_E_CP -> N2_E_UP
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP UP peer FSM\nUser Plane side, managing association with remote CP peer"
 | 
			
		||||
 | 
			
		||||
txrx [label="PFCP socket",shape="box"]
 | 
			
		||||
 | 
			
		||||
txrx -> NOT_ASSOCIATED [label="rx PFCP msg from\nnew remote IP"]
 | 
			
		||||
txrx -> NOT_ASSOCIATED [label="EV_RX_ASSOC_SETUP_REQ",style=dotted]
 | 
			
		||||
 | 
			
		||||
NOT_ASSOCIATED -> ASSOCIATED [label="Assoc Setup Req",shape="box"]
 | 
			
		||||
 | 
			
		||||
heartbeat [label="PFCP heartbeat FSM",shape=box3d]
 | 
			
		||||
ASSOCIATED -> heartbeat [label="alloc()",style=dotted]
 | 
			
		||||
heartbeat -> ASSOCIATED [label="EV_HEARTBEAT_FAILURE",style=dotted]
 | 
			
		||||
 | 
			
		||||
txrx -> ASSOCIATED [label="EV_RX_SESSION_EST_REQ",style=dotted]
 | 
			
		||||
up_session [label="PFCP UP session FSM",shape=box3d]
 | 
			
		||||
ASSOCIATED -> up_session [label="up_session_create()",style=dotted]
 | 
			
		||||
 | 
			
		||||
txrx -> ASSOCIATED [label="EV_RX_ASSOC_UPD_REQ",style=dotted]
 | 
			
		||||
ASSOCIATED -> GRACEFUL_RELEASE [label="Association Update\nindicating graceful release"]
 | 
			
		||||
 | 
			
		||||
ASSOCIATED -> term [label="Heartbeat failure"]
 | 
			
		||||
GRACEFUL_RELEASE -> term
 | 
			
		||||
term [shape="octagon"]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=TB
 | 
			
		||||
labelloc=t; label="PFCP UP session FSM"
 | 
			
		||||
 | 
			
		||||
peer [label="PFCP UP peer FSM",shape=box3d]
 | 
			
		||||
peer -> ESTABLISHED [label="rx_session_est_req()"]
 | 
			
		||||
 | 
			
		||||
txrx [label="PFCP socket",shape="box"]
 | 
			
		||||
txrx2 [label="PFCP socket",shape="box"]
 | 
			
		||||
 | 
			
		||||
txrx -> ESTABLISHED [label="EV_RX_SESSION_MOD_REQ",style=dotted]
 | 
			
		||||
ESTABLISHED -> txrx [label="tx_session_mod_resp()",style=dotted,constraint=false]
 | 
			
		||||
ESTABLISHED -> ESTABLISHED [label="Mod"]
 | 
			
		||||
 | 
			
		||||
txrx2 -> ESTABLISHED [label="EV_RX_SESSION_DEL_REQ",style=dotted]
 | 
			
		||||
ESTABLISHED -> txrx2 [label="tx_session_del_resp()",style=dotted,constraint=false]
 | 
			
		||||
 | 
			
		||||
ESTABLISHED -> term [label="Deletion"]
 | 
			
		||||
term [shape="octagon"]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,15 @@
 | 
			
		||||
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 level set-all info
 | 
			
		||||
#logging level set-all debug
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
 | 
			
		||||
timer pfcp x24 5000
 | 
			
		||||
pfcp
 | 
			
		||||
 local-addr 127.0.0.1
 | 
			
		||||
gtp
 | 
			
		||||
tunend
 | 
			
		||||
 dev create apn23
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,17 @@
 | 
			
		||||
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 level set-all debug
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 | 
			
		||||
timer pfcp x24 5000
 | 
			
		||||
pfcp
 | 
			
		||||
 local-addr 127.0.0.1
 | 
			
		||||
gtp
 | 
			
		||||
tunend
 | 
			
		||||
 mockup
 | 
			
		||||
nft
 | 
			
		||||
tunmap
 | 
			
		||||
 mockup
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
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 level set-all debug
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 | 
			
		||||
timer pfcp x24 5000
 | 
			
		||||
pfcp
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								doc/manuals/chapters/netinst.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								doc/manuals/chapters/netinst.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
[[netinst]]
 | 
			
		||||
== Local GTP Addresses / Network Instance
 | 
			
		||||
 | 
			
		||||
PFCP features optional Network Instance IEs, in which the CPF may tell the UPF which local network interface to use for
 | 
			
		||||
a PDR and/or a FAR.
 | 
			
		||||
 | 
			
		||||
NOTE:: osmo-upf only evaluates the Network Instances configured in PDRs. Since osmo-upf always pairs a PDR+FAR with
 | 
			
		||||
another PDR+FAR in reverse direction, each side's PDR is sufficient.
 | 
			
		||||
 | 
			
		||||
Network Instance IEs affect both the tunend and the tunmap use cases, as well as which local IP address is returned
 | 
			
		||||
in the PFCP response
 | 
			
		||||
 | 
			
		||||
1. Look up Network Instance name in the osmo-upf.cfg `netinst` section, to obtain a local IP address.
 | 
			
		||||
2. Depending on use case:
 | 
			
		||||
  - tunend: create the tunnel on a GTP device matching the local IP address, see <<gtp_module>>.
 | 
			
		||||
  - tunmap: use the local IP address in the netfilter ruleset, see <<nftables>>.
 | 
			
		||||
3. Usually, return the chosen local IP address in the F-TEID IE of the Created PDR IE in the PFCP response.
 | 
			
		||||
 | 
			
		||||
Network Instance configuration consists of {name, IP address} pairs.
 | 
			
		||||
 | 
			
		||||
NOTE:: As soon as a `netinst` configuration is nonempty, receiving an undefined Network Instance name results in a PFCP
 | 
			
		||||
Reject response, and a log message on cateogry `session`, level `NOTICE`. To make the PFCP return success, add the
 | 
			
		||||
failing name to the `netinst` config.
 | 
			
		||||
 | 
			
		||||
=== netinst for tunend
 | 
			
		||||
 | 
			
		||||
The following configuration sets up two GTP devices for tunend, expecting Network Instance names `access1` or `access2`:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
tunend
 | 
			
		||||
 dev create apn1 10.0.0.1
 | 
			
		||||
 dev create apn2 10.0.0.2
 | 
			
		||||
netinst
 | 
			
		||||
 add access1 10.0.0.1
 | 
			
		||||
 add access2 10.0.0.2
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
For example, if a Create PDR IE indicates Network Instance = `access1`, a GTP tunnel is set up in GTP kernel device
 | 
			
		||||
`apn1`. For `access2`, use `apn2`.
 | 
			
		||||
 | 
			
		||||
=== netinst for tunmap
 | 
			
		||||
 | 
			
		||||
For the tunmap use case, it is sufficient to configure `netinst` entries, without any addition to the `tunmap` section.
 | 
			
		||||
The following example configures various interfaces for tunmap, to match Network Instance names received in PFCP:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
tunmap
 | 
			
		||||
 table-name osmo-upf
 | 
			
		||||
netinst
 | 
			
		||||
 add access1 10.0.0.1
 | 
			
		||||
 add access2 10.0.0.2
 | 
			
		||||
 add core1 9.0.0.1
 | 
			
		||||
 add core2 9.0.0.2
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
For example, a Create PDR indicating a Network Instance of `core1` will result in an nftables rule that receives packets
 | 
			
		||||
on local address `9.0.0.1`.
 | 
			
		||||
@@ -22,3 +22,189 @@ The aim is to provide:
 | 
			
		||||
- 1000 modifications of tunnel state per second (add/remove/modify),
 | 
			
		||||
- 4-8 Gbps throughput,
 | 
			
		||||
- 100-125k concurrent GTP tunnels.
 | 
			
		||||
 | 
			
		||||
A typical network scenario using OsmoUPF is illustrated in the following
 | 
			
		||||
diagram:
 | 
			
		||||
 | 
			
		||||
.Typical network architecture used with OsmoUPF
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
  rankdir = LR;
 | 
			
		||||
 | 
			
		||||
  UE [label="UE\n(3G phone)"]
 | 
			
		||||
 | 
			
		||||
  subgraph cluster_hnbgw_mgw_upf {
 | 
			
		||||
    style=dotted
 | 
			
		||||
    HNBGW -> UPF [label="PFCP",constraint=false]
 | 
			
		||||
    UPF [label=OsmoUPF,style=bold]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  subgraph cluster_hnbgw_mgw_upf2 {
 | 
			
		||||
    style=dotted
 | 
			
		||||
    SGSN -> UPF2 [label="PFCP",constraint=false]
 | 
			
		||||
    UPF2 [label=OsmoUPF,style=bold]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  subgraph cluster_hnbgw_mgw_upf3 {
 | 
			
		||||
    style=dotted
 | 
			
		||||
    GGSN -> UPF3 [label="PFCP",constraint=false]
 | 
			
		||||
    UPF3 [label=OsmoUPF,style=bold]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hNodeB [shape="box",label="hNodeB\n(3G femto cell)"]
 | 
			
		||||
 | 
			
		||||
  UE -> hNodeB [label="Uu"]
 | 
			
		||||
  hNodeB -> HNBGW [label="Iuh",style=dashed]
 | 
			
		||||
  STP [label="STP\n(SCCP/M3UA)"]
 | 
			
		||||
  HNBGW -> STP -> SGSN [label="IuPS",style=dashed]
 | 
			
		||||
  SGSN -> GGSN [label="GTP-C",style="dashed"]
 | 
			
		||||
  hNodeB -> UPF -> UPF2 -> UPF3 [label="GTP-U"]
 | 
			
		||||
  UPF3 -> internet [label="apn"]
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
NOTE: at the time of writing this section, the only Osmocom component providing
 | 
			
		||||
a PFCP CPF interface is OsmoHNBGW. PFCP support has not yet made its way into
 | 
			
		||||
OsmoSGSN nor OsmoGGSN.
 | 
			
		||||
 | 
			
		||||
=== the PFCP interface
 | 
			
		||||
 | 
			
		||||
PFCP is specified by 3GPP TS 29.244.
 | 
			
		||||
 | 
			
		||||
OsmoUPF implements a PFCP User Plane Function interface, listening for PFCP
 | 
			
		||||
requests from PFCP Control Plane Function clients, to carry out proxy-relaying
 | 
			
		||||
and encapsulation/decapsulation of GTP tunnels.
 | 
			
		||||
 | 
			
		||||
OsmoUPF does not support the complete PFCP feature set. It detects exactly two
 | 
			
		||||
use cases that will provide service of actual GTP tunnels:
 | 
			
		||||
 | 
			
		||||
.tunend use case
 | 
			
		||||
----
 | 
			
		||||
Access                 osmo-upf              Core
 | 
			
		||||
 PGW                      |              PDN/internet
 | 
			
		||||
  |                PDR1:  > FAR1:             |
 | 
			
		||||
  |                IP/GTP | IP                |
 | 
			
		||||
  |        ------> F-TEID |            -----> |
 | 
			
		||||
  |                       |                   |
 | 
			
		||||
  |                FAR2:  < PDR2:             |
 | 
			
		||||
  |                IP/GTP | IP                |
 | 
			
		||||
  | F-TEID <------        | UE IP addr <----- |
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
* `tunend`: GTP tunnel encapsulation/decapsulation:
 | 
			
		||||
  - One Packet Detection Rule (PDR) accepts a GTP tunnel from the Access side
 | 
			
		||||
    with an Outer Header Removal.
 | 
			
		||||
  - This PDR uses a Forwarding Action Rule (FAR) for plain IP towards Core.
 | 
			
		||||
  - Another PDR accepts plain IP on a specific IP address from Core.
 | 
			
		||||
  - The second PDR uses a FAR towards Access with Outer Header Creation for GTP.
 | 
			
		||||
 | 
			
		||||
.tunmap use case
 | 
			
		||||
----
 | 
			
		||||
Access                 osmo-upf                 Core
 | 
			
		||||
 PGW                      |                     PGW
 | 
			
		||||
  |                PDR1:  > FAR1:                |
 | 
			
		||||
  |                IP/GTP | IP/GTP               |
 | 
			
		||||
  |        ------> F-TEID |        -----> F-TEID |
 | 
			
		||||
  |                       |                      |
 | 
			
		||||
  |                FAR2:  < PDR2:                |
 | 
			
		||||
  |                IP/GTP | IP/GTP               |
 | 
			
		||||
  | F-TEID <------        | F-TEID <-----        |
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
* `tunmap`: GTP tunnel forwarding:
 | 
			
		||||
  - One Packet Detection Rule (PDR) accepts a GTP tunnel from the Access side
 | 
			
		||||
    with an Outer Header Removal.
 | 
			
		||||
  - This PDR uses a Forwarding Action Rule (FAR) towards Core with an Outer
 | 
			
		||||
    Header Creation for GTP.
 | 
			
		||||
  - A second PDR+FAR pair like above, with Access and Core swapped.
 | 
			
		||||
 | 
			
		||||
Access and Core must be indicated by the Source Interface IE (PDR) and
 | 
			
		||||
Destination Interface IE (FAR) in PFCP.
 | 
			
		||||
 | 
			
		||||
Any set of rules only partially or not at all matching the above PDR and FAR
 | 
			
		||||
rules will not result in any actions on the GTP user plane, but will still
 | 
			
		||||
return a successful outcome in the PFCP messages.
 | 
			
		||||
 | 
			
		||||
For example, a rule set using a Source Interface other than "Access" or "Core" results
 | 
			
		||||
in a PFCP no-op, returning PFCP responses with successful outcome, but not
 | 
			
		||||
providing any GTP-U service.
 | 
			
		||||
 | 
			
		||||
This is a direct result of:
 | 
			
		||||
 | 
			
		||||
- allowing PFCP rule sets to be setup incrementally by several subsequent PFCP
 | 
			
		||||
  messages, and of
 | 
			
		||||
- OsmoUPF using Linux kernel features for the GTP user plane, where there is
 | 
			
		||||
  either a full bidirectional GTP tunnel in place or none at all.
 | 
			
		||||
 | 
			
		||||
For example, for `tunmap`, a typical CPF will establish a PFCP session in two
 | 
			
		||||
steps: first request a local F-TEID from the UPF before passing on a data
 | 
			
		||||
service request from Access to Core. When the Core side has responded with its
 | 
			
		||||
GTP details, the PFCP session at the UPF is updated (Session Modifification),
 | 
			
		||||
to form a complete PFCP rule set.
 | 
			
		||||
 | 
			
		||||
.Typical sequence of establishing a GTP-U tunnel relay
 | 
			
		||||
["mscgen"]
 | 
			
		||||
----
 | 
			
		||||
msc {
 | 
			
		||||
	hscale="1";
 | 
			
		||||
	sgsn[label="SGSN"],sgwc[label="SGW-C"],sgwu[label="SGW-U"],pgwc[label="PGW-C"];
 | 
			
		||||
 | 
			
		||||
	sgsn << pgwc [label="Access"];
 | 
			
		||||
	sgsn >> pgwc [label="Core"];
 | 
			
		||||
 | 
			
		||||
	sgsn => sgwc [label="GTP Create Session Request\n\n\n"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgwc => sgwu [label="PFCP Session Establishment Request\n\n2x Create PDR\nF-TEID = CHOOSE"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgwc <= sgwu [label="PFCP Session Establishment Response\n\n2x Created PDR\nwith chosen local F-TEID"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgwc => pgwc [label="GTP Create Session Request\nwith chosen local F-TEID towards Core"];
 | 
			
		||||
	sgwc <= pgwc [label="GTP Create Session Response\nwith remote F-TEID at Core"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgwc => sgwu [label="PFCP Session Modification Request\n\nUpdate FAR\nwith remote F-TEID at Core"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgwc <= sgwu [label="PFCP Session Modification Response\n\n\n"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
 | 
			
		||||
	sgsn <= sgwc [label="GTP Create Session Response\n\n\n"];
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
The OsmoUPF logging as well as the VTY interface yield information on whether a
 | 
			
		||||
ruleset results in an actual bidirectional GTP tunnel being set up.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== the GTP interface
 | 
			
		||||
 | 
			
		||||
OsmoUPF requires the following Linux kernel features to provide the GTP user
 | 
			
		||||
plane functionality:
 | 
			
		||||
 | 
			
		||||
- the Linux kernel GTP module for encapsulation/decapsulation between GTP and
 | 
			
		||||
  plain IP.
 | 
			
		||||
- the Linux netfilter nftables feature for relaying GTP, i.e. forwarding between
 | 
			
		||||
  two GTP tunnels.
 | 
			
		||||
 | 
			
		||||
Tunnel relaying with netfilter requires at least Linux kernel 5.17.
 | 
			
		||||
 | 
			
		||||
To be able to interact with these Linux kernel features, the osmo-upf binary
 | 
			
		||||
needs cap_net_admin privileges, as in:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
sudo setcap cap_net_admin+pe /usr/bin/osmo-upf
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
Without above Linux kernel features, or when no cap_net_admin is available,
 | 
			
		||||
OsmoUPF is only useful for testing PFCP clients: the GTP features may be run in
 | 
			
		||||
mockup mode, so that OsmoUPF serves as a "dry run" PFCP server.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										269
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
			
		||||
== Running OsmoUPF
 | 
			
		||||
 | 
			
		||||
The OsmoUPF executable (`osmo-upf`) offers the following command-line
 | 
			
		||||
arguments:
 | 
			
		||||
 | 
			
		||||
=== SYNOPSIS
 | 
			
		||||
 | 
			
		||||
*osmo-upf* [-h|-V] [-D] [-c 'CONFIGFILE']
 | 
			
		||||
 | 
			
		||||
=== OPTIONS
 | 
			
		||||
 | 
			
		||||
*-h, --help*::
 | 
			
		||||
	Print a short help message about the supported options
 | 
			
		||||
*-V, --version*::
 | 
			
		||||
	Print the compile-time version number of the OsmoHNBGW program
 | 
			
		||||
*-D, --daemonize*::
 | 
			
		||||
	Fork the process as a daemon into background.
 | 
			
		||||
*-c, --config-file 'CONFIGFILE'*::
 | 
			
		||||
	Specify the file and path name of the configuration file to be
 | 
			
		||||
	used. If none is specified, use `osmo-upf.cfg` in the current
 | 
			
		||||
	working directory.
 | 
			
		||||
 | 
			
		||||
=== Multiple instances
 | 
			
		||||
 | 
			
		||||
Running multiple instances of `osmo-upf` on the same computer is possible if
 | 
			
		||||
all interfaces (VTY, CTRL, PFCP) are separated using the appropriate
 | 
			
		||||
configuration options. The IP based interfaces are binding to local host by
 | 
			
		||||
default. In order to separate the processes, the user has to bind those
 | 
			
		||||
services to different ports, or different specific IP addresses.
 | 
			
		||||
 | 
			
		||||
The VTY and the Control interface can be bound to IP addresses from the loopback
 | 
			
		||||
address range, for example:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
line vty
 | 
			
		||||
 bind 127.0.0.2
 | 
			
		||||
ctrl
 | 
			
		||||
 bind 127.0.0.2
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
The PFCP port is specified to be fixed as port 8805. Hence, each osmo-upf
 | 
			
		||||
process needs to run on a distinct local interface:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
pfcp
 | 
			
		||||
  local-addr 10.9.0.2
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
For GTP encapsulation/decapsulation and GTP tunnel relaying, osmo-upf depends on
 | 
			
		||||
the IP addresses configured at the Linux kernel GTP module, and the IP addresses
 | 
			
		||||
negotiated within PFCP by the control plane function.
 | 
			
		||||
 | 
			
		||||
If multiple `osmo-upf` processes are running on the same Linux kernel, each
 | 
			
		||||
`osmo-upf` needs to be configured with a distinct netfilter table name, so that
 | 
			
		||||
naming of individual tunnel rulesets does not collide:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
tunmap
 | 
			
		||||
 table-name osmo-upf-2
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== 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:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
pfcp
 | 
			
		||||
 local-addr 10.9.8.7
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
3GPP TS 29.244 4.2.2 specifies that PFCP Request messages shall be sent to UDP
 | 
			
		||||
port 8805, i.e. the PFCP port is fixed as 8805 and currently not configurable in
 | 
			
		||||
osmo-upf.
 | 
			
		||||
 | 
			
		||||
Setting a 'local-addr' is required: the PFCP protocol features a Node ID, which
 | 
			
		||||
uniquely identifies PFCP peers across different interfaces. According to the
 | 
			
		||||
PFCP specification, the Node ID can be a fully-qualified domain name (FQDN) or
 | 
			
		||||
an IP address. Currently, osmo-upf has no support for using an FQDN as Node
 | 
			
		||||
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 .
 | 
			
		||||
 | 
			
		||||
=== Linux Kernel Features
 | 
			
		||||
 | 
			
		||||
OsmoUPF uses two distinct Linux kernel features:
 | 
			
		||||
 | 
			
		||||
* The GTP module is used for `tunend`: GTP encapsulation/decapsulation from/to
 | 
			
		||||
  "the internet".
 | 
			
		||||
 | 
			
		||||
* 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]
 | 
			
		||||
----
 | 
			
		||||
include::upf_gtp_roles.dot[]
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
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`
 | 
			
		||||
 | 
			
		||||
The Linux kernel GTP module is used for the `tunend` use case, i.e. GTP
 | 
			
		||||
encapsulation/decapsulation from/to "the internet".
 | 
			
		||||
 | 
			
		||||
To use the GTP kernel module, OsmoUPF requires a GTP device, which is a
 | 
			
		||||
dedicated network device provided by the Linux kernel, serving as GTP tunnel
 | 
			
		||||
endpoint. It is typically named like "apn0".
 | 
			
		||||
 | 
			
		||||
`osmo-upf` can either create a GTP device on startup, or use a pre-existing GTP
 | 
			
		||||
device. To en/decapsulate GTP, the APN device needs to be assigned an IP address
 | 
			
		||||
range that matches the UE IP addresses that are configured in GTP-C / PFCP.
 | 
			
		||||
 | 
			
		||||
The following configuration placed in `osmo-upf.cfg` creates a GTP device called
 | 
			
		||||
`apn23` on startup of osmo-upf, which is destroyed on program exit. It listens
 | 
			
		||||
for GTP on local IP address `1.2.3.4`:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
tunend
 | 
			
		||||
 dev create apn23 1.2.3.4
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
TODO:: `osmo-upf` is not yet able to configure this network device's IP address
 | 
			
		||||
range, MTU etc.
 | 
			
		||||
 | 
			
		||||
The following configuration placed in `osmo-upf.cfg` uses a pre-existing device
 | 
			
		||||
called `apn42`:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
tunend
 | 
			
		||||
 dev use apn42 2.3.4.5
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
GTP kernel devices can be managed manually using the `gtp-link` program
 | 
			
		||||
available from the 'libgtpnl' project:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
# gtp-link add apn42
 | 
			
		||||
(keep this process running)
 | 
			
		||||
# ip addr add dev apn42 192.168.42.1/24
 | 
			
		||||
 | 
			
		||||
$ osmo-upf -c osmo-upf.cfg
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
It is possible to configure multiple GTP devices in `osmo-upf.cfg`. Depending on
 | 
			
		||||
the Network Instance name, osmo-upf creates tunnel endpoints on the GTP device
 | 
			
		||||
with a matching IP address:
 | 
			
		||||
 | 
			
		||||
- The Network Instance IE in the PDR on the Access side determines the local IP
 | 
			
		||||
  address to use, see <<netinst>>.
 | 
			
		||||
- This local IP address in turn determines the GTP device to use.
 | 
			
		||||
 | 
			
		||||
It is possible for a GTP device to listen on ANY -- just omit the IP address in
 | 
			
		||||
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`
 | 
			
		||||
 | 
			
		||||
The Linux kernel netfilter module is used for GTP tunnel proxying, also known as
 | 
			
		||||
tunnel forwarding or tunnel mapping.
 | 
			
		||||
 | 
			
		||||
When using the netfilter module, you may set up `osmo-upf.cfg` for:
 | 
			
		||||
- GTP Echo (required)
 | 
			
		||||
- nft table name (optional)
 | 
			
		||||
 | 
			
		||||
[[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
 | 
			
		||||
 table-name my-table-name
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
When running more than one osmo-upf process on a system, pick distinct table
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										31
									
								
								doc/manuals/chapters/upf_gtp_roles.dot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								doc/manuals/chapters/upf_gtp_roles.dot
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
rankdir=LR
 | 
			
		||||
sgsn [label="SGSN"]
 | 
			
		||||
 | 
			
		||||
subgraph cluster_sgw {
 | 
			
		||||
	style=invisible
 | 
			
		||||
	sgwc [label="SGW-C"]
 | 
			
		||||
	sgwu [label="OsmoUPF as SGW-U\ntunnel proxy\n*netfilter* kernel module",style=bold,shape=box]
 | 
			
		||||
	sgwc -> sgwu [label="PFCP",constraint=false]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subgraph cluster_pgw {
 | 
			
		||||
	style=invisible
 | 
			
		||||
	pgwc [label="PGW-C"]
 | 
			
		||||
	pgwu [label="OsmoUPF as PGW-U\ntunnel proxy\n*netfilter* kernel module",style=bold,shape=box]
 | 
			
		||||
	pgwc -> pgwu [label="PFCP",constraint=false]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subgraph cluster_tdf {
 | 
			
		||||
	style=invisible
 | 
			
		||||
	tdfc [label="TDF-C"]
 | 
			
		||||
	tdfu [label="OsmoUPF as TDF-U\ntunnel en-/decaps\n*GTP* kernel module",style=bold,shape=box]
 | 
			
		||||
	tdfc -> tdfu [label="PFCP",constraint=false]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pdn [label="PDN\n'the internet'"]
 | 
			
		||||
 | 
			
		||||
sgsn -> sgwc -> pgwc -> tdfc [label="GTP-C"]
 | 
			
		||||
sgsn -> sgwu -> pgwu -> tdfu [label="GTP-U",dir=both]
 | 
			
		||||
tdfu -> pdn [label="IP",dir=both]
 | 
			
		||||
}
 | 
			
		||||
@@ -9,6 +9,10 @@ include::./common/chapters/preface.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/overview.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/running.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/netinst.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/logging.adoc[]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	netinst.h \
 | 
			
		||||
	up_endpoint.h \
 | 
			
		||||
	up_peer.h \
 | 
			
		||||
	up_session.h \
 | 
			
		||||
@@ -6,5 +7,6 @@ noinst_HEADERS = \
 | 
			
		||||
	upf_gtp.h \
 | 
			
		||||
	upf_gtpu_echo.h \
 | 
			
		||||
	upf_nft.h \
 | 
			
		||||
	upf_tun.h \
 | 
			
		||||
	up_gtp_action.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								include/osmocom/upf/netinst.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								include/osmocom/upf/netinst.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2022 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 <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
struct vty;
 | 
			
		||||
 | 
			
		||||
struct network_instance {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	char *name;
 | 
			
		||||
	struct osmo_sockaddr_str addr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct network_instance *netinst_add(void *ctx, struct llist_head *list, const char *name, const char *addr,
 | 
			
		||||
					   const char **errmsg);
 | 
			
		||||
const struct network_instance *netinst_find(struct llist_head *list, const char *name);
 | 
			
		||||
const struct network_instance *netinst_first(struct llist_head *list);
 | 
			
		||||
int netinst_clear(struct llist_head *list);
 | 
			
		||||
 | 
			
		||||
int netinst_vty_write(struct vty *vty, struct llist_head *list, const char *indent, const char *name_or_null);
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/hashtable.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_pfcp_msg;
 | 
			
		||||
struct osmo_pfcp_endpoint;
 | 
			
		||||
@@ -35,14 +36,18 @@ struct osmo_sockaddr;
 | 
			
		||||
struct up_endpoint {
 | 
			
		||||
	struct osmo_pfcp_endpoint *pfcp_ep;
 | 
			
		||||
 | 
			
		||||
	/* list of struct up_peer. */
 | 
			
		||||
	struct llist_head peers;
 | 
			
		||||
	/* hashtable of (struct up_session) with key up_seid.
 | 
			
		||||
	 * Allows quick access to sessions (and its endpoint as backpointer)
 | 
			
		||||
	 * with a given up_seid. */
 | 
			
		||||
	DECLARE_HASHTABLE(sessions_by_up_seid, 10);
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ struct up_session;
 | 
			
		||||
 | 
			
		||||
enum up_gtp_action_kind {
 | 
			
		||||
	UP_GTP_DROP,
 | 
			
		||||
	UP_GTP_U_ENDECAPS,
 | 
			
		||||
	UP_GTP_U_TUNEND,
 | 
			
		||||
	UP_GTP_U_TUNMAP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -48,22 +48,25 @@ struct up_gtp_action {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	struct up_session *session;
 | 
			
		||||
 | 
			
		||||
	uint16_t pdr_core;
 | 
			
		||||
	uint16_t pdr_access;
 | 
			
		||||
	uint16_t pdr_core;
 | 
			
		||||
 | 
			
		||||
	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_tun_desc endecaps;
 | 
			
		||||
		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 */
 | 
			
		||||
	void *handle;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct up_gtp_action *up_gtp_action_alloc(void *ctx, struct up_session *session, enum up_gtp_action_kind kind, struct llist_head *dst);
 | 
			
		||||
void up_gtp_action_free(struct up_gtp_action *a);
 | 
			
		||||
 | 
			
		||||
int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b);
 | 
			
		||||
 | 
			
		||||
int up_gtp_action_enable(struct up_gtp_action *a);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,9 +41,11 @@ enum up_peer_event {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct up_peer {
 | 
			
		||||
	/* item in up_endpoint->peers */
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	/* backpointer */
 | 
			
		||||
	struct up_endpoint *up_endpoint;
 | 
			
		||||
 | 
			
		||||
	/* peer's remote address */
 | 
			
		||||
@@ -61,8 +63,8 @@ struct up_peer {
 | 
			
		||||
	struct osmo_use_count use_count;
 | 
			
		||||
	struct osmo_use_count_entry use_count_buf[5];
 | 
			
		||||
 | 
			
		||||
	DECLARE_HASHTABLE(sessions_by_up_seid, 6);
 | 
			
		||||
	DECLARE_HASHTABLE(sessions_by_cp_seid, 6);
 | 
			
		||||
	DECLARE_HASHTABLE(sessions_by_up_seid, 10);
 | 
			
		||||
	DECLARE_HASHTABLE(sessions_by_cp_seid, 10);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct up_peer *up_peer_find_or_add(struct up_endpoint *up_ep, const struct osmo_sockaddr *remote_addr);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,17 +41,15 @@ enum up_session_fsm_event {
 | 
			
		||||
	UP_SESSION_EV_USE_COUNT_ZERO,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum up_session_kind {
 | 
			
		||||
	UP_SESSION_DROP,
 | 
			
		||||
	UP_SESSION_GTP_U_ENDECAPS,
 | 
			
		||||
	UP_SESSION_GTP_U_FORW,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct up_session {
 | 
			
		||||
	struct hlist_node node_by_up_seid;
 | 
			
		||||
	struct hlist_node node_by_cp_seid;
 | 
			
		||||
 | 
			
		||||
	/* item in up_endpoint->peers_by_up_seid: */
 | 
			
		||||
	struct hlist_node ep_node_by_up_seid;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	/* backpointer */
 | 
			
		||||
	struct up_peer *up_peer;
 | 
			
		||||
 | 
			
		||||
	struct osmo_pfcp_ie_f_seid cp_f_seid;
 | 
			
		||||
@@ -60,18 +58,20 @@ struct up_session {
 | 
			
		||||
	struct osmo_use_count use_count;
 | 
			
		||||
	struct osmo_use_count_entry use_count_buf[8];
 | 
			
		||||
 | 
			
		||||
	/* llist of struct pdr */
 | 
			
		||||
	struct llist_head pdrs;
 | 
			
		||||
	/* llist of struct far */
 | 
			
		||||
	struct llist_head fars;
 | 
			
		||||
	/* llist of struct chosen_f_teid */
 | 
			
		||||
	struct llist_head chosen_f_teids;
 | 
			
		||||
 | 
			
		||||
	/* llist of struct up_gtp_action */
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
void up_session_set_msg_ctx(struct up_session *session, struct osmo_pfcp_msg *m);
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +85,8 @@ int up_session_to_str_buf(char *buf, size_t buflen, struct up_session *session);
 | 
			
		||||
char *up_session_to_str_c(void *ctx, struct up_session *session);
 | 
			
		||||
 | 
			
		||||
struct pdr {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	struct llist_head entry; /* item in session->pdrs */
 | 
			
		||||
	struct hlist_node node_by_local_f_teid; /* item in g_upf->gtp.pdrs_by_local_f_teid */
 | 
			
		||||
	struct up_session *session;
 | 
			
		||||
 | 
			
		||||
	struct osmo_pfcp_ie_create_pdr desc;
 | 
			
		||||
@@ -96,8 +97,8 @@ struct pdr {
 | 
			
		||||
 | 
			
		||||
	bool rx_decaps;
 | 
			
		||||
	bool forw_encaps;
 | 
			
		||||
	bool forw_to_core;
 | 
			
		||||
	bool forw_from_core;
 | 
			
		||||
	bool access_to_core;
 | 
			
		||||
	bool core_to_access;
 | 
			
		||||
 | 
			
		||||
	struct pdr *reverse_pdr;
 | 
			
		||||
	bool active;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/hashtable.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_tdef;
 | 
			
		||||
struct ctrl_handle;
 | 
			
		||||
@@ -37,14 +38,21 @@ struct nft_ctx;
 | 
			
		||||
 | 
			
		||||
#define UPF_PFCP_LISTEN_DEFAULT "0.0.0.0"
 | 
			
		||||
 | 
			
		||||
#define PORT_GTP0_C 3386
 | 
			
		||||
#define PORT_GTP0_U 3386
 | 
			
		||||
 | 
			
		||||
#define PORT_GTP1_C 2123
 | 
			
		||||
#define PORT_GTP1_U 2152
 | 
			
		||||
 | 
			
		||||
extern struct osmo_tdef_group g_upf_tdef_groups[];
 | 
			
		||||
extern struct osmo_tdef g_upf_nft_tdefs[];
 | 
			
		||||
 | 
			
		||||
struct pfcp_vty_cfg {
 | 
			
		||||
	char *local_addr;
 | 
			
		||||
	uint16_t local_port;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gtp_vty_cfg_dev {
 | 
			
		||||
struct tunend_vty_cfg_dev {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	/* If true, osmo-upf creates the GTP device on startup. If false, the GTP device was created by the user, and we
 | 
			
		||||
@@ -60,12 +68,18 @@ struct gtp_vty_cfg_dev {
 | 
			
		||||
	char *local_addr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gtp_vty_cfg {
 | 
			
		||||
	/* list of struct gtp_vty_cfg_dev, GTP devices as in the config file. The actual GTP devices in use are in
 | 
			
		||||
	 * g_upf->gtp.devs. */
 | 
			
		||||
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->tunend.devs. */
 | 
			
		||||
	struct llist_head devs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Item in an llist of string pointers */
 | 
			
		||||
struct string_listitem {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	char *str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct g_upf {
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +94,7 @@ struct g_upf {
 | 
			
		||||
		bool mockup;
 | 
			
		||||
 | 
			
		||||
		/* GTP devices as in osmo-upf.cfg */
 | 
			
		||||
		struct gtp_vty_cfg vty_cfg;
 | 
			
		||||
		struct tunend_vty_cfg vty_cfg;
 | 
			
		||||
 | 
			
		||||
		/* GTP devices actually in use, list of struct upf_gtp_dev. */
 | 
			
		||||
		struct llist_head devs;
 | 
			
		||||
@@ -89,7 +103,7 @@ struct g_upf {
 | 
			
		||||
		int32_t genl_id;
 | 
			
		||||
 | 
			
		||||
		uint8_t recovery_count;
 | 
			
		||||
	} gtp;
 | 
			
		||||
	} tunend;
 | 
			
		||||
 | 
			
		||||
	/* Tunnel forwarding via linux netfilter */
 | 
			
		||||
	struct {
 | 
			
		||||
@@ -98,9 +112,21 @@ struct g_upf {
 | 
			
		||||
 | 
			
		||||
		struct nft_ctx *nft_ctx;
 | 
			
		||||
		char *table_name;
 | 
			
		||||
		int priority;
 | 
			
		||||
		uint32_t next_id_state;
 | 
			
		||||
	} nft;
 | 
			
		||||
		int priority_pre;
 | 
			
		||||
		int priority_post;
 | 
			
		||||
		uint32_t next_chain_id_state;
 | 
			
		||||
		/* hashtable of (struct upf_nft_tun)->node_by_chain_id: */
 | 
			
		||||
		DECLARE_HASHTABLE(nft_tun_by_chain_id, 12);
 | 
			
		||||
	} tunmap;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		uint32_t next_local_teid_state;
 | 
			
		||||
		/* hashtable of (struct pdr)->node_by_local_f_teid: */
 | 
			
		||||
		DECLARE_HASHTABLE(pdrs_by_local_f_teid, 12);
 | 
			
		||||
		uint16_t next_echo_seq_nr;
 | 
			
		||||
	} gtp;
 | 
			
		||||
 | 
			
		||||
	struct llist_head netinst;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct g_upf *g_upf;
 | 
			
		||||
@@ -115,7 +141,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,18 +24,15 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/hashtable.h>
 | 
			
		||||
#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)
 | 
			
		||||
 | 
			
		||||
#define PORT_GTP0_C 3386
 | 
			
		||||
#define PORT_GTP0_U 3386
 | 
			
		||||
 | 
			
		||||
#define PORT_GTP1_C 2123
 | 
			
		||||
#define PORT_GTP1_U 2152
 | 
			
		||||
 | 
			
		||||
struct upf_gtp_dev {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
@@ -57,29 +54,34 @@ struct upf_gtp_dev {
 | 
			
		||||
 | 
			
		||||
	uint32_t ifidx;
 | 
			
		||||
 | 
			
		||||
	/* list of struct upf_gtp_tunend */
 | 
			
		||||
	struct llist_head tunnels;
 | 
			
		||||
	/* hashtable of (struct upf_gtp_tunen) with key desc.access.local.teid */
 | 
			
		||||
	DECLARE_HASHTABLE(tunnels_by_local_f_teid, 10);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct upf_gtp_tun_desc {
 | 
			
		||||
	uint32_t local_teid;
 | 
			
		||||
	uint32_t remote_teid;
 | 
			
		||||
	struct osmo_sockaddr ue_addr;
 | 
			
		||||
	struct osmo_sockaddr gtp_remote_addr;
 | 
			
		||||
/* 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_tunend {
 | 
			
		||||
	struct upf_tun access;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_sockaddr ue_local_addr;
 | 
			
		||||
	} core;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int upf_gtp_tun_desc_cmp(const struct upf_gtp_tun_desc *a, const struct upf_gtp_tun_desc *b);
 | 
			
		||||
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b);
 | 
			
		||||
 | 
			
		||||
int upf_gtp_genl_open();
 | 
			
		||||
int upf_gtp_genl_ensure_open();
 | 
			
		||||
void upf_gtp_genl_close();
 | 
			
		||||
 | 
			
		||||
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0,
 | 
			
		||||
		     bool sgsn_mode);
 | 
			
		||||
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_tunnel_add(struct upf_gtp_dev *dev, const struct upf_gtp_tun_desc *t);
 | 
			
		||||
bool upf_gtp_dev_is_tunnel_active(struct upf_gtp_dev *dev, const struct upf_gtp_tun_desc *t);
 | 
			
		||||
int upf_gtp_dev_tunnel_del(struct upf_gtp_dev *dev, const struct upf_gtp_tun_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);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,3 +2,4 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev);
 | 
			
		||||
int upf_gtpu_echo_req_tx(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,27 +24,30 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <osmocom/core/hashtable.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/upf/upf_tun.h>
 | 
			
		||||
 | 
			
		||||
#define NFT_CHAIN_NAME_PREFIX_TUNMAP "tunmap"
 | 
			
		||||
 | 
			
		||||
struct upf_nft_tunmap_desc {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_sockaddr gtp_remote_addr;
 | 
			
		||||
		uint32_t local_teid;
 | 
			
		||||
		uint32_t remote_teid;
 | 
			
		||||
	} access;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_sockaddr gtp_remote_addr;
 | 
			
		||||
		uint32_t local_teid;
 | 
			
		||||
		uint32_t remote_teid;
 | 
			
		||||
	} core;
 | 
			
		||||
	uint32_t id;
 | 
			
		||||
struct upf_nft_tun {
 | 
			
		||||
	struct hlist_node node_by_chain_id; /* item in g_upf->tunmap.nft_tun_by_chain_id */
 | 
			
		||||
	struct upf_tun tun;
 | 
			
		||||
	uint32_t chain_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
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_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_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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
@@ -21,3 +21,18 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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,7 +10,6 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGTLV_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOPFCP_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -19,7 +18,6 @@ AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOGTLV_LIBS) \
 | 
			
		||||
	$(LIBOSMOPFCP_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -206,17 +206,16 @@ static void signal_handler(int signum)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char * const osmo_pfcp_tool_copyright =
 | 
			
		||||
	"OsmoPFCPTool - Osmocom Packet Forwarding Control Protocol tool for testing\r\n"
 | 
			
		||||
	"Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"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";
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info pfcp_tool_vty_app_info = {
 | 
			
		||||
	.name = "osmo-pfcp-tool",
 | 
			
		||||
	.version = PACKAGE_VERSION,
 | 
			
		||||
	.copyright = osmo_pfcp_tool_copyright,
 | 
			
		||||
	.copyright =
 | 
			
		||||
	"OsmoPFCPTool - Osmocom Packet Forwarding Control Protocol tool for testing\r\n"
 | 
			
		||||
	"Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"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",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat pfcp_tool_default_categories[] = {
 | 
			
		||||
@@ -300,13 +299,13 @@ int main(int argc, char **argv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* start telnet, after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_pfcp_tool_ctx, &g_pfcp_tool, vty_get_bind_addr(), OSMO_VTY_PORT_PFCP_TOOL);
 | 
			
		||||
	/* start telnet VTY */
 | 
			
		||||
	rc = telnet_init_default(tall_pfcp_tool_ctx, &g_pfcp_tool, OSMO_VTY_PORT_PFCP_TOOL);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return 2;
 | 
			
		||||
 | 
			
		||||
	/* start control interface, after reading config for ctrl_vty_get_bind_addr() */
 | 
			
		||||
	g_pfcp_tool->ctrl = ctrl_interface_setup_dynip(g_pfcp_tool, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_PFCP_TOOL, NULL);
 | 
			
		||||
	g_pfcp_tool->ctrl = ctrl_interface_setup(g_pfcp_tool, OSMO_CTRL_PORT_PFCP_TOOL, NULL);
 | 
			
		||||
	if (!g_pfcp_tool->ctrl) {
 | 
			
		||||
		fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ struct pfcp_tool_session *pfcp_tool_session_find(struct pfcp_tool_peer *peer, ui
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid,
 | 
			
		||||
							   enum up_gtp_action_kind gtp_action)
 | 
			
		||||
							   enum up_gtp_action_kind kind)
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = pfcp_tool_session_find(peer, cp_seid);
 | 
			
		||||
	if (session)
 | 
			
		||||
@@ -95,7 +95,7 @@ struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer
 | 
			
		||||
	*session = (struct pfcp_tool_session){
 | 
			
		||||
		.peer = peer,
 | 
			
		||||
		.cp_seid = cp_seid,
 | 
			
		||||
		.gtp_action = gtp_action,
 | 
			
		||||
		.kind = kind,
 | 
			
		||||
	};
 | 
			
		||||
	llist_add(&session->entry, &peer->sessions);
 | 
			
		||||
	return session;
 | 
			
		||||
@@ -159,14 +159,23 @@ void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, st
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void copy_msg(struct osmo_pfcp_msg *dst, const struct osmo_pfcp_msg *m)
 | 
			
		||||
{
 | 
			
		||||
	*dst = *m;
 | 
			
		||||
	dst->encoded = NULL;
 | 
			
		||||
	dst->ctx.peer_use_token = NULL;
 | 
			
		||||
	dst->ctx.session_use_token = NULL;
 | 
			
		||||
	dst->ctx.resp_cb = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m);
 | 
			
		||||
	if (m->is_response)
 | 
			
		||||
		peer->last_resp = *m;
 | 
			
		||||
		copy_msg(&peer->last_resp, m);
 | 
			
		||||
	else
 | 
			
		||||
		peer->last_req = *m;
 | 
			
		||||
		copy_msg(&peer->last_req, m);
 | 
			
		||||
	rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -51,30 +51,43 @@ struct pfcp_tool_peer {
 | 
			
		||||
	struct llist_head sessions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_teid_pair {
 | 
			
		||||
	uint32_t local;
 | 
			
		||||
	uint32_t remote;
 | 
			
		||||
struct pfcp_tool_gtp_tun_ep {
 | 
			
		||||
	struct osmo_sockaddr_str addr;
 | 
			
		||||
	uint32_t teid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_gtp_tun {
 | 
			
		||||
	struct pfcp_tool_gtp_tun_ep local;
 | 
			
		||||
	struct pfcp_tool_gtp_tun_ep remote;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_tunend {
 | 
			
		||||
	struct pfcp_tool_gtp_tun access;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_sockaddr_str ue_local_addr;
 | 
			
		||||
	} core;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_tunmap {
 | 
			
		||||
	struct pfcp_tool_gtp_tun access;
 | 
			
		||||
	struct pfcp_tool_gtp_tun core;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pfcp_tool_session {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	enum up_gtp_action_kind gtp_action;
 | 
			
		||||
 | 
			
		||||
	struct pfcp_tool_peer *peer;
 | 
			
		||||
	uint64_t cp_seid;
 | 
			
		||||
	struct osmo_pfcp_ie_f_seid up_f_seid;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		struct pfcp_tool_teid_pair teid;
 | 
			
		||||
		struct osmo_sockaddr_str gtp_ip;
 | 
			
		||||
	} access;
 | 
			
		||||
	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 tunend;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		struct pfcp_tool_teid_pair teid;
 | 
			
		||||
		struct osmo_sockaddr_str gtp_ip;
 | 
			
		||||
		struct osmo_sockaddr_str ue_addr;
 | 
			
		||||
	} core;
 | 
			
		||||
		/* Tunnel-map GTP: translate from one TEID to another and forward */
 | 
			
		||||
		struct pfcp_tool_tunmap tunmap;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct g_pfcp_tool {
 | 
			
		||||
 
 | 
			
		||||
@@ -97,9 +97,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 +108,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;
 | 
			
		||||
@@ -234,7 +234,7 @@ DEFUN(peer_retrans_req, peer_retrans_req_cmd,
 | 
			
		||||
	else
 | 
			
		||||
		*m = peer->last_resp;
 | 
			
		||||
 | 
			
		||||
	OSMO_LOG_PFCP_MSG(m, LOGL_DEBUG, "retrans %s\n", argv[0]);
 | 
			
		||||
	OSMO_LOG_PFCP_MSG(m, LOGL_INFO, "retrans %s\n", argv[0]);
 | 
			
		||||
 | 
			
		||||
	rc = osmo_pfcp_endpoint_tx_data(g_pfcp_tool->ep, m);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
@@ -250,24 +250,26 @@ static struct cmd_node session_node = {
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SESSION_STR "Enter the 'session' node for the given SEID\n"
 | 
			
		||||
#define TUNEND_STR "Set up GTP tunnel encapsulation/decapsulation (default)\n"
 | 
			
		||||
#define TUNMAP_STR "Set up GTP tunnel mapping\n"
 | 
			
		||||
#define SEID_STR "local Session Endpoint ID\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(session, session_cmd,
 | 
			
		||||
      "session [(endecaps|tunmap)] [<0-18446744073709551615>]",
 | 
			
		||||
      "Enter the 'session' node for the given SEID\n"
 | 
			
		||||
      "Set up GTP tunnel encapsulation/decapsulation (default)\n"
 | 
			
		||||
      "Set up GTP tunnel mapping\n"
 | 
			
		||||
      "local Session Endpoint ID\n")
 | 
			
		||||
      "session [(tunend|tunmap)] [<0-18446744073709551615>]",
 | 
			
		||||
      SESSION_STR TUNEND_STR TUNMAP_STR SEID_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_peer *peer = vty->index;
 | 
			
		||||
	struct pfcp_tool_session *session;
 | 
			
		||||
	enum up_gtp_action_kind gtp_action = UP_GTP_U_ENDECAPS;
 | 
			
		||||
	enum up_gtp_action_kind kind = UP_GTP_U_TUNEND;
 | 
			
		||||
 | 
			
		||||
	if (argc > 0 && !strcmp(argv[0], "tunmap"))
 | 
			
		||||
		gtp_action = UP_GTP_U_TUNMAP;
 | 
			
		||||
		kind = UP_GTP_U_TUNMAP;
 | 
			
		||||
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		session = pfcp_tool_session_find_or_create(peer, atoll(argv[1]), gtp_action);
 | 
			
		||||
		session = pfcp_tool_session_find_or_create(peer, atoll(argv[1]), kind);
 | 
			
		||||
	else
 | 
			
		||||
		session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), gtp_action);
 | 
			
		||||
		session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), kind);
 | 
			
		||||
 | 
			
		||||
	vty->index = session;
 | 
			
		||||
	vty->node = SESSION_NODE;
 | 
			
		||||
@@ -275,64 +277,132 @@ DEFUN(session, session_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* legacy compat: "tunend" was originally named "endecaps" */
 | 
			
		||||
DEFUN_CMD_ELEMENT(session, session_endecaps_cmd,
 | 
			
		||||
		  "session (endecaps) [<0-18446744073709551615>]",
 | 
			
		||||
		  SESSION_STR TUNEND_STR SEID_STR, CMD_ATTR_HIDDEN, 0);
 | 
			
		||||
 | 
			
		||||
DEFUN(s_ue, s_ue_cmd,
 | 
			
		||||
      "ue ip A.B.C.D",
 | 
			
		||||
      "Setup the UE as it appears towards the Core network in plain IP traffic\n"
 | 
			
		||||
      "IP address assigned to the UE\n")
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	if (osmo_sockaddr_str_from_str2(&session->core.ue_addr, argv[0])) {
 | 
			
		||||
 | 
			
		||||
	if (session->kind != UP_GTP_U_TUNEND) {
 | 
			
		||||
		vty_out(vty, "%% Error: 'ue ip' makes no sense in a 'tunmap' session%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	if (osmo_sockaddr_str_from_str2(&session->tunend.core.ue_local_addr, argv[0])) {
 | 
			
		||||
		vty_out(vty, "Error setting UE IP address%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(s_teid, s_teid_cmd,
 | 
			
		||||
      "gtp (access|core) teid local <0-4294967295> remote <0-4294967295>",
 | 
			
		||||
      "Setup TEID used in GTP\n"
 | 
			
		||||
      "Set the TEIDs towards the ACCESS network (towards the radio network and the actual UE)\n"
 | 
			
		||||
      "Set the TEIDs towards the CORE network (towards the internet)\n"
 | 
			
		||||
      "Local TEID, which the UPF expects to see in incoming GTP packets\n"
 | 
			
		||||
      "Local TEID, when 0 tell the UPF to choose (PFCP: FAR F-TEID: CHOOSE=1)\n"
 | 
			
		||||
      "Remote TEID, which the UPF sends out in GTP packets\n"
 | 
			
		||||
      "Remote TEID, which the GTP peer has assigned for itself\n")
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	struct pfcp_tool_teid_pair *dst;
 | 
			
		||||
	if (!strcmp(argv[0], "access"))
 | 
			
		||||
		dst = &session->access.teid;
 | 
			
		||||
	else
 | 
			
		||||
		dst = &session->core.teid;
 | 
			
		||||
	*dst = (struct pfcp_tool_teid_pair){
 | 
			
		||||
		.local = atoi(argv[1]),
 | 
			
		||||
		.remote = atoi(argv[2]),
 | 
			
		||||
	};
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
#define GTP_ACCESS_CORE_STRS \
 | 
			
		||||
      "Setup GTP\n" \
 | 
			
		||||
      "Setup GTP towards ACCESS (towards the radio network and the actual UE)\n" \
 | 
			
		||||
      "Setup GTP towards CORE (towards the internet)\n"
 | 
			
		||||
#define GTP_LOCAL_STR "Setup GTP on the local side (UPF's local GTP endpoint)\n"
 | 
			
		||||
#define GTP_REMOTE_STR "Setup GTP on the remote side (UPF's remote GTP peer)\n"
 | 
			
		||||
#define F_TEID_STR "Set the fully-qualified TEID, i.e. GTP IP address and TEID\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(s_gtp, s_gtp_cmd,
 | 
			
		||||
      "gtp (access|core) ip A.B.C.D",
 | 
			
		||||
      "Setup GTP peer\n"
 | 
			
		||||
      "Set the GTP peer towards the ACCESS network (towards the radio network and the actual UE)\n"
 | 
			
		||||
      "Set the GTP peer towards the CORE network (towards the internet)\n"
 | 
			
		||||
      "Set the GTP peer IP address, where to send GTP packets to / receive GTP packets from\n"
 | 
			
		||||
      "GTP peer IP address\n")
 | 
			
		||||
DEFUN(s_f_teid, s_f_teid_cmd,
 | 
			
		||||
      "gtp (access|core) (local|remote) f-teid A.B.C.D <0-4294967295>",
 | 
			
		||||
      GTP_ACCESS_CORE_STRS
 | 
			
		||||
      GTP_LOCAL_STR GTP_REMOTE_STR
 | 
			
		||||
      F_TEID_STR
 | 
			
		||||
      "GTP peer IP address\n"
 | 
			
		||||
      "GTP TEID\n")
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	struct osmo_sockaddr_str *dst;
 | 
			
		||||
	if (!strcmp(argv[0], "access"))
 | 
			
		||||
		dst = &session->access.gtp_ip;
 | 
			
		||||
	else
 | 
			
		||||
		dst = &session->core.gtp_ip;
 | 
			
		||||
	if (osmo_sockaddr_str_from_str2(dst, argv[1])) {
 | 
			
		||||
		vty_out(vty, "Error setting GTP IP address%s", VTY_NEWLINE);
 | 
			
		||||
	const char *tun_side = argv[0];
 | 
			
		||||
	const char *local_remote = argv[1];
 | 
			
		||||
	const char *addr_str = argv[2];
 | 
			
		||||
	const char *teid_str = argv[3];
 | 
			
		||||
	struct pfcp_tool_gtp_tun_ep *dst;
 | 
			
		||||
 | 
			
		||||
	switch (session->kind) {
 | 
			
		||||
	case UP_GTP_U_TUNEND:
 | 
			
		||||
		if (!strcmp(tun_side, "access")) {
 | 
			
		||||
			if (!strcmp(local_remote, "local"))
 | 
			
		||||
				dst = &session->tunend.access.local;
 | 
			
		||||
			else
 | 
			
		||||
				dst = &session->tunend.access.remote;
 | 
			
		||||
		} else {
 | 
			
		||||
			vty_out(vty, "%% Error: 'gtp core (local|remote) f-teid': 'tunend' only has GTP on"
 | 
			
		||||
				" the 'access' side%s", VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case UP_GTP_U_TUNMAP:
 | 
			
		||||
		if (!strcmp(tun_side, "access")) {
 | 
			
		||||
			if (!strcmp(local_remote, "local"))
 | 
			
		||||
				dst = &session->tunmap.access.local;
 | 
			
		||||
			else
 | 
			
		||||
				dst = &session->tunmap.access.remote;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (!strcmp(local_remote, "local"))
 | 
			
		||||
				dst = &session->tunmap.core.local;
 | 
			
		||||
			else
 | 
			
		||||
				dst = &session->tunmap.core.remote;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmo_sockaddr_str_from_str2(&dst->addr, addr_str)) {
 | 
			
		||||
		vty_out(vty, "Error setting GTP IP address from %s%s",
 | 
			
		||||
			osmo_quote_cstr_c(OTC_SELECT, addr_str, -1), VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	dst->teid = atoi(teid_str);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int session_endecaps_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
DEFUN(s_f_teid_choose, s_f_teid_choose_cmd,
 | 
			
		||||
      "gtp (access|core) local f-teid choose",
 | 
			
		||||
      GTP_ACCESS_CORE_STRS
 | 
			
		||||
      GTP_LOCAL_STR
 | 
			
		||||
      F_TEID_STR
 | 
			
		||||
      "Send F-TEID with CHOOSE=1, i.e. the UPF shall return the local F-TEID in a PFCP Created PDR IE\n")
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	const char *tun_side = argv[0];
 | 
			
		||||
	struct pfcp_tool_gtp_tun_ep *dst;
 | 
			
		||||
 | 
			
		||||
	switch (session->kind) {
 | 
			
		||||
	case UP_GTP_U_TUNEND:
 | 
			
		||||
		if (!strcmp(tun_side, "access")) {
 | 
			
		||||
			dst = &session->tunend.access.local;
 | 
			
		||||
		} else {
 | 
			
		||||
			vty_out(vty, "%% Error: 'gtp core local choose': 'tunend' only has GTP on"
 | 
			
		||||
				" the 'access' side%s", VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case UP_GTP_U_TUNMAP:
 | 
			
		||||
		if (!strcmp(tun_side, "access"))
 | 
			
		||||
			dst = &session->tunmap.access.local;
 | 
			
		||||
		else
 | 
			
		||||
			dst = &session->tunmap.core.local;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*dst = (struct pfcp_tool_gtp_tun_ep){};
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum pdr_id_fixed {
 | 
			
		||||
	PDR_ID_CORE = 1,
 | 
			
		||||
	PDR_ID_ACCESS = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	struct pfcp_tool_peer *peer = session->peer;
 | 
			
		||||
@@ -344,6 +414,8 @@ int session_endecaps_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	struct osmo_sockaddr ue_addr;
 | 
			
		||||
	struct osmo_pfcp_ie_f_seid cp_f_seid;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(session->kind == UP_GTP_U_TUNEND);
 | 
			
		||||
 | 
			
		||||
	if (!g_pfcp_tool->ep) {
 | 
			
		||||
		vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -354,12 +426,17 @@ int session_endecaps_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	else
 | 
			
		||||
		osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
 | 
			
		||||
 | 
			
		||||
	if (osmo_sockaddr_str_to_sockaddr(&session->core.ue_addr, &ue_addr.u.sas)) {
 | 
			
		||||
		vty_out(vty, "Error in UE IP%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
#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); \
 | 
			
		||||
				return CMD_WARNING; \
 | 
			
		||||
			} \
 | 
			
		||||
		} while (0)
 | 
			
		||||
 | 
			
		||||
	if (session->access.teid.local == 0) {
 | 
			
		||||
	STR_TO_ADDR(ue_addr, session->tunend.core.ue_local_addr);
 | 
			
		||||
 | 
			
		||||
	if (session->tunend.access.local.teid == 0) {
 | 
			
		||||
		f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.choose_flag = true,
 | 
			
		||||
			.choose = {
 | 
			
		||||
@@ -369,29 +446,22 @@ int session_endecaps_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	} else {
 | 
			
		||||
		f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.fixed = {
 | 
			
		||||
				.teid = session->access.teid.local,
 | 
			
		||||
				.teid = session->tunend.access.local.teid,
 | 
			
		||||
				.ip_addr = {
 | 
			
		||||
					.v4_present = true,
 | 
			
		||||
					.v4 = osmo_pfcp_endpoint_get_cfg(g_pfcp_tool->ep)->local_addr,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		if (osmo_sockaddr_str_to_sockaddr(&session->access.gtp_ip, &f_teid_access_local.fixed.ip_addr.v4.u.sas)) {
 | 
			
		||||
			vty_out(vty, "Error in GTP IP towards Access%s", VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		STR_TO_ADDR(f_teid_access_local.fixed.ip_addr.v4, session->tunend.access.local.addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ohc_access = (struct osmo_pfcp_ie_outer_header_creation){
 | 
			
		||||
		.teid_present = true,
 | 
			
		||||
		.teid = session->access.teid.remote,
 | 
			
		||||
		.teid = session->tunend.access.remote.teid,
 | 
			
		||||
		.ip_addr.v4_present = true,
 | 
			
		||||
	};
 | 
			
		||||
	osmo_pfcp_bits_set(ohc_access.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
 | 
			
		||||
	if (osmo_sockaddr_str_to_sockaddr(&session->access.gtp_ip, &ohc_access.ip_addr.v4.u.sas)) {
 | 
			
		||||
		vty_out(vty, "Error in GTP IP towards Access%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	STR_TO_ADDR(ohc_access.ip_addr.v4, session->tunend.access.remote.addr);
 | 
			
		||||
 | 
			
		||||
	cp_f_seid = (struct osmo_pfcp_ie_f_seid){
 | 
			
		||||
		.seid = session->cp_seid,
 | 
			
		||||
@@ -410,7 +480,7 @@ int session_endecaps_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,
 | 
			
		||||
@@ -427,7 +497,7 @@ int session_endecaps_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,
 | 
			
		||||
@@ -500,7 +570,7 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	else
 | 
			
		||||
		osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
 | 
			
		||||
 | 
			
		||||
	if (session->access.teid.local == 0) {
 | 
			
		||||
	if (session->tunmap.access.local.teid == 0) {
 | 
			
		||||
		f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.choose_flag = true,
 | 
			
		||||
			.choose = {
 | 
			
		||||
@@ -510,31 +580,25 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	} else {
 | 
			
		||||
		f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.fixed = {
 | 
			
		||||
				.teid = session->access.teid.local,
 | 
			
		||||
				.teid = session->tunmap.access.local.teid,
 | 
			
		||||
				.ip_addr = {
 | 
			
		||||
					.v4_present = true,
 | 
			
		||||
					.v4 = osmo_pfcp_endpoint_get_cfg(g_pfcp_tool->ep)->local_addr,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		if (osmo_sockaddr_str_to_sockaddr(&session->access.gtp_ip, &f_teid_access_local.fixed.ip_addr.v4.u.sas)) {
 | 
			
		||||
			vty_out(vty, "Error in GTP IP towards Access%s", VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		STR_TO_ADDR(f_teid_access_local.fixed.ip_addr.v4, session->tunmap.access.local.addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ohc_access = (struct osmo_pfcp_ie_outer_header_creation){
 | 
			
		||||
		.teid_present = true,
 | 
			
		||||
		.teid = session->access.teid.remote,
 | 
			
		||||
		.teid = session->tunmap.access.remote.teid,
 | 
			
		||||
		.ip_addr.v4_present = true,
 | 
			
		||||
	};
 | 
			
		||||
	osmo_pfcp_bits_set(ohc_access.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
 | 
			
		||||
	if (osmo_sockaddr_str_to_sockaddr(&session->access.gtp_ip, &ohc_access.ip_addr.v4.u.sas)) {
 | 
			
		||||
		vty_out(vty, "Error in GTP IP towards Access%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	STR_TO_ADDR(ohc_access.ip_addr.v4, session->tunmap.access.remote.addr);
 | 
			
		||||
 | 
			
		||||
	if (session->core.teid.local == 0) {
 | 
			
		||||
	if (session->tunmap.core.local.teid == 0) {
 | 
			
		||||
		f_teid_core_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.choose_flag = true,
 | 
			
		||||
			.choose = {
 | 
			
		||||
@@ -544,28 +608,21 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
 | 
			
		||||
	} else {
 | 
			
		||||
		f_teid_core_local = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.fixed = {
 | 
			
		||||
				.teid = session->core.teid.local,
 | 
			
		||||
				.teid = session->tunmap.core.local.teid,
 | 
			
		||||
				.ip_addr = {
 | 
			
		||||
					.v4_present = true,
 | 
			
		||||
					.v4 = osmo_pfcp_endpoint_get_cfg(g_pfcp_tool->ep)->local_addr,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		if (osmo_sockaddr_str_to_sockaddr(&session->core.gtp_ip, &f_teid_core_local.fixed.ip_addr.v4.u.sas)) {
 | 
			
		||||
			vty_out(vty, "Error in GTP IP towards Core%s", VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		STR_TO_ADDR(f_teid_core_local.fixed.ip_addr.v4, session->tunmap.core.local.addr);
 | 
			
		||||
	}
 | 
			
		||||
	ohc_core = (struct osmo_pfcp_ie_outer_header_creation){
 | 
			
		||||
		.teid_present = true,
 | 
			
		||||
		.teid = session->core.teid.remote,
 | 
			
		||||
		.teid = session->tunmap.core.remote.teid,
 | 
			
		||||
		.ip_addr.v4_present = true,
 | 
			
		||||
	};
 | 
			
		||||
	osmo_pfcp_bits_set(ohc_core.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
 | 
			
		||||
	if (osmo_sockaddr_str_to_sockaddr(&session->core.gtp_ip, &ohc_core.ip_addr.v4.u.sas)) {
 | 
			
		||||
		vty_out(vty, "Error in GTP IP towards Core%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	STR_TO_ADDR(ohc_core.ip_addr.v4, session->tunmap.core.remote.addr);
 | 
			
		||||
 | 
			
		||||
	cp_f_seid = (struct osmo_pfcp_ie_f_seid){
 | 
			
		||||
		.seid = session->cp_seid,
 | 
			
		||||
@@ -583,7 +640,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,
 | 
			
		||||
@@ -598,7 +655,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,
 | 
			
		||||
@@ -653,9 +710,9 @@ DEFUN(session_tx_est_req, session_tx_est_req_cmd,
 | 
			
		||||
      "Set FAR to DROP = 1\n")
 | 
			
		||||
{
 | 
			
		||||
	struct pfcp_tool_session *session = vty->index;
 | 
			
		||||
	switch (session->gtp_action) {
 | 
			
		||||
	case UP_GTP_U_ENDECAPS:
 | 
			
		||||
		return session_endecaps_tx_est_req(vty, argv, argc);
 | 
			
		||||
	switch (session->kind) {
 | 
			
		||||
	case UP_GTP_U_TUNEND:
 | 
			
		||||
		return session_tunend_tx_est_req(vty, argv, argc);
 | 
			
		||||
	case UP_GTP_U_TUNMAP:
 | 
			
		||||
		return session_tunmap_tx_est_req(vty, argv, argc);
 | 
			
		||||
	default:
 | 
			
		||||
@@ -776,12 +833,13 @@ void pfcp_tool_vty_init_cmds()
 | 
			
		||||
	install_element(PEER_NODE, &peer_retrans_req_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, &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_gtp_cmd);
 | 
			
		||||
	install_element(SESSION_NODE, &s_teid_cmd);
 | 
			
		||||
	install_element(SESSION_NODE, &s_f_teid_cmd);
 | 
			
		||||
	install_element(SESSION_NODE, &s_f_teid_choose_cmd);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGTLV_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOPFCP_CFLAGS) \
 | 
			
		||||
	$(LIBGTPNL_CFLAGS) \
 | 
			
		||||
	$(LIBNFTNL_CFLAGS) \
 | 
			
		||||
@@ -19,18 +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 = \
 | 
			
		||||
	osmo_upf_main.c \
 | 
			
		||||
libupf_la_SOURCES = \
 | 
			
		||||
	netinst.c \
 | 
			
		||||
	up_endpoint.c \
 | 
			
		||||
	up_gtp_action.c \
 | 
			
		||||
	up_peer.c \
 | 
			
		||||
@@ -42,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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								src/osmo-upf/netinst.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/osmo-upf/netinst.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2022 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 <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/upf/netinst.h>
 | 
			
		||||
 | 
			
		||||
/* Add a new netinst entry to the given list.
 | 
			
		||||
 * \param ctx  talloc allocate new entry from ctx.
 | 
			
		||||
 * \param list  append to this list.
 | 
			
		||||
 * \param name  The Network Instance name as given in PFCP Network Instance IEs.
 | 
			
		||||
 * \param addr  IP address string of local interface to associate with the Network Instance.
 | 
			
		||||
 * \param errmsg  On error, an error description is returned in this out-argument.
 | 
			
		||||
 * \return new network_instance entry, or NULL on error.
 | 
			
		||||
 */
 | 
			
		||||
const struct network_instance *netinst_add(void *ctx, struct llist_head *list, const char *name, const char *addr,
 | 
			
		||||
					   const char **errmsg)
 | 
			
		||||
{
 | 
			
		||||
	struct network_instance *netinst;
 | 
			
		||||
	if (errmsg)
 | 
			
		||||
		*errmsg = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!name || !*name) {
 | 
			
		||||
		if (errmsg)
 | 
			
		||||
			*errmsg = "Network Instance name must not be empty";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (netinst_find(list, name)) {
 | 
			
		||||
		if (errmsg)
 | 
			
		||||
			*errmsg = "Network Instance entry with this name already exists";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	netinst = talloc(ctx, struct network_instance);
 | 
			
		||||
	*netinst = (struct network_instance){
 | 
			
		||||
		.name = talloc_strdup(netinst, name),
 | 
			
		||||
	};
 | 
			
		||||
	if (osmo_sockaddr_str_from_str(&netinst->addr, addr, 0)) {
 | 
			
		||||
		if (errmsg)
 | 
			
		||||
			*errmsg = "Network Instance address is not a valid IP address string";
 | 
			
		||||
		talloc_free(netinst);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_add_tail(&netinst->entry, list);
 | 
			
		||||
 | 
			
		||||
	return netinst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct network_instance *netinst_find(struct llist_head *list, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	const struct network_instance *netinst;
 | 
			
		||||
 | 
			
		||||
	if (!name)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(netinst, list, entry)
 | 
			
		||||
		if (!strcmp(netinst->name, name))
 | 
			
		||||
			return netinst;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct network_instance *netinst_first(struct llist_head *list)
 | 
			
		||||
{
 | 
			
		||||
	return llist_first_entry_or_null(list, struct network_instance, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear the list of Network Instance entries, return the nr of entries that were removed. */
 | 
			
		||||
int netinst_clear(struct llist_head *list)
 | 
			
		||||
{
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	while (1) {
 | 
			
		||||
		struct network_instance *netinst = llist_first_entry_or_null(list, struct network_instance, entry);
 | 
			
		||||
		if (!netinst)
 | 
			
		||||
			break;
 | 
			
		||||
		llist_del(&netinst->entry);
 | 
			
		||||
		talloc_free(netinst);
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write one or all netinst entries to the VTY output.
 | 
			
		||||
 * If name_or_null is NULL, print all entries. Else, print only the entry matching that name.
 | 
			
		||||
 * Return number of printed entries. */
 | 
			
		||||
int netinst_vty_write(struct vty *vty, struct llist_head *list, const char *indent, const char *name_or_null)
 | 
			
		||||
{
 | 
			
		||||
	const struct network_instance *netinst;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(netinst, list, entry) {
 | 
			
		||||
		if (name_or_null && strcmp(netinst->name, name_or_null))
 | 
			
		||||
			continue;
 | 
			
		||||
		vty_out(vty, "%sadd %s %s%s", indent, netinst->name, netinst->addr.ip, VTY_NEWLINE);
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
@@ -304,13 +304,13 @@ int main(int argc, char **argv)
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* start telnet, after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_upf_ctx, &g_upf, vty_get_bind_addr(), OSMO_VTY_PORT_UPF);
 | 
			
		||||
	/* start telnet VTY */
 | 
			
		||||
	rc = telnet_init_default(tall_upf_ctx, &g_upf, OSMO_VTY_PORT_UPF);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return 2;
 | 
			
		||||
 | 
			
		||||
	/* start control interface, after reading config for ctrl_vty_get_bind_addr() */
 | 
			
		||||
	g_upf->ctrl = ctrl_interface_setup_dynip(g_upf, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_UPF, NULL);
 | 
			
		||||
	g_upf->ctrl = ctrl_interface_setup(g_upf, OSMO_CTRL_PORT_UPF, NULL);
 | 
			
		||||
	if (!g_upf->ctrl) {
 | 
			
		||||
		fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -331,9 +331,6 @@ int main(int argc, char **argv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (upf_gtp_genl_open())
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (upf_gtp_devs_open())
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ static void up_endpoint_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_p
 | 
			
		||||
		if (!m->ctx.peer_fi && req->ctx.peer_fi)
 | 
			
		||||
			up_peer_set_msg_ctx(req->ctx.peer_fi->priv, m);
 | 
			
		||||
		if (!m->ctx.session_fi && req->ctx.session_fi)
 | 
			
		||||
			up_session_set_msg_ctx(req->ctx.peer_fi->priv, m);
 | 
			
		||||
			up_session_set_msg_ctx(req->ctx.session_fi->priv, m);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* From the remote address, find the matching peer instance */
 | 
			
		||||
@@ -221,19 +221,25 @@ static void up_endpoint_rx_cb(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_ms
 | 
			
		||||
	case OSMO_PFCP_MSGT_SESSION_REP_REQ:
 | 
			
		||||
		up_ep_rx_session_rep_req(up_ep, m);
 | 
			
		||||
		return;
 | 
			
		||||
	case OSMO_PFCP_MSGT_HEARTBEAT_REQ:
 | 
			
		||||
	case OSMO_PFCP_MSGT_HEARTBEAT_RESP:
 | 
			
		||||
		/* Heartbeat is already handled in osmo_pfcp_endpoint_handle_rx() in pfcp_endpoint.c. The heartbeat
 | 
			
		||||
		 * messages are also dispatched here, to the rx_cb, "on informtional basis", nothing needs to happen
 | 
			
		||||
		 * here. */
 | 
			
		||||
		return;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Unknown message type\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	INIT_LLIST_HEAD(&up_ep->peers);
 | 
			
		||||
	hash_init(up_ep->sessions_by_up_seid);
 | 
			
		||||
 | 
			
		||||
	cfg = (struct osmo_pfcp_endpoint_cfg){
 | 
			
		||||
		.local_addr = *local_addr,
 | 
			
		||||
@@ -246,41 +252,31 @@ 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;
 | 
			
		||||
		llist_for_each_entry(peer, &ep->peers, entry) {
 | 
			
		||||
			struct up_session *session = up_session_find_by_up_seid(peer, up_seid);
 | 
			
		||||
			if (session)
 | 
			
		||||
		struct up_session *session;
 | 
			
		||||
		hash_for_each_possible(ep->sessions_by_up_seid, session, ep_node_by_up_seid, up_seid) {
 | 
			
		||||
			if (session->up_seid == up_seid)
 | 
			
		||||
				return session;
 | 
			
		||||
		}
 | 
			
		||||
		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;
 | 
			
		||||
@@ -288,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -47,27 +47,27 @@ int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action
 | 
			
		||||
		return cmp;
 | 
			
		||||
 | 
			
		||||
	switch (a->kind) {
 | 
			
		||||
	case UP_GTP_U_ENDECAPS:
 | 
			
		||||
		if ((cmp = CMP_MEMB(endecaps.local_teid)))
 | 
			
		||||
	case UP_GTP_U_TUNEND:
 | 
			
		||||
		if ((cmp = CMP_MEMB(tunend.access.local.teid)))
 | 
			
		||||
			return cmp;
 | 
			
		||||
		if ((cmp = CMP_MEMB(endecaps.remote_teid)))
 | 
			
		||||
		if ((cmp = CMP_MEMB(tunend.access.remote.teid)))
 | 
			
		||||
			return cmp;
 | 
			
		||||
		cmp = osmo_sockaddr_cmp(&a->endecaps.gtp_remote_addr, &b->endecaps.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->endecaps.ue_addr, &b->endecaps.ue_addr);
 | 
			
		||||
		cmp = osmo_sockaddr_cmp(&a->tunend.core.ue_local_addr, &b->tunend.core.ue_local_addr);
 | 
			
		||||
		if (cmp)
 | 
			
		||||
			return cmp;
 | 
			
		||||
		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:
 | 
			
		||||
@@ -79,70 +79,61 @@ int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action
 | 
			
		||||
static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_dev *gtp_dev;
 | 
			
		||||
	const struct osmo_sockaddr *gtp_addr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	switch (a->kind) {
 | 
			
		||||
	case UP_GTP_U_ENDECAPS:
 | 
			
		||||
		if (g_upf->gtp.mockup) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "gtp/mockup active, skipping GTP action %s\n",
 | 
			
		||||
	case UP_GTP_U_TUNEND:
 | 
			
		||||
		if (g_upf->tunend.mockup) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_INFO, "tunend/mockup active, skipping GTP action %s\n",
 | 
			
		||||
					  enable ? "enable" : "disable");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* use the first available GTP device.
 | 
			
		||||
		 * TODO: select by interface name?
 | 
			
		||||
		 */
 | 
			
		||||
		gtp_dev = upf_gtp_dev_first();
 | 
			
		||||
		/* 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.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, cannot %s\n", enable ? "enable" : "disable");
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR, "No GTP device open for local address %s, cannot %s"
 | 
			
		||||
					  " -- consider configuring 'tunend' / 'dev (create|use) foo %s'\n",
 | 
			
		||||
					  osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr),
 | 
			
		||||
					  enable ? "enable" : "disable",
 | 
			
		||||
					  osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr));
 | 
			
		||||
			return -EIO;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (enable)
 | 
			
		||||
			rc = upf_gtp_dev_tunnel_add(gtp_dev, &a->endecaps);
 | 
			
		||||
			rc = upf_gtp_dev_tunend_add(gtp_dev, &a->tunend);
 | 
			
		||||
		else
 | 
			
		||||
			rc = upf_gtp_dev_tunnel_del(gtp_dev, &a->endecaps);
 | 
			
		||||
			rc = upf_gtp_dev_tunend_del(gtp_dev, &a->tunend);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s GTP tunnel: %d %s\n",
 | 
			
		||||
					  enable ? "enable" : "disable", rc, strerror(-rc));
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s GTP tunnel (rc=%d)\n",
 | 
			
		||||
					  enable ? "enable" : "disable", rc);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
		LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s GTP tunnel\n", enable ? "Enabled" : "Disabled");
 | 
			
		||||
		LOG_UP_GTP_ACTION(a, LOGL_INFO, "%s tunend on dev %s\n", enable ? "Enabled" : "Disabled",
 | 
			
		||||
				  gtp_dev->name);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case UP_GTP_U_TUNMAP:
 | 
			
		||||
		if (g_upf->nft.mockup) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "nft/mockup active, skipping nftables ruleset %s\n",
 | 
			
		||||
		if (g_upf->tunmap.mockup) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_INFO, "tunmap/mockup active, skipping nftables ruleset %s\n",
 | 
			
		||||
					  enable ? "enable" : "disable");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (enable && a->tunmap.id != 0) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR,
 | 
			
		||||
					  "Cannot enable: nft GTP tunnel mapping rule has been enabled before"
 | 
			
		||||
					  " as " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u\n", a->tunmap.id);
 | 
			
		||||
			return -EALREADY;
 | 
			
		||||
		}
 | 
			
		||||
		if (!enable && a->tunmap.id == 0) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR,
 | 
			
		||||
					  "Cannot disable: nft GTP tunnel mapping rule has not been enabled"
 | 
			
		||||
					  " (no " NFT_CHAIN_NAME_PREFIX_TUNMAP " id)\n");
 | 
			
		||||
			return -ENOENT;
 | 
			
		||||
		}
 | 
			
		||||
		if (enable)
 | 
			
		||||
			rc = upf_nft_tunmap_create(&a->tunmap);
 | 
			
		||||
		else
 | 
			
		||||
			rc = upf_nft_tunmap_delete(&a->tunmap);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR,
 | 
			
		||||
					  "Failed to %s nft GTP tunnel mapping " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u:"
 | 
			
		||||
					  " %d %s\n", enable ? "enable" : "disable", a->tunmap.id, rc, strerror(-rc));
 | 
			
		||||
			LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s nft GTP tunnel mapping (rc=%d)\n",
 | 
			
		||||
					  enable ? "enable" : "disable", rc);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
		LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s nft GTP tunnel mapping " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u\n",
 | 
			
		||||
				  enable ? "Enabled" : "Disabled", a->tunmap.id);
 | 
			
		||||
		if (!enable)
 | 
			
		||||
			a->tunmap.id = 0;
 | 
			
		||||
		LOG_UP_GTP_ACTION(a, LOGL_INFO, "%s tunmap, nft chain IDs: access--%u-> <-%u--core\n",
 | 
			
		||||
				  enable ? "Enabled" : "Disabled",
 | 
			
		||||
				  a->tunmap.access.chain_id, a->tunmap.core.chain_id);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
@@ -165,21 +156,28 @@ int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_actio
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	switch (a->kind) {
 | 
			
		||||
	case UP_GTP_U_ENDECAPS:
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "GTP:endecaps GTP-access:");
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->endecaps.gtp_remote_addr);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " TEID-r:0x%"PRIx32" TEID-l:0x%"PRIx32" IP-core:",
 | 
			
		||||
				   a->endecaps.remote_teid, a->endecaps.local_teid);
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->endecaps.ue_addr);
 | 
			
		||||
	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.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.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:");
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.gtp_remote_addr);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32" TEID-access-l:0x%"PRIx32" GTP-core:",
 | 
			
		||||
				   a->tunmap.access.remote_teid, a->tunmap.access.local_teid);
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.gtp_remote_addr);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " TEID-core-r:0x%"PRIx32" TEID-core-l:0x%"PRIx32,
 | 
			
		||||
				   a->tunmap.core.remote_teid, a->tunmap.core.local_teid);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "GTP:tunmap GTP-access-r:");
 | 
			
		||||
		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.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.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.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");
 | 
			
		||||
@@ -189,9 +187,10 @@ int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_actio
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (a->session)
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " PFCP-peer:%s SEID-l:0x%"PRIx64" PDR:%d,%d",
 | 
			
		||||
				   up_peer_remote_addr_str(a->session->up_peer),
 | 
			
		||||
				   a->session->up_seid, a->pdr_core, a->pdr_access);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " PFCP-peer:%s SEID-l:0x%"PRIx64,
 | 
			
		||||
				   up_peer_remote_addr_str(a->session->up_peer), a->session->up_seid);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " PDR-access:%d", a->pdr_access);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " PDR-core:%d", a->pdr_core);
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -199,3 +198,33 @@ char *up_gtp_action_to_str_c(void *ctx, const struct up_gtp_action *a)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 128, "ERROR", up_gtp_action_to_str_buf, a)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct up_gtp_action *up_gtp_action_alloc(void *ctx, struct up_session *session, enum up_gtp_action_kind kind, struct llist_head *dst)
 | 
			
		||||
{
 | 
			
		||||
	struct up_gtp_action *a = talloc_zero(ctx, struct up_gtp_action);
 | 
			
		||||
	OSMO_ASSERT(a);
 | 
			
		||||
	a->session = session;
 | 
			
		||||
	a->kind = kind;
 | 
			
		||||
 | 
			
		||||
	if (kind == UP_GTP_U_TUNMAP) {
 | 
			
		||||
		INIT_HLIST_NODE(&a->tunmap.access.node_by_chain_id);
 | 
			
		||||
		INIT_HLIST_NODE(&a->tunmap.core.node_by_chain_id);
 | 
			
		||||
	}
 | 
			
		||||
	llist_add_tail(&a->entry, dst);
 | 
			
		||||
	return a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void up_gtp_action_free(struct up_gtp_action *a)
 | 
			
		||||
{
 | 
			
		||||
	if (!a)
 | 
			
		||||
		return;
 | 
			
		||||
	up_gtp_action_disable(a);
 | 
			
		||||
	llist_del(&a->entry);
 | 
			
		||||
	if (a->kind == UP_GTP_U_TUNMAP) {
 | 
			
		||||
		if (!hlist_unhashed(&a->tunmap.access.node_by_chain_id))
 | 
			
		||||
			hash_del(&a->tunmap.access.node_by_chain_id);
 | 
			
		||||
		if (!hlist_unhashed(&a->tunmap.core.node_by_chain_id))
 | 
			
		||||
			hash_del(&a->tunmap.core.node_by_chain_id);
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(a);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -169,11 +169,6 @@ struct up_peer *up_peer_find_or_add(struct up_endpoint *up_endpoint, const struc
 | 
			
		||||
	return up_peer_add(up_endpoint, remote_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int up_peer_tx(struct up_peer *peer, struct osmo_pfcp_msg *m)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int up_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	//struct up_peer *peer = fi->priv;
 | 
			
		||||
@@ -188,7 +183,7 @@ void up_peer_set_msg_ctx(struct up_peer *peer, struct osmo_pfcp_msg *m)
 | 
			
		||||
	m->ctx.peer_fi = peer->fi;
 | 
			
		||||
	m->ctx.peer_use_count = &peer->use_count;
 | 
			
		||||
	m->ctx.peer_use_token = (m->rx ? UP_USE_MSG_RX : UP_USE_MSG_TX);
 | 
			
		||||
	osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, 1);
 | 
			
		||||
	OSMO_ASSERT(osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, 1) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_pfcp_msg *up_peer_init_tx(struct up_peer *peer, struct osmo_pfcp_msg *in_reply_to,
 | 
			
		||||
@@ -217,7 +212,8 @@ static int up_peer_tx_assoc_setup_resp(struct up_peer *peer, struct osmo_pfcp_ms
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
 | 
			
		||||
		OSMO_LOG_PFCP_MSG(resp, LOGL_ERROR, "Error sending response, cannot associate with peer\n");
 | 
			
		||||
		OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message,"
 | 
			
		||||
				  " cannot associate with peer\n");
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -234,7 +230,7 @@ static int up_peer_tx_assoc_rel_resp(struct up_peer *peer, struct osmo_pfcp_msg
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
 | 
			
		||||
		OSMO_LOG_PFCP_MSG(resp, LOGL_ERROR, "Error sending response\n");
 | 
			
		||||
		OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message\n");
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -303,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;
 | 
			
		||||
@@ -326,6 +322,8 @@ nack_response:
 | 
			
		||||
		.cause = cause,
 | 
			
		||||
	};
 | 
			
		||||
	osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp);
 | 
			
		||||
	if (session)
 | 
			
		||||
		up_session_discard(session);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void up_peer_not_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@
 | 
			
		||||
#include <osmocom/upf/up_peer.h>
 | 
			
		||||
#include <osmocom/upf/up_session.h>
 | 
			
		||||
#include <osmocom/upf/up_gtp_action.h>
 | 
			
		||||
#include <osmocom/upf/netinst.h>
 | 
			
		||||
 | 
			
		||||
static enum osmo_pfcp_cause up_session_setup_gtp(struct up_session *session);
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +48,7 @@ void up_session_set_msg_ctx(struct up_session *session, struct osmo_pfcp_msg *m)
 | 
			
		||||
	m->ctx.session_fi = session->fi;
 | 
			
		||||
	m->ctx.session_use_count = &session->use_count;
 | 
			
		||||
	m->ctx.session_use_token = (m->rx ? UP_USE_MSG_RX : UP_USE_MSG_TX);
 | 
			
		||||
	osmo_use_count_get_put(m->ctx.session_use_count, m->ctx.session_use_token, 1);
 | 
			
		||||
	OSMO_ASSERT(osmo_use_count_get_put(m->ctx.session_use_count, m->ctx.session_use_token, 1) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum up_session_fsm_state {
 | 
			
		||||
@@ -114,47 +115,116 @@ struct chosen_f_teid *chosen_f_teid_find(struct llist_head *list, uint8_t choose
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find local interface's IP address by Network Instance name. Return 0 on success, or an OSMO_PFCP_CAUSE_* value on
 | 
			
		||||
 * failure. */
 | 
			
		||||
static int up_session_choose_local_ip(struct up_session *session, struct osmo_pfcp_ip_addrs *local_addr,
 | 
			
		||||
				      const char *netinst_name)
 | 
			
		||||
{
 | 
			
		||||
	const struct network_instance *netinst;
 | 
			
		||||
	struct osmo_sockaddr osa = {};
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&g_upf->netinst)) {
 | 
			
		||||
		/* No network instances are configured in osmo-upf.cfg. Instead use the local address configured for
 | 
			
		||||
		 * PFCP, assuming that in a simplistic setup the host has only one interface. It is unlikely to be
 | 
			
		||||
		 * useful for a production environment where the entire point is to hand packet data from one interface
 | 
			
		||||
		 * to another, and where PFCP most probably happens on an entirely different interface, but may make
 | 
			
		||||
		 * things simpler for lab testing. */
 | 
			
		||||
		if (osmo_pfcp_ip_addrs_set(local_addr,
 | 
			
		||||
					   osmo_pfcp_endpoint_get_local_addr(session->up_peer->up_endpoint->pfcp_ep))) {
 | 
			
		||||
			LOGPFSML(session->fi, LOGL_ERROR, "Invalid local address in pfcp_endpoint cfg\n");
 | 
			
		||||
			return OSMO_PFCP_CAUSE_SYSTEM_FAILURE;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPFSML(session->fi, LOGL_NOTICE,
 | 
			
		||||
			 "Cannot look up Network Instance %s: No 'netinst' is configured, setting up GTP on same local"
 | 
			
		||||
			 " interface as PFCP: %s (makes sense only for lab testing)\n",
 | 
			
		||||
			 osmo_quote_str_c(OTC_SELECT, netinst_name, -1),
 | 
			
		||||
			 osmo_pfcp_ip_addrs_to_str_c(OTC_SELECT, local_addr));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!netinst_name || !*netinst_name) {
 | 
			
		||||
		/* Empty or no Network Instance IE in incoming PFCP request. Pick the first network instance; makes
 | 
			
		||||
		 * sense only in a simplistic lab setup where packet data is forwarded to the same interface that it is
 | 
			
		||||
		 * received on, and where no Network Instance is indicated by the CPF. Warn if more than one network
 | 
			
		||||
		 * instance is configured to choose from. */
 | 
			
		||||
		if (llist_count(&g_upf->netinst) > 1)
 | 
			
		||||
			LOGPFSML(session->fi, LOGL_NOTICE,
 | 
			
		||||
				 "Missing Network Instance in incoming request, using the first 'netinst' from cfg\n");
 | 
			
		||||
		netinst = netinst_first(&g_upf->netinst);
 | 
			
		||||
		/* there has to be a first entry, because we handled the empty list above. */
 | 
			
		||||
		OSMO_ASSERT(netinst);
 | 
			
		||||
	} else {
 | 
			
		||||
		netinst = netinst_find(&g_upf->netinst, netinst_name);
 | 
			
		||||
		if (!netinst) {
 | 
			
		||||
			LOGPFSML(session->fi, LOGL_ERROR, "Network Instance from PFCP request not found: %s"
 | 
			
		||||
				 " -- ensure there is a 'netinst' / 'add %s <ip-addr>' entry in your config\n",
 | 
			
		||||
				 osmo_quote_str_c(OTC_SELECT, netinst_name, -1),
 | 
			
		||||
				 osmo_escape_str_c(OTC_SELECT, netinst_name, -1));
 | 
			
		||||
			return OSMO_PFCP_CAUSE_RULE_CREATION_MOD_FAILURE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Convert netinst IP address string first to osmo_sockaddr and then to osmo_pfcp_ip_addrs. */
 | 
			
		||||
	if (osmo_sockaddr_str_to_sockaddr(&netinst->addr, &osa.u.sas)
 | 
			
		||||
	    || osmo_pfcp_ip_addrs_set(local_addr, &osa)) {
 | 
			
		||||
		LOGPFSML(session->fi, LOGL_ERROR,
 | 
			
		||||
			 "Network Instance %s from PFCP request yields no valid IP address: "
 | 
			
		||||
				OSMO_SOCKADDR_STR_FMT "\n",
 | 
			
		||||
			 osmo_quote_str_c(OTC_SELECT, netinst_name, -1),
 | 
			
		||||
			 OSMO_SOCKADDR_STR_FMT_ARGS(&netinst->addr));
 | 
			
		||||
		return OSMO_PFCP_CAUSE_RULE_CREATION_MOD_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Choose an F-TEID (when the peer has sent CHOOSE = 1).
 | 
			
		||||
 * If the peer also sent a CHOOSE_ID, then remember this F-TEID choice under the given ID, and re-use that choice when
 | 
			
		||||
 * the same ID re-appears. The chosen IDs are saved in session->chosen_f_teids. */
 | 
			
		||||
 * the same ID re-appears. The chosen IDs are saved in session->chosen_f_teids.
 | 
			
		||||
 * Return 0 on success, or an OSMO_PFCP_CAUSE_* value on failure. */
 | 
			
		||||
static enum osmo_pfcp_cause up_session_choose_f_teid(struct up_session *session, struct osmo_pfcp_ie_f_teid *dst,
 | 
			
		||||
						     bool choose_id_present, uint8_t choose_id)
 | 
			
		||||
						     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;
 | 
			
		||||
	struct chosen_f_teid *chosen;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (choose_id_present)
 | 
			
		||||
	if (choose_id_present) {
 | 
			
		||||
		chosen = chosen_f_teid_find(&session->chosen_f_teids, choose_id);
 | 
			
		||||
	if (chosen) {
 | 
			
		||||
		/* Re-use a previous F-TEID */
 | 
			
		||||
		*dst = chosen->f_teid;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Choose a new F-TEID */
 | 
			
		||||
		*dst = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
			.fixed = {
 | 
			
		||||
				.teid = up_endpoint_next_teid(up_ep),
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		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;
 | 
			
		||||
		if (chosen) {
 | 
			
		||||
			/* Re-use a previous F-TEID */
 | 
			
		||||
			*dst = chosen->f_teid;
 | 
			
		||||
			return OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPFSML(session->fi, LOGL_INFO, "Allocated new local TEID 0x%x\n", dst->fixed.teid);
 | 
			
		||||
		/* No previous F-TEID found, allocate a new one below */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		if (osmo_pfcp_ip_addrs_set(&dst->fixed.ip_addr,
 | 
			
		||||
					   osmo_pfcp_endpoint_get_local_addr(up_ep->pfcp_ep))) {
 | 
			
		||||
			LOGPFSML(session->fi, LOGL_ERROR, "Invalid local address in pfcp_endpoint cfg\n");
 | 
			
		||||
			return OSMO_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION;
 | 
			
		||||
		}
 | 
			
		||||
		/* Save this choice */
 | 
			
		||||
		if (choose_id_present) {
 | 
			
		||||
			chosen = talloc(session, struct chosen_f_teid);
 | 
			
		||||
			*chosen = (struct chosen_f_teid){
 | 
			
		||||
				.f_teid = *dst,
 | 
			
		||||
				.choose_id = choose_id,
 | 
			
		||||
			};
 | 
			
		||||
			llist_add_tail(&chosen->entry, &session->chosen_f_teids);
 | 
			
		||||
		}
 | 
			
		||||
	*dst = (struct osmo_pfcp_ie_f_teid){
 | 
			
		||||
		.choose_flag = false,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* Determine local IP address from Network Instance value received in PFCP request */
 | 
			
		||||
	rc = up_session_choose_local_ip(session, &dst->fixed.ip_addr, netinst_name);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* Choose a new TEID */
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(session->fi, LOGL_INFO, "Allocated new local F-TEID %s\n",
 | 
			
		||||
		 osmo_pfcp_ie_f_teid_to_str_c(OTC_SELECT, dst));
 | 
			
		||||
 | 
			
		||||
	/* Save this choice */
 | 
			
		||||
	if (choose_id_present) {
 | 
			
		||||
		chosen = talloc(session, struct chosen_f_teid);
 | 
			
		||||
		*chosen = (struct chosen_f_teid){
 | 
			
		||||
			.f_teid = *dst,
 | 
			
		||||
			.choose_id = choose_id,
 | 
			
		||||
		};
 | 
			
		||||
		llist_add_tail(&chosen->entry, &session->chosen_f_teids);
 | 
			
		||||
	}
 | 
			
		||||
	return OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
 | 
			
		||||
}
 | 
			
		||||
@@ -189,22 +259,23 @@ static void far_upd(struct far *far, const struct osmo_pfcp_ie_upd_far *upd)
 | 
			
		||||
	if (upd->upd_forw_params_present) {
 | 
			
		||||
		const struct osmo_pfcp_ie_upd_forw_params *u = &upd->upd_forw_params;
 | 
			
		||||
		struct osmo_pfcp_ie_forw_params *p = &far->desc.forw_params;
 | 
			
		||||
		far->desc.forw_params_present = true;
 | 
			
		||||
		if (u->destination_iface_present)
 | 
			
		||||
			p->destination_iface = u->destination_iface;
 | 
			
		||||
		if (u->network_inst_present) {
 | 
			
		||||
			p->network_inst = p->network_inst;
 | 
			
		||||
			p->network_inst = u->network_inst;
 | 
			
		||||
			p->network_inst_present = true;
 | 
			
		||||
		}
 | 
			
		||||
		if (u->outer_header_creation_present) {
 | 
			
		||||
			p->outer_header_creation = p->outer_header_creation;
 | 
			
		||||
			p->outer_header_creation = u->outer_header_creation;
 | 
			
		||||
			p->outer_header_creation_present = true;
 | 
			
		||||
		}
 | 
			
		||||
		if (u->linked_te_id_present) {
 | 
			
		||||
			p->linked_te_id = p->linked_te_id;
 | 
			
		||||
			p->linked_te_id = u->linked_te_id;
 | 
			
		||||
			p->linked_te_id_present = true;
 | 
			
		||||
		}
 | 
			
		||||
		if (u->destination_iface_type_present) {
 | 
			
		||||
			p->destination_iface_type = p->destination_iface_type;
 | 
			
		||||
			p->destination_iface_type = u->destination_iface_type;
 | 
			
		||||
			p->destination_iface_type_present = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -237,12 +308,9 @@ static int far_to_str_buf(char *buf, size_t len, const struct far *far)
 | 
			
		||||
	if (f->forw_params_present) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " dst:%s", osmo_pfcp_dest_iface_str(f->forw_params.destination_iface));
 | 
			
		||||
		if (f->forw_params.outer_header_creation_present) {
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " encaps-");
 | 
			
		||||
			OSMO_STRBUF_APPEND(sb, osmo_pfcp_bits_to_str_buf,
 | 
			
		||||
					   f->forw_params.outer_header_creation.desc_bits,
 | 
			
		||||
					   osmo_pfcp_outer_header_creation_strs);
 | 
			
		||||
			if (f->forw_params.outer_header_creation.teid_present)
 | 
			
		||||
				OSMO_STRBUF_PRINTF(sb, " TEID-0x%x", f->forw_params.outer_header_creation.teid);
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, ",");
 | 
			
		||||
			OSMO_STRBUF_APPEND(sb, osmo_pfcp_ie_outer_header_creation_to_str_buf,
 | 
			
		||||
					   &f->forw_params.outer_header_creation);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "}");
 | 
			
		||||
@@ -273,6 +341,10 @@ int pdr_to_str_buf(char *buf, size_t buflen, const struct pdr *pdr)
 | 
			
		||||
			OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &pdr->desc.pdi.ue_ip_address.ip_addr.v6);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (pdr->desc.pdi.network_inst_present) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " netinst:");
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_quote_str_buf3, pdr->desc.pdi.network_inst.str, -1);
 | 
			
		||||
	}
 | 
			
		||||
	if (pdr->local_f_teid) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " ");
 | 
			
		||||
		OSMO_STRBUF_APPEND(sb, osmo_pfcp_ie_f_teid_to_str_buf, pdr->local_f_teid);
 | 
			
		||||
@@ -307,6 +379,8 @@ static struct pdr *pdr_find(struct up_session *session, uint16_t pdr_id)
 | 
			
		||||
 | 
			
		||||
static void pdr_del(struct pdr *pdr)
 | 
			
		||||
{
 | 
			
		||||
	if (!hlist_unhashed(&pdr->node_by_local_f_teid))
 | 
			
		||||
		hash_del(&pdr->node_by_local_f_teid);
 | 
			
		||||
	llist_del(&pdr->entry);
 | 
			
		||||
	talloc_free(pdr);
 | 
			
		||||
}
 | 
			
		||||
@@ -316,6 +390,7 @@ static void pdr_set_far(struct pdr *pdr, struct far *far)
 | 
			
		||||
	pdr->far = far;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set up a new Packet Detection Rule, append the response to the end of the created_pdr/created_pdr_count array. */
 | 
			
		||||
static struct pdr *pdr_create(struct up_session *session,
 | 
			
		||||
			      const struct osmo_pfcp_ie_create_pdr *create_pdr,
 | 
			
		||||
			      enum osmo_pfcp_cause *cause,
 | 
			
		||||
@@ -338,6 +413,7 @@ static struct pdr *pdr_create(struct up_session *session,
 | 
			
		||||
		.session = session,
 | 
			
		||||
		.desc = *create_pdr,
 | 
			
		||||
	};
 | 
			
		||||
	INIT_HLIST_NODE(&pdr->node_by_local_f_teid);
 | 
			
		||||
	llist_add_tail(&pdr->entry, &session->pdrs);
 | 
			
		||||
 | 
			
		||||
	if (pdr->desc.far_id_present) {
 | 
			
		||||
@@ -372,9 +448,13 @@ static struct pdr *pdr_create(struct up_session *session,
 | 
			
		||||
		if (pdr->desc.pdi.local_f_teid.choose_flag) {
 | 
			
		||||
			/* CHOOSE = 1: we need to pick our own local F-TEID */
 | 
			
		||||
			struct osmo_pfcp_ie_f_teid local_f_teid;
 | 
			
		||||
			const char *netinst_name = NULL;
 | 
			
		||||
			if (pdr->desc.pdi.network_inst_present)
 | 
			
		||||
				netinst_name = pdr->desc.pdi.network_inst.str;
 | 
			
		||||
			*cause = up_session_choose_f_teid(session, &local_f_teid,
 | 
			
		||||
							  pdr->desc.pdi.local_f_teid.choose.choose_id_present,
 | 
			
		||||
							  pdr->desc.pdi.local_f_teid.choose.choose_id);
 | 
			
		||||
							  pdr->desc.pdi.local_f_teid.choose.choose_id,
 | 
			
		||||
							  netinst_name);
 | 
			
		||||
			if (*cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) {
 | 
			
		||||
				*offending_ie = OSMO_PFCP_IEI_F_TEID;
 | 
			
		||||
				*offending_ie_present = true;
 | 
			
		||||
@@ -405,6 +485,7 @@ static struct pdr *pdr_create(struct up_session *session,
 | 
			
		||||
			.local_f_teid_present = true,
 | 
			
		||||
			.local_f_teid = *pdr->local_f_teid,
 | 
			
		||||
		};
 | 
			
		||||
		hash_add(g_upf->gtp.pdrs_by_local_f_teid, &pdr->node_by_local_f_teid, pdr->local_f_teid->fixed.teid);
 | 
			
		||||
	} else {
 | 
			
		||||
		created_pdr[*created_pdr_count] = (struct osmo_pfcp_ie_created_pdr){
 | 
			
		||||
			.pdr_id = pdr->desc.pdr_id,
 | 
			
		||||
@@ -482,8 +563,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;
 | 
			
		||||
@@ -556,14 +636,20 @@ static void up_session_est(struct up_session *session, struct osmo_pfcp_msg *m)
 | 
			
		||||
	resp->up_f_seid_present = true;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
 | 
			
		||||
	if (rc)
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		/* sending ACK failed, discard session. It might seem like a good idea to keep the session around,
 | 
			
		||||
		 * because the creation succeeded, only the ACK failed. But in the greater scheme of things, if we
 | 
			
		||||
		 * cannot ACK to the PFCP peer, all is lost. Rather not keep stale sessions around. */
 | 
			
		||||
		up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	up_session_fsm_state_chg(UP_SESSION_ST_ESTABLISHED);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
nack_response:
 | 
			
		||||
	resp->created_pdr_count = 0;
 | 
			
		||||
	osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
 | 
			
		||||
	/* No matter if sending the NACK succeeded or not, discard the session. */
 | 
			
		||||
	up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -644,15 +730,21 @@ static void up_session_mod(struct up_session *session, struct osmo_pfcp_msg *m)
 | 
			
		||||
		goto nack_response;
 | 
			
		||||
 | 
			
		||||
	/* Success, send ACK */
 | 
			
		||||
	if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx))
 | 
			
		||||
	if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx)) {
 | 
			
		||||
		/* sending ACK failed, discard session. It might seem like a good idea to keep the session around,
 | 
			
		||||
		 * because the modification succeeded, only the ACK failed. But in the greater scheme of things, if we
 | 
			
		||||
		 * cannot ACK to the PFCP peer, all is lost. Rather not keep stale sessions around. */
 | 
			
		||||
		up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(fi, LOGL_NOTICE, "Session modified: %s\n", up_session_gtp_status(session));
 | 
			
		||||
	LOGPFSML(fi, LOGL_INFO, "Session modified: %s\n", up_session_gtp_status(session));
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
nack_response:
 | 
			
		||||
	resp->created_pdr_count = 0;
 | 
			
		||||
	osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
 | 
			
		||||
	/* No matter if sending the NACK succeeded or not, discard the session. */
 | 
			
		||||
	up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -667,6 +759,7 @@ static void up_session_del(struct up_session *session, struct osmo_pfcp_msg *m)
 | 
			
		||||
		.cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED
 | 
			
		||||
	};
 | 
			
		||||
	osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
 | 
			
		||||
	/* No matter if sending the deletion ACK succeeded or not, discard the session. */
 | 
			
		||||
	up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -696,7 +789,7 @@ static void up_session_init_action(struct osmo_fsm_inst *fi, uint32_t event, voi
 | 
			
		||||
static void up_session_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct up_session *session = fi->priv;
 | 
			
		||||
	LOGPFSML(fi, LOGL_NOTICE, "Session established: %s %s\n", up_session_to_str_c(OTC_SELECT, session),
 | 
			
		||||
	LOGPFSML(fi, LOGL_INFO, "Session established: %s %s\n", up_session_to_str_c(OTC_SELECT, session),
 | 
			
		||||
		 up_session_gtp_status(session));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -727,13 +820,11 @@ static void up_session_established_onleave(struct osmo_fsm_inst *fi, uint32_t ne
 | 
			
		||||
{
 | 
			
		||||
	struct up_session *session = fi->priv;
 | 
			
		||||
	struct up_gtp_action *a;
 | 
			
		||||
	LOGPFSML(fi, LOGL_NOTICE, "Session releasing: %s %s\n", up_session_to_str_c(OTC_SELECT, session), up_session_gtp_status(session));
 | 
			
		||||
	LOGPFSML(fi, LOGL_INFO, "Session releasing: %s %s\n", up_session_to_str_c(OTC_SELECT, session), up_session_gtp_status(session));
 | 
			
		||||
 | 
			
		||||
	/* Shut down all active GTP rules */
 | 
			
		||||
	while ((a = llist_first_entry_or_null(&session->active_gtp_actions, struct up_gtp_action, entry))) {
 | 
			
		||||
		up_gtp_action_disable(a);
 | 
			
		||||
		llist_del(&a->entry);
 | 
			
		||||
		talloc_free(a);
 | 
			
		||||
		up_gtp_action_free(a);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -794,6 +885,7 @@ static void up_session_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_
 | 
			
		||||
 | 
			
		||||
	up_session_clear_pdr_far(session);
 | 
			
		||||
 | 
			
		||||
	hash_del(&session->ep_node_by_up_seid);
 | 
			
		||||
	hash_del(&session->node_by_up_seid);
 | 
			
		||||
	hash_del(&session->node_by_cp_seid);
 | 
			
		||||
}
 | 
			
		||||
@@ -899,7 +991,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;
 | 
			
		||||
@@ -931,19 +1023,15 @@ static struct up_session *up_session_add(struct up_peer *peer, const struct osmo
 | 
			
		||||
 | 
			
		||||
	hash_add(peer->sessions_by_up_seid, &session->node_by_up_seid, session->up_seid);
 | 
			
		||||
	hash_add(peer->sessions_by_cp_seid, &session->node_by_cp_seid, session->cp_f_seid.seid);
 | 
			
		||||
	hash_add(peer->up_endpoint->sessions_by_up_seid, &session->ep_node_by_up_seid, session->up_seid);
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
@@ -970,22 +1058,6 @@ struct up_session *up_session_find_by_cp_f_seid(struct up_peer *peer, const stru
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct up_session *up_session_find_by_local_teid(struct up_peer *peer, uint32_t teid)
 | 
			
		||||
{
 | 
			
		||||
	struct up_session *session;
 | 
			
		||||
	int bkt;
 | 
			
		||||
	hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
 | 
			
		||||
		struct pdr *pdr;
 | 
			
		||||
		llist_for_each_entry(pdr, &session->pdrs, entry) {
 | 
			
		||||
			if (!pdr->local_f_teid)
 | 
			
		||||
				continue;
 | 
			
		||||
			if (pdr->local_f_teid->fixed.teid == teid)
 | 
			
		||||
				return session;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool action_is_forw(const struct osmo_pfcp_ie_apply_action *aa)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_pfcp_bits_get(aa->bits, OSMO_PFCP_APPLY_ACTION_FORW)
 | 
			
		||||
@@ -996,8 +1068,8 @@ static void pdr_classify(struct pdr *pdr)
 | 
			
		||||
{
 | 
			
		||||
	pdr->rx_decaps = false;
 | 
			
		||||
	pdr->forw_encaps = false;
 | 
			
		||||
	pdr->forw_to_core = false;
 | 
			
		||||
	pdr->forw_from_core = false;
 | 
			
		||||
	pdr->access_to_core = false;
 | 
			
		||||
	pdr->core_to_access = false;
 | 
			
		||||
	if (!pdr->far)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
@@ -1009,10 +1081,10 @@ static void pdr_classify(struct pdr *pdr)
 | 
			
		||||
	if (!action_is_forw(&pdr->far->desc.apply_action))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pdr->forw_to_core = (pdr->desc.pdi.source_iface == OSMO_PFCP_SOURCE_IFACE_ACCESS
 | 
			
		||||
			     && pdr->far->desc.forw_params.destination_iface == OSMO_PFCP_DEST_IFACE_CORE);
 | 
			
		||||
	pdr->access_to_core = (pdr->desc.pdi.source_iface == OSMO_PFCP_SOURCE_IFACE_ACCESS
 | 
			
		||||
			       && pdr->far->desc.forw_params.destination_iface == OSMO_PFCP_DEST_IFACE_CORE);
 | 
			
		||||
 | 
			
		||||
	pdr->forw_from_core = (pdr->desc.pdi.source_iface == OSMO_PFCP_SOURCE_IFACE_CORE
 | 
			
		||||
	pdr->core_to_access = (pdr->desc.pdi.source_iface == OSMO_PFCP_SOURCE_IFACE_CORE
 | 
			
		||||
			       && pdr->far->desc.forw_params.destination_iface == OSMO_PFCP_DEST_IFACE_ACCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1032,6 +1104,11 @@ void pdr_reverse_unset(struct pdr *pdr)
 | 
			
		||||
	pdr->reverse_pdr = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Log that a PDR (and its reverse-PDR) is inactive.
 | 
			
		||||
 * \param pdr  The Access-to-Core PDR.
 | 
			
		||||
 * \param desc  Why it is inactive.
 | 
			
		||||
 * \param pdr_to_str  The PDR that desc describes, can be pdr or the reverse Core-to-Access PDR.
 | 
			
		||||
 */
 | 
			
		||||
static void log_inactive_pdr_set(struct pdr *pdr, const char *desc, const struct pdr *pdr_to_str)
 | 
			
		||||
{
 | 
			
		||||
	struct pdr *rpdr = pdr->reverse_pdr;
 | 
			
		||||
@@ -1055,8 +1132,9 @@ static void log_inactive_pdr_set(struct pdr *pdr, const char *desc, const struct
 | 
			
		||||
 * The given PDR must have an outer-header-removal and a local F-TEID.
 | 
			
		||||
 * Its reverse-PDR must have a UE address flagged as "Destination" IP addr.
 | 
			
		||||
 * Its reverse-PDR's FAR must have an outer-header creation with a remote TEID.
 | 
			
		||||
 * \param pdr  A rule detecting packets on Access, where pdr->reverse_pdr detects packets on Core.
 | 
			
		||||
 */
 | 
			
		||||
static void add_gtp_action_endecaps(void *ctx, struct llist_head *dst, struct pdr *pdr)
 | 
			
		||||
static void add_gtp_action_tunend(void *ctx, struct llist_head *dst, struct pdr *pdr)
 | 
			
		||||
{
 | 
			
		||||
	struct up_session *session = pdr->session;
 | 
			
		||||
	struct up_gtp_action *a;
 | 
			
		||||
@@ -1067,18 +1145,20 @@ static void add_gtp_action_endecaps(void *ctx, struct llist_head *dst, struct pd
 | 
			
		||||
	OSMO_ASSERT(pdr->far);
 | 
			
		||||
	OSMO_ASSERT(pdr->reverse_pdr);
 | 
			
		||||
	OSMO_ASSERT(pdr->reverse_pdr->far);
 | 
			
		||||
 | 
			
		||||
	/* To decaps, we need to have a local TEID assigned for which to receive GTP packets. */
 | 
			
		||||
	if (!pdr->local_f_teid || pdr->local_f_teid->choose_flag) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local TEID", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* To encaps, we need to have a remote TEID assigned to send out in GTP packets, and we need to know where to
 | 
			
		||||
	 * send GTP to. */
 | 
			
		||||
	rpdr = pdr->reverse_pdr;
 | 
			
		||||
	rfar = rpdr->far;
 | 
			
		||||
	rfar_forw = &rfar->desc.forw_params;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(pdr->access_to_core);
 | 
			
		||||
	OSMO_ASSERT(rpdr->core_to_access);
 | 
			
		||||
 | 
			
		||||
	/* To decaps incoming on Access, we need to have a local F-TEID assigned for which to receive GTP packets. */
 | 
			
		||||
	if (!pdr->local_f_teid || pdr->local_f_teid->choose_flag) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local F-TEID", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* To encaps outgoing on Access, we need to have a remote F-TEID assigned to send out in GTP packets */
 | 
			
		||||
	if (!rfar->desc.forw_params_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Forwarding Parameters", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
@@ -1096,8 +1176,7 @@ static void add_gtp_action_endecaps(void *ctx, struct llist_head *dst, struct pd
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* To receive packets to be encapsulated, we need to know the assigned IP address for the UE, which receives the
 | 
			
		||||
	 * IP packets that should be placed into GTP. */
 | 
			
		||||
	/* To receive IP packets incoming on Core, we need to know the assigned IP address for the UE */
 | 
			
		||||
	if (!rpdr->desc.pdi.ue_ip_address_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing UE IP Address in PDI", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
@@ -1123,25 +1202,22 @@ static void add_gtp_action_endecaps(void *ctx, struct llist_head *dst, struct pd
 | 
			
		||||
	talloc_free(rpdr->inactive_reason);
 | 
			
		||||
	rpdr->inactive_reason = NULL;
 | 
			
		||||
 | 
			
		||||
	a = talloc(ctx, struct up_gtp_action);
 | 
			
		||||
	OSMO_ASSERT(a);
 | 
			
		||||
	*a = (struct up_gtp_action){
 | 
			
		||||
		.session = session,
 | 
			
		||||
		.pdr_core = pdr->desc.pdr_id,
 | 
			
		||||
		.pdr_access = rpdr->desc.pdr_id,
 | 
			
		||||
		.kind = UP_GTP_U_ENDECAPS,
 | 
			
		||||
		.endecaps = {
 | 
			
		||||
			.local_teid = pdr->local_f_teid->fixed.teid,
 | 
			
		||||
			.remote_teid = rfar_forw->outer_header_creation.teid,
 | 
			
		||||
			.gtp_remote_addr = rfar_forw->outer_header_creation.ip_addr.v4,
 | 
			
		||||
			.ue_addr = rpdr->desc.pdi.ue_ip_address.ip_addr.v4,
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	llist_add_tail(&a->entry, dst);
 | 
			
		||||
	a = up_gtp_action_alloc(ctx, session, UP_GTP_U_TUNEND, dst);
 | 
			
		||||
	a->pdr_access = pdr->desc.pdr_id;
 | 
			
		||||
	a->pdr_core = rpdr->desc.pdr_id;
 | 
			
		||||
	a->tunend.access.local.addr = pdr->local_f_teid->fixed.ip_addr.v4;
 | 
			
		||||
	a->tunend.access.local.teid = pdr->local_f_teid->fixed.teid;
 | 
			
		||||
	a->tunend.access.remote.addr = rfar_forw->outer_header_creation.ip_addr.v4;
 | 
			
		||||
	a->tunend.access.remote.teid = rfar_forw->outer_header_creation.teid;
 | 
			
		||||
	a->tunend.core.ue_local_addr = rpdr->desc.pdi.ue_ip_address.ip_addr.v4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void add_gtp_action_forw(void *ctx, struct llist_head *dst, struct pdr *pdr)
 | 
			
		||||
/* A GTP tunnel on Access side, mapping to another GTP tunnel on Core side and vice versa.
 | 
			
		||||
 * The PDR and its reverse PDR must both have an outer-header-removal and a local F-TEID.
 | 
			
		||||
 * Both FARs must have an outer-header creation with a remote F-TEID.
 | 
			
		||||
 * \param pdr  A rule detecting packets on Access, where pdr->reverse_pdr detects packets on Core.
 | 
			
		||||
 */
 | 
			
		||||
static void add_gtp_action_tunmap(void *ctx, struct llist_head *dst, struct pdr *pdr)
 | 
			
		||||
{
 | 
			
		||||
	struct up_session *session = pdr->session;
 | 
			
		||||
	struct up_gtp_action *a;
 | 
			
		||||
@@ -1161,52 +1237,53 @@ static void add_gtp_action_forw(void *ctx, struct llist_head *dst, struct pdr *p
 | 
			
		||||
	rfar = rpdr->far;
 | 
			
		||||
	rfar_forw = &rfar->desc.forw_params;
 | 
			
		||||
 | 
			
		||||
	/* To decaps, we need to have a local TEID assigned for which to receive GTP packets. */
 | 
			
		||||
	/* decaps from CORE */
 | 
			
		||||
	OSMO_ASSERT(pdr->access_to_core);
 | 
			
		||||
	OSMO_ASSERT(rpdr->core_to_access);
 | 
			
		||||
 | 
			
		||||
	/* To decaps incoming on Access, we need to have a local F-TEID assigned for which to receive GTP packets. */
 | 
			
		||||
	if (!pdr->local_f_teid || pdr->local_f_teid->choose_flag) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local TEID (CORE side)", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local F-TEID (Access side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/* decaps from ACCESS */
 | 
			
		||||
	/* To decaps incoming on Core, we need to have a local F-TEID assigned for which to receive GTP packets. */
 | 
			
		||||
	if (!rpdr->local_f_teid || rpdr->local_f_teid->choose_flag) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local TEID (ACCESS side)", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing local F-TEID (Core side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* To encaps, we need to have a remote TEID assigned to send out in GTP packets, and we need to know where to
 | 
			
		||||
	 * send GTP to. */
 | 
			
		||||
	/* encaps towards ACCESS */
 | 
			
		||||
	/* To encaps outgoing on Core, we need to have a remote F-TEID assigned to send out in GTP packets */
 | 
			
		||||
	if (!far->desc.forw_params_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Forwarding Parameters", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Forwarding Parameters (Access side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!far_forw->outer_header_creation_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Outer Header Creation", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Outer Header Creation (Access side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!far_forw->outer_header_creation.teid_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing TEID in FAR Outer Header Creation", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing TEID in FAR Outer Header Creation (Access side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!far_forw->outer_header_creation.ip_addr.v4_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing IPv4 in FAR Outer Header Creation", pdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing IPv4 in FAR Outer Header Creation (Access side)", pdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/* encaps towards CORE */
 | 
			
		||||
 | 
			
		||||
	/* To encaps outgoing on Access, we need to have a remote F-TEID assigned to send out in GTP packets */
 | 
			
		||||
	if (!rfar->desc.forw_params_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Forwarding Parameters", rpdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Forwarding Parameters (Access side)", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!rfar_forw->outer_header_creation_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Outer Header Creation", rpdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing FAR Outer Header Creation (Access side)", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!rfar_forw->outer_header_creation.teid_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing TEID in FAR Outer Header Creation", rpdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing TEID in FAR Outer Header Creation (Access side)", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!rfar_forw->outer_header_creation.ip_addr.v4_present) {
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing IPv4 in FAR Outer Header Creation", rpdr);
 | 
			
		||||
		log_inactive_pdr_set(pdr, "missing IPv4 in FAR Outer Header Creation (Access side)", rpdr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1222,28 +1299,17 @@ static void add_gtp_action_forw(void *ctx, struct llist_head *dst, struct pdr *p
 | 
			
		||||
	talloc_free(rpdr->inactive_reason);
 | 
			
		||||
	rpdr->inactive_reason = NULL;
 | 
			
		||||
 | 
			
		||||
	a = talloc(ctx, struct up_gtp_action);
 | 
			
		||||
	OSMO_ASSERT(a);
 | 
			
		||||
	*a = (struct up_gtp_action){
 | 
			
		||||
		.session = session,
 | 
			
		||||
		.pdr_core = pdr->desc.pdr_id,
 | 
			
		||||
		.pdr_access = rpdr->desc.pdr_id,
 | 
			
		||||
		.kind = UP_GTP_U_TUNMAP,
 | 
			
		||||
		.tunmap = {
 | 
			
		||||
			.core = {
 | 
			
		||||
				.local_teid = pdr->local_f_teid->fixed.teid,
 | 
			
		||||
				.remote_teid = rfar_forw->outer_header_creation.teid,
 | 
			
		||||
				.gtp_remote_addr = rfar_forw->outer_header_creation.ip_addr.v4,
 | 
			
		||||
			},
 | 
			
		||||
			.access = {
 | 
			
		||||
				.local_teid = rpdr->local_f_teid->fixed.teid,
 | 
			
		||||
				.remote_teid = far_forw->outer_header_creation.teid,
 | 
			
		||||
				.gtp_remote_addr = far_forw->outer_header_creation.ip_addr.v4,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	llist_add_tail(&a->entry, dst);
 | 
			
		||||
	a = up_gtp_action_alloc(ctx, session, UP_GTP_U_TUNMAP, dst);
 | 
			
		||||
	a->pdr_access = pdr->desc.pdr_id;
 | 
			
		||||
	a->pdr_core = rpdr->desc.pdr_id;
 | 
			
		||||
	a->tunmap.access.tun.local.addr = pdr->local_f_teid->fixed.ip_addr.v4;
 | 
			
		||||
	a->tunmap.access.tun.local.teid = pdr->local_f_teid->fixed.teid;
 | 
			
		||||
	a->tunmap.access.tun.remote.addr = rfar_forw->outer_header_creation.ip_addr.v4;
 | 
			
		||||
	a->tunmap.access.tun.remote.teid = rfar_forw->outer_header_creation.teid;
 | 
			
		||||
	a->tunmap.core.tun.local.addr = rpdr->local_f_teid->fixed.ip_addr.v4;
 | 
			
		||||
	a->tunmap.core.tun.local.teid = rpdr->local_f_teid->fixed.teid;
 | 
			
		||||
	a->tunmap.core.tun.remote.addr = far_forw->outer_header_creation.ip_addr.v4;
 | 
			
		||||
	a->tunmap.core.tun.remote.teid = far_forw->outer_header_creation.teid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Analyse all PDRs and FARs and find configurations that match either a GTP encaps/decaps or a GTP forward rule. Add to
 | 
			
		||||
@@ -1270,12 +1336,13 @@ static enum osmo_pfcp_cause find_gtp_actions(void *ctx, struct llist_head *dst,
 | 
			
		||||
		if (pdr->reverse_pdr)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* In this outer loop, only follow the forw_to_core directed PDRs, in the inner loop find the matching
 | 
			
		||||
		 * forw_from_core PDR. */
 | 
			
		||||
		if (!pdr->forw_to_core)
 | 
			
		||||
		/* In this outer loop, only follow the access_to_core directed PDRs, in the inner loop find the matching
 | 
			
		||||
		 * core_to_access PDR. i.e. we are looking only at PDRs detecting packets on the Access side, pairing up
 | 
			
		||||
		 * with "reverse PDRs" detecting packets on the Core side. */
 | 
			
		||||
		if (!pdr->access_to_core)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* If a required TEID is not known, we cannot pair this PDR up */
 | 
			
		||||
		/* If a required local addr + TEID is not known, we cannot pair this PDR up */
 | 
			
		||||
		if (pdr->rx_decaps && !pdr->local_f_teid)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
@@ -1286,7 +1353,7 @@ static enum osmo_pfcp_cause find_gtp_actions(void *ctx, struct llist_head *dst,
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			/* Looking for a PDR facing the other way */
 | 
			
		||||
			if (!other->forw_from_core)
 | 
			
		||||
			if (!other->core_to_access)
 | 
			
		||||
				continue;
 | 
			
		||||
			/* GTP header-ness must match, in reverse. */
 | 
			
		||||
			if (pdr->rx_decaps != other->forw_encaps
 | 
			
		||||
@@ -1309,14 +1376,14 @@ static enum osmo_pfcp_cause find_gtp_actions(void *ctx, struct llist_head *dst,
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Iterate in direction to-Core, where pdr->reverse_pdr will be the from-Core counterpart. */
 | 
			
		||||
		if (!pdr->forw_to_core)
 | 
			
		||||
		/* Iterate in direction Access-to-Core, where pdr->reverse_pdr will be the Core-to-Access counterpart. */
 | 
			
		||||
		if (!pdr->access_to_core)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (pdr->rx_decaps && !pdr->forw_encaps)
 | 
			
		||||
			add_gtp_action_endecaps(ctx, dst, pdr);
 | 
			
		||||
			add_gtp_action_tunend(ctx, dst, pdr);
 | 
			
		||||
		else if (pdr->rx_decaps && pdr->forw_encaps)
 | 
			
		||||
			add_gtp_action_forw(ctx, dst, pdr);
 | 
			
		||||
			add_gtp_action_tunmap(ctx, dst, pdr);
 | 
			
		||||
		else {
 | 
			
		||||
			/* log the details of both PDRs in two separate log lines */
 | 
			
		||||
			log_inactive_pdr_set(pdr, "not implemented", pdr);
 | 
			
		||||
@@ -1367,9 +1434,7 @@ static enum osmo_pfcp_cause setup_gtp_actions(struct up_session *session, struct
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		LOGPFSML(session->fi, LOGL_DEBUG, "disabling: %s\n", up_gtp_action_to_str_c(OTC_SELECT, a));
 | 
			
		||||
		up_gtp_action_disable(a);
 | 
			
		||||
		llist_del(&a->entry);
 | 
			
		||||
		talloc_free(a);
 | 
			
		||||
		up_gtp_action_free(a);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set up all GTP tunnels requested in the session setup, but not active yet */
 | 
			
		||||
@@ -1414,11 +1479,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,12 +29,27 @@
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
struct osmo_tdef g_upf_nft_tdefs[] = {
 | 
			
		||||
	{ .T = -32, .default_val = 1000, .unit = OSMO_TDEF_MS,
 | 
			
		||||
	  .desc = "How long to wait for more nft rulesets before flushing in batch",
 | 
			
		||||
	},
 | 
			
		||||
	{ .T = -33, .default_val = 1, .unit = OSMO_TDEF_CUSTOM,
 | 
			
		||||
	  .desc = "When reaching this nr of queued nft rulesets, flush the queue",
 | 
			
		||||
	  .max_val = 128,
 | 
			
		||||
	},
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_tdef_group g_upf_tdef_groups[] = {
 | 
			
		||||
	{ "pfcp", "PFCP endpoint timers", osmo_pfcp_tdefs, },
 | 
			
		||||
	{ "nft", "netfilter timers", g_upf_nft_tdefs, },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -50,20 +65,24 @@ void g_upf_alloc(void *ctx)
 | 
			
		||||
				.local_port = OSMO_PFCP_PORT,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.nft = {
 | 
			
		||||
			.priority = -300,
 | 
			
		||||
		.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);
 | 
			
		||||
	hash_init(g_upf->tunmap.nft_tun_by_chain_id);
 | 
			
		||||
	hash_init(g_upf->gtp.pdrs_by_local_f_teid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_pfcp_listen()
 | 
			
		||||
int upf_pfcp_init(void)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sockaddr_str local_addr_str;
 | 
			
		||||
	struct osmo_sockaddr local_addr;
 | 
			
		||||
@@ -75,9 +94,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;
 | 
			
		||||
@@ -85,10 +103,30 @@ 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 gtp_vty_cfg *c = &g_upf->gtp.vty_cfg;
 | 
			
		||||
	struct gtp_vty_cfg_dev *d;
 | 
			
		||||
	struct tunend_vty_cfg *c = &g_upf->tunend.vty_cfg;
 | 
			
		||||
	struct tunend_vty_cfg_dev *d;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(d, &c->devs, entry) {
 | 
			
		||||
		if (upf_gtp_dev_open(d->dev_name, d->create, d->local_addr, false, false))
 | 
			
		||||
@@ -96,3 +134,76 @@ int upf_gtp_devs_open()
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool upf_is_local_teid_in_use(uint32_t teid)
 | 
			
		||||
{
 | 
			
		||||
	struct pdr *pdr;
 | 
			
		||||
	hash_for_each_possible(g_upf->gtp.pdrs_by_local_f_teid, pdr, node_by_local_f_teid, teid) {
 | 
			
		||||
		if (!pdr->local_f_teid)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (pdr->local_f_teid->fixed.teid != teid)
 | 
			
		||||
			continue;
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool upf_is_chain_id_in_use(uint32_t chain_id)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_nft_tun *nft_tun;
 | 
			
		||||
	hash_for_each_possible(g_upf->tunmap.nft_tun_by_chain_id, nft_tun, node_by_chain_id, chain_id) {
 | 
			
		||||
		if (nft_tun->chain_id != chain_id)
 | 
			
		||||
			continue;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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--) {
 | 
			
		||||
		uint32_t chain_id = upf_next_chain_id_inc();
 | 
			
		||||
 | 
			
		||||
		if (!g_upf->pfcp.ep)
 | 
			
		||||
			return chain_id;
 | 
			
		||||
 | 
			
		||||
		if (upf_is_chain_id_in_use(chain_id))
 | 
			
		||||
			continue;
 | 
			
		||||
		return chain_id;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* finding a chain_id became insane, return invalid = 0 */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
#include <osmocom/upf/upf_gtpu_echo.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_GTP_TUN(TUN, LEVEL, FMT, ARGS...) \
 | 
			
		||||
	LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_tun_to_str_c(OTC_SELECT, (TUN)), ##ARGS)
 | 
			
		||||
	LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_tunend_to_str_c(OTC_SELECT, (TUN)), ##ARGS)
 | 
			
		||||
 | 
			
		||||
int upf_gtp_dev_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
@@ -66,16 +66,36 @@ 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;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *local_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_dev *dev;
 | 
			
		||||
	struct upf_gtp_dev *dev_any = NULL;
 | 
			
		||||
	struct osmo_sockaddr needle = *local_addr;
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
		if (!osmo_sockaddr_cmp(&needle, &dev->gtpv1.local_addr))
 | 
			
		||||
			return dev;
 | 
			
		||||
		if (osmo_sockaddr_is_any(&dev->gtpv1.local_addr) == 1)
 | 
			
		||||
			dev_any = dev;
 | 
			
		||||
	}
 | 
			
		||||
	/* No 1:1 match found, but there is a dev listening on ANY? Return that.
 | 
			
		||||
	 * If there is no such dev, return NULL. */
 | 
			
		||||
	return dev_any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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()). */
 | 
			
		||||
@@ -96,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -114,6 +134,7 @@ static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local
 | 
			
		||||
		.gtpv1.ofd.fd = -1,
 | 
			
		||||
	};
 | 
			
		||||
	INIT_LLIST_HEAD(&dev->tunnels);
 | 
			
		||||
	hash_init(dev->tunnels_by_local_f_teid);
 | 
			
		||||
 | 
			
		||||
	osmo_sockaddr_str_from_str(&addr_conv, local_addr, PORT_GTP0_U);
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +145,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);
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +164,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)
 | 
			
		||||
@@ -158,6 +179,13 @@ static int dev_resolve_ifidx(struct upf_gtp_dev *dev)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_dev_create(struct upf_gtp_dev *dev, int gtp0_fd, int gtp1_fd)
 | 
			
		||||
{
 | 
			
		||||
	if (dev->sgsn_mode)
 | 
			
		||||
		return gtp_dev_create_sgsn(-1, dev->name, gtp0_fd, gtp1_fd);
 | 
			
		||||
	return gtp_dev_create(-1, dev->name, gtp0_fd, gtp1_fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0, bool sgsn_mode)
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_sockaddr any = {
 | 
			
		||||
@@ -172,8 +200,8 @@ 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) {
 | 
			
		||||
		LOGP(DGTP, LOGL_NOTICE, "gtp/mockup active: not opening GTP device '%s'\n", name);
 | 
			
		||||
	if (g_upf->tunend.mockup) {
 | 
			
		||||
		LOGP(DGTP, LOGL_NOTICE, "tunend/mockup active: not opening GTP device '%s'\n", name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -183,6 +211,12 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
 | 
			
		||||
 | 
			
		||||
	dev->sgsn_mode = sgsn_mode;
 | 
			
		||||
 | 
			
		||||
	rc = upf_gtp_genl_ensure_open();
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot set up GTP device, failed to open mnl_socket\n");
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (listen_for_gtpv0) {
 | 
			
		||||
		rc = osmo_sock_init_osa_ofd(&dev->gtpv0.ofd, SOCK_DGRAM, 0, &dev->gtpv0.local_addr, &any,
 | 
			
		||||
					    OSMO_SOCK_F_BIND);
 | 
			
		||||
@@ -209,10 +243,13 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
 | 
			
		||||
	if (create_gtp_dev) {
 | 
			
		||||
		int gtp0_fd = listen_for_gtpv0 ? dev->gtpv0.ofd.fd : -1;
 | 
			
		||||
		int gtp1_fd = dev->gtpv1.ofd.fd;
 | 
			
		||||
		if (dev->sgsn_mode)
 | 
			
		||||
			rc = gtp_dev_create_sgsn(-1, dev->name, gtp0_fd, gtp1_fd);
 | 
			
		||||
		else
 | 
			
		||||
			rc = gtp_dev_create(-1, dev->name, gtp0_fd, gtp1_fd);
 | 
			
		||||
 | 
			
		||||
		rc = upf_gtp_dev_create(dev, gtp0_fd, gtp1_fd);
 | 
			
		||||
		if (rc == -EEXIST && gtp_dev_destroy(dev->name) == 0) {
 | 
			
		||||
			LOG_GTP_DEV(dev, LOGL_ERROR, "deleted GTP device from unclean shutdown\n");
 | 
			
		||||
			rc = upf_gtp_dev_create(dev, gtp0_fd, gtp1_fd);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot create GTP device: rc=%d\n", rc);
 | 
			
		||||
			/* name = NULL: signal to the destructor that it does not need to delete the device */
 | 
			
		||||
@@ -239,45 +276,40 @@ 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");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open an MNL socket which allows to create and remove GTP devices (requires CAP_NET_ADMIN). */
 | 
			
		||||
int upf_gtp_genl_open()
 | 
			
		||||
int upf_gtp_genl_ensure_open()
 | 
			
		||||
{
 | 
			
		||||
	if (g_upf->gtp.mockup) {
 | 
			
		||||
		LOGP(DGTP, LOGL_NOTICE, "gtp/mockup active: not opening mnl_socket\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	}
 | 
			
		||||
@@ -286,59 +318,72 @@ int upf_gtp_genl_open()
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct upf_gtp_tun {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
struct upf_gtp_tunend {
 | 
			
		||||
	struct llist_head entry; /* item in (struct upf_gtp_dev)->tunnels */
 | 
			
		||||
	struct hlist_node node_by_local_f_teid; /* item in g_upf->gtp.pdrs_by_local_f_teid */
 | 
			
		||||
 | 
			
		||||
	struct upf_gtp_dev *dev;
 | 
			
		||||
	struct upf_gtp_tun_desc desc;
 | 
			
		||||
	struct upf_tunend desc;
 | 
			
		||||
	bool active;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_tun_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_tun *tun)
 | 
			
		||||
static int upf_gtp_tunend_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "%s:tun{TEID=l:0x%x,r:0x%x UE=", tun->dev->name, tun->desc.local_teid,
 | 
			
		||||
			   tun->desc.remote_teid);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.ue_addr);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " GTP-dst=");
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.gtp_remote_addr);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "}");
 | 
			
		||||
	/* "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.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);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.core.ue_local_addr);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, ")}");
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *upf_gtp_tun_to_str_c(void *ctx, const struct upf_gtp_tun *tun)
 | 
			
		||||
static char *upf_gtp_tunend_to_str_c(void *ctx, const struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 64, "ERROR", upf_gtp_tun_to_str_buf, tun)
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 64, "ERROR", upf_gtp_tunend_to_str_buf, tun)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_tun_deactivate(struct upf_gtp_tun *tun);
 | 
			
		||||
static int upf_gtp_tunend_deactivate(struct upf_gtp_tunend *tun);
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_tun_destruct(struct upf_gtp_tun *tun)
 | 
			
		||||
static int upf_gtp_tunend_destruct(struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	if (tun->active)
 | 
			
		||||
		upf_gtp_tun_deactivate(tun);
 | 
			
		||||
		upf_gtp_tunend_deactivate(tun);
 | 
			
		||||
	hash_del(&tun->node_by_local_f_teid);
 | 
			
		||||
	llist_del(&tun->entry);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct upf_gtp_tun *upf_gtp_tun_alloc(struct upf_gtp_dev *dev, const struct upf_gtp_tun_desc *desc)
 | 
			
		||||
#define tunend_validate(TUNEND) \
 | 
			
		||||
	do { \
 | 
			
		||||
		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_tunend *desc)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_tun *tun = talloc(dev, struct upf_gtp_tun);
 | 
			
		||||
	struct upf_gtp_tunend *tun = talloc(dev, struct upf_gtp_tunend);
 | 
			
		||||
	OSMO_ASSERT(tun);
 | 
			
		||||
	*tun = (struct upf_gtp_tun){
 | 
			
		||||
	tunend_validate(desc);
 | 
			
		||||
	*tun = (struct upf_gtp_tunend){
 | 
			
		||||
		.dev = dev,
 | 
			
		||||
		.desc = *desc,
 | 
			
		||||
	};
 | 
			
		||||
	hash_add(dev->tunnels_by_local_f_teid, &tun->node_by_local_f_teid, tun->desc.access.local.teid);
 | 
			
		||||
	llist_add(&tun->entry, &dev->tunnels);
 | 
			
		||||
	talloc_set_destructor(tun, upf_gtp_tun_destruct);
 | 
			
		||||
	talloc_set_destructor(tun, upf_gtp_tunend_destruct);
 | 
			
		||||
	return tun;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct gtp_tunnel *upf_gtp_tun_to_gtp_tunnel(struct upf_gtp_tun *tun)
 | 
			
		||||
static struct gtp_tunnel *upf_gtp_tunend_to_gtp_tunnel(struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	struct gtp_tunnel *t;
 | 
			
		||||
 | 
			
		||||
	if (tun->desc.ue_addr.u.sas.ss_family != AF_INET || tun->desc.gtp_remote_addr.u.sas.ss_family != AF_INET) {
 | 
			
		||||
	if (tun->desc.core.ue_local_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;
 | 
			
		||||
	}
 | 
			
		||||
@@ -347,14 +392,14 @@ static struct gtp_tunnel *upf_gtp_tun_to_gtp_tunnel(struct upf_gtp_tun *tun)
 | 
			
		||||
	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.local_teid);
 | 
			
		||||
	gtp_tunnel_set_o_tei(t, tun->desc.remote_teid);
 | 
			
		||||
	gtp_tunnel_set_ms_ip4(t, &tun->desc.ue_addr.u.sin.sin_addr);
 | 
			
		||||
	gtp_tunnel_set_sgsn_ip4(t, &tun->desc.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtp_tun_activate(struct upf_gtp_tun *tun)
 | 
			
		||||
int upf_gtp_tunend_activate(struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct gtp_tunnel *t;
 | 
			
		||||
@@ -362,12 +407,12 @@ int upf_gtp_tun_activate(struct upf_gtp_tun *tun)
 | 
			
		||||
	if (tun->active)
 | 
			
		||||
		return -EALREADY;
 | 
			
		||||
 | 
			
		||||
	t = upf_gtp_tun_to_gtp_tunnel(tun);
 | 
			
		||||
	t = upf_gtp_tunend_to_gtp_tunnel(tun);
 | 
			
		||||
	if (!t)
 | 
			
		||||
		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) {
 | 
			
		||||
@@ -380,37 +425,41 @@ int upf_gtp_tun_activate(struct upf_gtp_tun *tun)
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct upf_gtp_tun *upf_gtp_dev_tunnel_find(struct upf_gtp_dev *dev, const struct upf_gtp_tun_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_tun *tun;
 | 
			
		||||
	llist_for_each_entry(tun, &dev->tunnels, entry) {
 | 
			
		||||
		if (upf_gtp_tun_desc_cmp(tun_desc, &tun->desc))
 | 
			
		||||
	struct upf_gtp_tunend *tun;
 | 
			
		||||
	tunend_validate(tunend);
 | 
			
		||||
 | 
			
		||||
	hash_for_each_possible(dev->tunnels_by_local_f_teid, tun, node_by_local_f_teid, tunend->access.local.teid) {
 | 
			
		||||
		if (upf_gtp_tunend_cmp(tunend, &tun->desc))
 | 
			
		||||
			continue;
 | 
			
		||||
		return tun;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtp_dev_tunnel_add(struct upf_gtp_dev *dev, const struct upf_gtp_tun_desc *tun_desc)
 | 
			
		||||
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_tun *tun;
 | 
			
		||||
	tun = upf_gtp_dev_tunnel_find(dev, tun_desc);
 | 
			
		||||
	struct upf_gtp_tunend *tun;
 | 
			
		||||
	tunend_validate(tunend);
 | 
			
		||||
	tun = upf_gtp_dev_tunend_find(dev, tunend);
 | 
			
		||||
	if (!tun)
 | 
			
		||||
		tun = upf_gtp_tun_alloc(dev, tun_desc);
 | 
			
		||||
		tun = upf_gtp_tunend_alloc(dev, tunend);
 | 
			
		||||
	if (tun->active)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return upf_gtp_tun_activate(tun);
 | 
			
		||||
	return upf_gtp_tunend_activate(tun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtp_dev_tunnel_del(struct upf_gtp_dev *dev, const struct upf_gtp_tun_desc *tun_desc)
 | 
			
		||||
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_tun *tun;
 | 
			
		||||
	struct upf_gtp_tunend *tun;
 | 
			
		||||
	int rc;
 | 
			
		||||
	tun = upf_gtp_dev_tunnel_find(dev, tun_desc);
 | 
			
		||||
	tunend_validate(tunend);
 | 
			
		||||
	tun = upf_gtp_dev_tunend_find(dev, tunend);
 | 
			
		||||
	if (!tun)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (tun->active) {
 | 
			
		||||
		rc = upf_gtp_tun_deactivate(tun);
 | 
			
		||||
		rc = upf_gtp_tunend_deactivate(tun);
 | 
			
		||||
		if (rc)
 | 
			
		||||
			return rc;
 | 
			
		||||
	}
 | 
			
		||||
@@ -418,7 +467,7 @@ int upf_gtp_dev_tunnel_del(struct upf_gtp_dev *dev, const struct upf_gtp_tun_des
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_tun_deactivate(struct upf_gtp_tun *tun)
 | 
			
		||||
static int upf_gtp_tunend_deactivate(struct upf_gtp_tunend *tun)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct gtp_tunnel *t;
 | 
			
		||||
@@ -428,13 +477,13 @@ static int upf_gtp_tun_deactivate(struct upf_gtp_tun *tun)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t = upf_gtp_tun_to_gtp_tunnel(tun);
 | 
			
		||||
	t = upf_gtp_tunend_to_gtp_tunnel(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: %d %s\n", rc, strerror(rc));
 | 
			
		||||
		LOG_GTP_TUN(tun, LOGL_ERROR, "Failed to delete tunnel\n");
 | 
			
		||||
	else
 | 
			
		||||
		tun->active = false;
 | 
			
		||||
 | 
			
		||||
@@ -444,9 +493,9 @@ static int upf_gtp_tun_deactivate(struct upf_gtp_tun *tun)
 | 
			
		||||
 | 
			
		||||
static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_tun *t;
 | 
			
		||||
	struct upf_gtp_tunend *t;
 | 
			
		||||
	/* Destruct and clean up all active tunnels before deleting the device */
 | 
			
		||||
	while ((t = llist_first_entry_or_null(&dev->tunnels, struct upf_gtp_tun, entry)))
 | 
			
		||||
	while ((t = llist_first_entry_or_null(&dev->tunnels, struct upf_gtp_tunend, entry)))
 | 
			
		||||
		talloc_free(t);
 | 
			
		||||
	llist_del(&dev->entry);
 | 
			
		||||
	/* osmo_fd_close() is a noop if ofd.fd == -1 */
 | 
			
		||||
@@ -457,7 +506,7 @@ static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtp_tun_desc_cmp(const struct upf_gtp_tun_desc *a, const struct upf_gtp_tun_desc *b)
 | 
			
		||||
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
@@ -469,9 +518,9 @@ int upf_gtp_tun_desc_cmp(const struct upf_gtp_tun_desc *a, const struct upf_gtp_
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
#define CMP_MEMB(MEMB) OSMO_CMP(a->MEMB, b->MEMB)
 | 
			
		||||
	if ((r = CMP_MEMB(local_teid)))
 | 
			
		||||
	if ((r = CMP_MEMB(access.local.teid)))
 | 
			
		||||
		return r;
 | 
			
		||||
	if ((r = CMP_MEMB(remote_teid)))
 | 
			
		||||
	if ((r = CMP_MEMB(access.remote.teid)))
 | 
			
		||||
		return r;
 | 
			
		||||
	return osmo_sockaddr_cmp(&a->gtp_remote_addr, &b->gtp_remote_addr);
 | 
			
		||||
	return osmo_sockaddr_cmp(&a->access.remote.addr, &b->access.remote.addr);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,12 @@
 | 
			
		||||
#define GTP1U_PORT	2152
 | 
			
		||||
 | 
			
		||||
enum gtp1u_msgt {
 | 
			
		||||
	GTP1U_MSGTYPE_ECHO_REQ = 1,
 | 
			
		||||
	GTP1U_MSGTYPE_ECHO_RSP = 2,
 | 
			
		||||
	GTP1U_MSGTYPE_ECHO_REQ			= 1,
 | 
			
		||||
	GTP1U_MSGTYPE_ECHO_RSP			= 2,
 | 
			
		||||
	GTP1U_MSGTYPE_ERRR_IND			= 26,
 | 
			
		||||
	GTP1U_MSGTYPE_SUPP_EXT_HDR_NOTIF	= 31,
 | 
			
		||||
	GTP1U_MSGTYPE_END_MARKER		= 254,
 | 
			
		||||
	GTP1U_MSGTYPE_GPDU			= 255,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gtp1u_iei {
 | 
			
		||||
@@ -31,6 +35,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 +54,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,20 +108,62 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtpu_echo_read_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
int upf_gtpu_echo_req_tx(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr)
 | 
			
		||||
{
 | 
			
		||||
	struct gtp1u_hdr *tx_h;
 | 
			
		||||
	int rc;
 | 
			
		||||
	uint8_t msgbuf[sizeof(struct gtp1u_hdr) + 2];
 | 
			
		||||
 | 
			
		||||
	tx_h = (void *)msgbuf;
 | 
			
		||||
	*tx_h = (struct gtp1u_hdr){
 | 
			
		||||
		/* 3GPP TS 29.281 5.1 defines that the ECHO REQ & RESP shall contain a sequence nr */
 | 
			
		||||
		.s = 1,
 | 
			
		||||
		.pt = 1,
 | 
			
		||||
		.version = 1,
 | 
			
		||||
		.msg_type = GTP1U_MSGTYPE_ECHO_REQ,
 | 
			
		||||
		.ext = {
 | 
			
		||||
			.seq_nr = seq_nr,
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* ECHO REQUEST shall contain a recovery counter */
 | 
			
		||||
	tx_h->data2[0] = GTP1U_IEI_RECOVERY;
 | 
			
		||||
	tx_h->data2[1] = g_upf->tunend.recovery_count;
 | 
			
		||||
 | 
			
		||||
	osmo_store16be(sizeof(msgbuf) - offsetof(struct gtp1u_hdr, data1), &tx_h->length);
 | 
			
		||||
 | 
			
		||||
	rc = sendto(dev->gtpv1.ofd.fd, msgbuf, sizeof(msgbuf), 0, &remote->u.sa, sizeof(*remote));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		rc = -errno;
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_ERROR, "GTP1-U sendto(len=%zu, to=%s): %s\n", sizeof(msgbuf),
 | 
			
		||||
			    osmo_sockaddr_to_str(remote), strerror(-rc));
 | 
			
		||||
	} else {
 | 
			
		||||
		rc = 0;
 | 
			
		||||
	}
 | 
			
		||||
	LOG_GTP_DEV(dev, LOGL_INFO, "<- %s: tx GTP1-U Echo Request: seq_nr=%u recovery_count=%u\n",
 | 
			
		||||
		    osmo_sockaddr_to_str(remote), seq_nr, g_upf->tunend.recovery_count);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_gtpu_read_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_gtp_dev *dev = ofd->data;
 | 
			
		||||
 | 
			
		||||
@@ -119,43 +186,61 @@ 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;
 | 
			
		||||
	case GTP1U_MSGTYPE_ERRR_IND:
 | 
			
		||||
		/* 3GPP TS 29.281 7.3.1: Log "Tunnel Endpoint Identifier Data I" and "GTP-U Peer Address" */
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_NOTICE, "%s rx: GTPv1-U Error Indication not supported\n",
 | 
			
		||||
			    osmo_sockaddr_to_str(&remote));
 | 
			
		||||
		return 0;
 | 
			
		||||
	case GTP1U_MSGTYPE_GPDU:
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_NOTICE, "%s rx: GTPv1-U PDU TEID=0x%08x over slow path not supported\n",
 | 
			
		||||
			    osmo_sockaddr_to_str(&remote), osmo_load32be(&h->tei));
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_ERROR, "rx: GTPv1-U message type %u not supported\n", h->msg_type);
 | 
			
		||||
		LOG_GTP_DEV(dev, LOGL_ERROR, "%s rx: GTPv1-U message type %u not supported\n",
 | 
			
		||||
			    osmo_sockaddr_to_str(&remote), 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev->gtpv1.ofd.cb = upf_gtpu_echo_read_cb;
 | 
			
		||||
	/* the caller should already have osmo_fd_register()ed when setting up the socket. */
 | 
			
		||||
	OSMO_ASSERT(osmo_fd_is_registered(&dev->gtpv1.ofd));
 | 
			
		||||
	/* make sure there is no cb yet that this would be replacing. */
 | 
			
		||||
	OSMO_ASSERT(dev->gtpv1.ofd.cb == NULL);
 | 
			
		||||
 | 
			
		||||
	dev->gtpv1.ofd.cb = upf_gtpu_read_cb;
 | 
			
		||||
	dev->gtpv1.ofd.data = dev;
 | 
			
		||||
	return osmo_fd_register(&dev->gtpv1.ofd);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,122 +26,351 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/upf/upf.h>
 | 
			
		||||
#include <osmocom/upf/upf_nft.h>
 | 
			
		||||
 | 
			
		||||
static char *upf_nft_ruleset_table_create(void *ctx, const char *table_name)
 | 
			
		||||
{
 | 
			
		||||
	return talloc_asprintf(ctx, "add table inet %s\n", table_name);
 | 
			
		||||
	return talloc_asprintf(ctx, "add table inet %s { flags owner; };\n", table_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_nft_run(const char *ruleset)
 | 
			
		||||
static char *upf_nft_ruleset_vmap_init(void *ctx, const char *table_name, int priority_pre, int priority_post)
 | 
			
		||||
{
 | 
			
		||||
	/* 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
 | 
			
		||||
	 */
 | 
			
		||||
	return talloc_asprintf(ctx,
 | 
			
		||||
		"add chain inet %s pre { type filter hook prerouting priority %d; policy accept; };\n"
 | 
			
		||||
		"add chain inet %s post { type filter hook postrouting priority %d; policy accept; };\n"
 | 
			
		||||
		"add map inet %s tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; };\n"
 | 
			
		||||
		"add map inet %s tunmap-post { typeof meta mark : verdict; };\n"
 | 
			
		||||
		"add rule inet %s pre udp dport %u ip daddr . @ih,32,32 vmap @tunmap-pre;\n"
 | 
			
		||||
		"add rule inet %s post meta mark vmap @tunmap-post;\n",
 | 
			
		||||
		table_name, priority_pre,
 | 
			
		||||
		table_name, priority_post,
 | 
			
		||||
		table_name,
 | 
			
		||||
		table_name,
 | 
			
		||||
		table_name, PORT_GTP1_U,
 | 
			
		||||
		table_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_nft_run_now(const char *ruleset)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	const int logmax = 256;
 | 
			
		||||
 | 
			
		||||
	if (g_upf->nft.mockup) {
 | 
			
		||||
		LOGP(DNFT, LOGL_NOTICE, "nft/mockup active: not running nft ruleset: '%s'\n", ruleset);
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (log_check_level(DNFT, LOGL_DEBUG)) {
 | 
			
		||||
		size_t l = strlen(ruleset);
 | 
			
		||||
		LOGP(DNFT, LOGL_DEBUG, "ran nft ruleset, %zu chars: \"%s%s\"\n",
 | 
			
		||||
		     l,
 | 
			
		||||
		     osmo_escape_cstr_c(OTC_SELECT, ruleset, OSMO_MIN(logmax, l)),
 | 
			
		||||
		     l > logmax ? "..." : "");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct nft_queue {
 | 
			
		||||
	struct osmo_tdef *flush_time_tdef;
 | 
			
		||||
	struct osmo_tdef *ruleset_max_tdef;
 | 
			
		||||
	struct osmo_strbuf sb;
 | 
			
		||||
	/* 128 NFT rulesets amount to about 110 kb of char */
 | 
			
		||||
	char buf[1<<17];
 | 
			
		||||
	unsigned int ruleset_count;
 | 
			
		||||
	struct osmo_timer_list timer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void nft_queue_clear_buf(struct nft_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	q->sb = (struct osmo_strbuf){ .buf = q->buf, .len = sizeof(q->buf) };
 | 
			
		||||
	q->buf[0] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nft_queue_init(void *ctx, struct nft_queue *q,
 | 
			
		||||
			   struct osmo_tdef *flush_time_tdef,
 | 
			
		||||
			   struct osmo_tdef *ruleset_max_tdef)
 | 
			
		||||
{
 | 
			
		||||
	*q = (struct nft_queue){
 | 
			
		||||
		.flush_time_tdef = flush_time_tdef,
 | 
			
		||||
		.ruleset_max_tdef = ruleset_max_tdef,
 | 
			
		||||
	};
 | 
			
		||||
	nft_queue_clear_buf(q);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nft_queue_flush(struct nft_queue *q, const char *reason)
 | 
			
		||||
{
 | 
			
		||||
	static unsigned int flush_count = 0;
 | 
			
		||||
	static unsigned int ruleset_count = 0;
 | 
			
		||||
 | 
			
		||||
	/* We will now flush the queue empty. A timer needs to run only when the next pending entry is added. */
 | 
			
		||||
	osmo_timer_del(&q->timer);
 | 
			
		||||
 | 
			
		||||
	/* Nothing to send? */
 | 
			
		||||
	if (!q->sb.chars_needed)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	flush_count++;
 | 
			
		||||
	ruleset_count += q->ruleset_count;
 | 
			
		||||
	LOGP(DNFT, LOGL_INFO, "Flushing NFT ruleset queue: %s: n:%u strlen:%zu (flush count: %u avg rules per flush: %s)\n",
 | 
			
		||||
	     reason,
 | 
			
		||||
	     q->ruleset_count, q->sb.chars_needed,
 | 
			
		||||
	     flush_count, osmo_int_to_float_str_c(OTC_SELECT, 10 * ruleset_count / flush_count, 1));
 | 
			
		||||
 | 
			
		||||
	q->ruleset_count = 0;
 | 
			
		||||
 | 
			
		||||
	upf_nft_run_now(q->sb.buf);
 | 
			
		||||
 | 
			
		||||
	nft_queue_clear_buf(q);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nft_queue_flush_cb(void *q)
 | 
			
		||||
{
 | 
			
		||||
	nft_queue_flush(q, "timeout");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nft_enqueue(struct nft_queue *q,
 | 
			
		||||
		       int (*tunmap_to_str_buf)(char *buf, size_t len, struct upf_tunmap *tunmap),
 | 
			
		||||
		       struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	int ruleset_max;
 | 
			
		||||
	struct osmo_strbuf q_sb_was = q->sb;
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
 | 
			
		||||
 | 
			
		||||
	/* is that being cut off? then revert the addition. This should never happen in practice. */
 | 
			
		||||
	if (q->sb.chars_needed >= q->sb.len) {
 | 
			
		||||
		q->sb = q_sb_was;
 | 
			
		||||
		if (q->sb.pos)
 | 
			
		||||
			*q->sb.pos = '\0';
 | 
			
		||||
		nft_queue_flush(q, "reached max nr of chars");
 | 
			
		||||
		OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Append separator -- no problem if that gets cut off. */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(q->sb, "\n");
 | 
			
		||||
 | 
			
		||||
	q->ruleset_count++;
 | 
			
		||||
 | 
			
		||||
	LOGP(DNFT, LOGL_INFO, "Added NFT ruleset to queue: n:%u strlen:%zu\n",
 | 
			
		||||
	     q->ruleset_count, q->sb.chars_needed);
 | 
			
		||||
 | 
			
		||||
	/* Added a rule, see if it has reached ruleset_max. */
 | 
			
		||||
	ruleset_max = osmo_tdef_get(q->ruleset_max_tdef, q->ruleset_max_tdef->T, OSMO_TDEF_CUSTOM, 128);
 | 
			
		||||
	if (q->ruleset_count >= ruleset_max) {
 | 
			
		||||
		nft_queue_flush(q, "reached max nr of rules");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Item added. If the timer is not running yet, schedule a flush in given timeout */
 | 
			
		||||
	if (!osmo_timer_pending(&q->timer)) {
 | 
			
		||||
		struct osmo_tdef *t;
 | 
			
		||||
		unsigned long us;
 | 
			
		||||
		osmo_timer_setup(&q->timer, nft_queue_flush_cb, q);
 | 
			
		||||
		t = q->flush_time_tdef;
 | 
			
		||||
		us = osmo_tdef_get(t, t->T, OSMO_TDEF_US, 100000);
 | 
			
		||||
		osmo_timer_schedule(&q->timer, us / 1000000, us % 1000000);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nft_queue_free(struct nft_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	osmo_timer_del(&q->timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct nft_queue g_nft_queue = {};
 | 
			
		||||
 | 
			
		||||
int upf_nft_init()
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	if (g_upf->nft.mockup) {
 | 
			
		||||
 | 
			
		||||
	nft_queue_init(g_upf, &g_nft_queue,
 | 
			
		||||
		       osmo_tdef_get_entry(g_upf_nft_tdefs, -32),
 | 
			
		||||
		       osmo_tdef_get_entry(g_upf_nft_tdefs, -33));
 | 
			
		||||
 | 
			
		||||
	/* Always set up the default settings, also in mockup mode, so that the VTY reflects sane values */
 | 
			
		||||
	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->tunmap.mockup) {
 | 
			
		||||
		LOGP(DNFT, LOGL_NOTICE,
 | 
			
		||||
		     "nft/mockup active: not allocating libnftables nft_ctx. FOR TESTING PURPOSES ONLY.\n");
 | 
			
		||||
		     "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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!g_upf->nft.table_name)
 | 
			
		||||
		g_upf->nft.table_name = talloc_strdup(g_upf, "osmo-upf");
 | 
			
		||||
 | 
			
		||||
	rc = upf_nft_run(upf_nft_ruleset_table_create(OTC_SELECT, g_upf->nft.table_name));
 | 
			
		||||
	rc = upf_nft_run_now(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->tunmap.table_name, -1));
 | 
			
		||||
 | 
			
		||||
	rc = upf_nft_run_now(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->tunmap.table_name);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->nft.table_name, -1));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_free()
 | 
			
		||||
{
 | 
			
		||||
	if (!g_upf->nft.nft_ctx)
 | 
			
		||||
	nft_queue_free(&g_nft_queue);
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct upf_nft_args_peer {
 | 
			
		||||
	/* The source IP address in packets received from this peer */
 | 
			
		||||
	const struct osmo_sockaddr *addr;
 | 
			
		||||
	const struct osmo_sockaddr *addr_remote;
 | 
			
		||||
	/* The TEID that we send to the peer in GTP packets. */
 | 
			
		||||
	uint32_t teid_remote;
 | 
			
		||||
	/* The local destination IP address in packets received from this peer */
 | 
			
		||||
	const struct osmo_sockaddr *addr_local;
 | 
			
		||||
	/* The TEID that the peer sends to us in GTP packets. */
 | 
			
		||||
	uint32_t teid_local;
 | 
			
		||||
	/* The nft chain id that forwards packets received on addr_local,teid_local. Also used for the 'mark' id in
 | 
			
		||||
	 * the verdict map ruleset. */
 | 
			
		||||
	uint32_t chain_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct upf_nft_args {
 | 
			
		||||
	/* global table name */
 | 
			
		||||
	const char *table_name;
 | 
			
		||||
	/* chain name for this specific tunnel mapping */
 | 
			
		||||
	uint32_t chain_id;
 | 
			
		||||
	int priority;
 | 
			
		||||
 | 
			
		||||
	struct upf_nft_args_peer peer_a;
 | 
			
		||||
	struct upf_nft_args_peer peer_b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int tunmap_single_direction(char *buf, size_t buflen,
 | 
			
		||||
				   const struct upf_nft_args *args,
 | 
			
		||||
				   const struct upf_nft_args_peer *from_peer,
 | 
			
		||||
				   const struct upf_nft_args_peer *to_peer)
 | 
			
		||||
static int tunmap_add_single_direction(char *buf, size_t buflen,
 | 
			
		||||
				       const struct upf_nft_args *args,
 | 
			
		||||
				       bool dir_a2b)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add rule inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u", args->table_name, args->chain_id);
 | 
			
		||||
	const struct upf_nft_args_peer *from_peer;
 | 
			
		||||
	const struct upf_nft_args_peer *to_peer;
 | 
			
		||||
 | 
			
		||||
	/* Match only UDP packets */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " meta l4proto udp");
 | 
			
		||||
	if (dir_a2b) {
 | 
			
		||||
		from_peer = &args->peer_a;
 | 
			
		||||
		to_peer = &args->peer_b;
 | 
			
		||||
	} else {
 | 
			
		||||
		from_peer = &args->peer_b;
 | 
			
		||||
		to_peer = &args->peer_a;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Match on packets coming in from from_peer */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " ip saddr ");
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr);
 | 
			
		||||
	/* # add chain for verdict map in prerouting
 | 
			
		||||
	 * add chain inet osmo-upf tunmap-pre-123
 | 
			
		||||
	 * # mangle destination address at prerouting
 | 
			
		||||
	 * add rule inet osmo-upf tunmap-pre-123 ip daddr set 1.1.1.1 meta mark set 123 counter accept
 | 
			
		||||
	 *
 | 
			
		||||
	 * # 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 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 }
 | 
			
		||||
	 * add element inet osmo-upf tunmap-post { 123 : jump tunmap-post-123 }
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* Match on the TEID in the header */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " @ih,32,32 0x%08x", from_peer->teid_local);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-pre-%u;\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	/* Change destination address to to_peer */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-pre-%u",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " ip daddr set ");
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_remote);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " meta mark set %u counter accept;\n", from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	/* Change the TEID in the header to the one to_peer expects */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " @ih,32,32 set 0x%08x", to_peer->teid_remote);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-post-%u;\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " counter\n");
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-post-%u",
 | 
			
		||||
			   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");
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-pre { ",
 | 
			
		||||
			   args->table_name);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " . 0x%x : jump tunmap-pre-%u };\n",
 | 
			
		||||
			   from_peer->teid_local, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-post { %u : jump tunmap-post-%u };\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tunmap_del_single_direction(char *buf, size_t buflen,
 | 
			
		||||
				       const struct upf_nft_args *args,
 | 
			
		||||
				       bool dir_a2b)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	const struct upf_nft_args_peer *from_peer;
 | 
			
		||||
 | 
			
		||||
	if (dir_a2b)
 | 
			
		||||
		from_peer = &args->peer_a;
 | 
			
		||||
	else
 | 
			
		||||
		from_peer = &args->peer_b;
 | 
			
		||||
 | 
			
		||||
	/* delete element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x203 }
 | 
			
		||||
	 * delete element inet osmo-upf tunmap-post { 123 }
 | 
			
		||||
	 * delete chain inet osmo-upf tunmap-pre-123
 | 
			
		||||
	 * delete chain inet osmo-upf tunmap-post-123
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-pre { ",
 | 
			
		||||
			   args->table_name);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, " . 0x%x };\n", from_peer->teid_local);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-post { %u };\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-pre-%u;\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-post-%u;\n",
 | 
			
		||||
			   args->table_name, from_peer->chain_id);
 | 
			
		||||
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
@@ -150,70 +379,129 @@ static int upf_nft_ruleset_tunmap_create_buf(char *buf, size_t buflen, const str
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
 | 
			
		||||
	/* Add a chain for this tunnel mapping */
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "add chain inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u { type filter hook prerouting priority %d; }\n",
 | 
			
		||||
			   args->table_name, args->chain_id, args->priority);
 | 
			
		||||
 | 
			
		||||
	/* Forwarding from peer_a to peer_b */
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_single_direction, args, &args->peer_a, &args->peer_b);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, true);
 | 
			
		||||
	/* And from peer_b to peer_a */
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_single_direction, args, &args->peer_b, &args->peer_a);
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, false);
 | 
			
		||||
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *upf_nft_ruleset_tunmap_create_c(void *ctx, const struct upf_nft_args *args)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 512, "ERROR", upf_nft_ruleset_tunmap_create_buf, args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int upf_nft_ruleset_tunmap_delete_buf(char *buf, size_t buflen, const struct upf_nft_args *args)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "delete chain inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u\n",
 | 
			
		||||
			   args->table_name, args->chain_id);
 | 
			
		||||
 | 
			
		||||
	/* Forwarding from peer_a to peer_b */
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, true);
 | 
			
		||||
	/* And from peer_b to peer_a */
 | 
			
		||||
	OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, false);
 | 
			
		||||
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *upf_nft_ruleset_tunmap_delete_c(void *ctx, const struct upf_nft_args *args)
 | 
			
		||||
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 64, "ERROR", upf_nft_ruleset_tunmap_delete_buf, args)
 | 
			
		||||
	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.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void upf_nft_args_from_tunmap_desc(struct upf_nft_args *args, 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(struct upf_nft_args *args, const struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	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,
 | 
			
		||||
		.chain_id = tunmap->id,
 | 
			
		||||
		.priority = g_upf->nft.priority,
 | 
			
		||||
		.table_name = g_upf->tunmap.table_name,
 | 
			
		||||
		.peer_a = {
 | 
			
		||||
			.addr = &tunmap->access.gtp_remote_addr,
 | 
			
		||||
			.teid_remote = tunmap->access.remote_teid,
 | 
			
		||||
			.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 = &tunmap->core.gtp_remote_addr,
 | 
			
		||||
			.teid_remote = tunmap->core.remote_teid,
 | 
			
		||||
			.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,
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_tunmap_create(struct upf_nft_tunmap_desc *tunmap)
 | 
			
		||||
char *upf_nft_tunmap_get_table_init_str(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_nft_args args;
 | 
			
		||||
 | 
			
		||||
	/* Give this tunnel mapping a new id, returned to the caller so that the tunnel mapping can be deleted later */
 | 
			
		||||
	g_upf->nft.next_id_state++;
 | 
			
		||||
	tunmap->id = g_upf->nft.next_id_state;
 | 
			
		||||
 | 
			
		||||
	upf_nft_args_from_tunmap_desc(&args, tunmap);
 | 
			
		||||
	return upf_nft_run(upf_nft_ruleset_tunmap_create_c(OTC_SELECT, &args));
 | 
			
		||||
	return upf_nft_ruleset_table_create(ctx, g_upf->tunmap.table_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_tunmap_delete(struct upf_nft_tunmap_desc *tunmap)
 | 
			
		||||
char *upf_nft_tunmap_get_vmap_init_str(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	return upf_nft_ruleset_vmap_init(ctx, g_upf->tunmap.table_name, g_upf->tunmap.priority_pre,
 | 
			
		||||
					 g_upf->tunmap.priority_post);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_tunmap_get_ruleset_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_nft_args args;
 | 
			
		||||
	upf_nft_args_from_tunmap_desc(&args, tunmap);
 | 
			
		||||
	return upf_nft_run(upf_nft_ruleset_tunmap_delete_c(OTC_SELECT, &args));
 | 
			
		||||
	upf_nft_args_from_tunmap(&args, tunmap);
 | 
			
		||||
	return upf_nft_ruleset_tunmap_create_buf(buf, len, &args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_str_buf, tunmap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_tunmap_get_ruleset_del_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	struct upf_nft_args args;
 | 
			
		||||
	upf_nft_args_from_tunmap(&args, tunmap);
 | 
			
		||||
	return upf_nft_ruleset_tunmap_delete_buf(buf, len, &args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_del_str_buf, 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;
 | 
			
		||||
	hash_add(g_upf->tunmap.nft_tun_by_chain_id, &tun->node_by_chain_id, tun->chain_id);
 | 
			
		||||
	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 nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_str_buf, tunmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap)
 | 
			
		||||
{
 | 
			
		||||
	return nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_del_str_buf, tunmap);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,11 +37,14 @@
 | 
			
		||||
#include <osmocom/upf/up_peer.h>
 | 
			
		||||
#include <osmocom/upf/up_session.h>
 | 
			
		||||
#include <osmocom/upf/up_gtp_action.h>
 | 
			
		||||
#include <osmocom/upf/netinst.h>
 | 
			
		||||
#include <osmocom/upf/upf_gtpu_echo.h>
 | 
			
		||||
 | 
			
		||||
enum upf_vty_node {
 | 
			
		||||
	PFCP_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
	GTP_NODE,
 | 
			
		||||
	NFT_NODE,
 | 
			
		||||
	TUNEND_NODE,
 | 
			
		||||
	TUNMAP_NODE,
 | 
			
		||||
	NETINST_NODE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cmd_node cfg_pfcp_node = {
 | 
			
		||||
@@ -51,7 +54,7 @@ static struct cmd_node cfg_pfcp_node = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define pfcp_vty (g_upf->pfcp.vty_cfg)
 | 
			
		||||
#define gtp_vty (g_upf->gtp.vty_cfg)
 | 
			
		||||
#define tunend_vty (g_upf->tunend.vty_cfg)
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pfcp, cfg_pfcp_cmd,
 | 
			
		||||
      "pfcp",
 | 
			
		||||
@@ -78,29 +81,32 @@ DEFUN(cfg_pfcp_local_addr, cfg_pfcp_local_addr_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cmd_node cfg_gtp_node = {
 | 
			
		||||
	GTP_NODE,
 | 
			
		||||
	"%s(config-gtp)# ",
 | 
			
		||||
static struct cmd_node cfg_tunend_node = {
 | 
			
		||||
	TUNEND_NODE,
 | 
			
		||||
	"%s(config-tunend)# ",
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp, cfg_gtp_cmd,
 | 
			
		||||
      "gtp",
 | 
			
		||||
      "Enter the 'gtp' node to configure Linux GTP kernel module usage\n")
 | 
			
		||||
#define TUNEND_NODE_STR "Enter the 'tunend' node to configure Linux GTP kernel module usage\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_tunend, cfg_tunend_cmd, "tunend", TUNEND_NODE_STR)
 | 
			
		||||
{
 | 
			
		||||
	vty->node = GTP_NODE;
 | 
			
		||||
	vty->node = TUNEND_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_gtp(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct gtp_vty_cfg_dev *d;
 | 
			
		||||
	vty_out(vty, "gtp%s", VTY_NEWLINE);
 | 
			
		||||
/* legacy compat: "tunend" was originally named "gtp" */
 | 
			
		||||
DEFUN_CMD_ELEMENT(cfg_tunend, cfg_gtp_cmd, "gtp", TUNEND_NODE_STR, CMD_ATTR_HIDDEN, 0);
 | 
			
		||||
 | 
			
		||||
	if (g_upf->gtp.mockup)
 | 
			
		||||
static int config_write_tunend(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct tunend_vty_cfg_dev *d;
 | 
			
		||||
	vty_out(vty, "tunend%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (g_upf->tunend.mockup)
 | 
			
		||||
		vty_out(vty, " mockup%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(d, >p_vty.devs, entry) {
 | 
			
		||||
	llist_for_each_entry(d, &tunend_vty.devs, entry) {
 | 
			
		||||
		if (d->create) {
 | 
			
		||||
			vty_out(vty, " dev create %s", d->dev_name);
 | 
			
		||||
			if (d->local_addr)
 | 
			
		||||
@@ -115,69 +121,76 @@ static int config_write_gtp(struct vty *vty)
 | 
			
		||||
 | 
			
		||||
#define DEV_STR "Configure the GTP device to use for encaps/decaps.\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp_mockup, cfg_gtp_mockup_cmd,
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp_no_mockup, cfg_gtp_no_mockup_cmd,
 | 
			
		||||
DEFUN(cfg_tunend_no_mockup, cfg_tunend_no_mockup_cmd,
 | 
			
		||||
      "no mockup",
 | 
			
		||||
      NO_STR
 | 
			
		||||
      "operate GTP kernel module normally\n")
 | 
			
		||||
{
 | 
			
		||||
	g_upf->gtp.mockup = false;
 | 
			
		||||
	g_upf->tunend.mockup = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp_dev_create, cfg_gtp_dev_create_cmd,
 | 
			
		||||
static struct tunend_vty_cfg_dev *tunend_dev_add(int argc, const char **argv, bool create)
 | 
			
		||||
{
 | 
			
		||||
	struct tunend_vty_cfg_dev *d = talloc_zero(g_upf, struct tunend_vty_cfg_dev);
 | 
			
		||||
	d->create = create;
 | 
			
		||||
	d->dev_name = talloc_strdup(d, argv[0]);
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		d->local_addr = talloc_strdup(d, argv[1]);
 | 
			
		||||
	llist_add(&d->entry, &tunend_vty.devs);
 | 
			
		||||
	return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_tunend_dev_create, cfg_tunend_dev_create_cmd,
 | 
			
		||||
      "dev create DEVNAME [LISTEN_ADDR]",
 | 
			
		||||
      DEV_STR
 | 
			
		||||
      "Add GTP device, creating a new Linux kernel GTP device. Will listen on GTPv1 port "
 | 
			
		||||
      OSMO_STRINGIFY_VAL(PORT_GTP1_U)
 | 
			
		||||
      " and GTPv0 port " OSMO_STRINGIFY_VAL(PORT_GTP0_U) " on the specified interface, or on ANY if LISTEN_ADDR is"
 | 
			
		||||
      " omitted.\n"
 | 
			
		||||
      " and GTPv0 port " OSMO_STRINGIFY_VAL(PORT_GTP0_U) " on the specified LISTEN_ADDR\n"
 | 
			
		||||
      "device name, e.g. 'apn0'\n"
 | 
			
		||||
      "IPv4 or IPv6 address to listen on, omit for ANY\n")
 | 
			
		||||
      "IPv4 or IPv6 address to listen on, omit for ANY. LISTEN_ADDR is used to pick a GTP device matching the local"
 | 
			
		||||
      " address for a PFCP Network Instance, which are configured in the 'netinst' node.\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gtp_vty_cfg_dev *d = talloc_zero(g_upf, struct gtp_vty_cfg_dev);
 | 
			
		||||
	d->create = true;
 | 
			
		||||
	d->dev_name = talloc_strdup(d, argv[0]);
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		d->local_addr = talloc_strdup(d, argv[1]);
 | 
			
		||||
	llist_add(&d->entry, >p_vty.devs);
 | 
			
		||||
	vty_out(vty, "Added GTP device %s (create new)%s", d->dev_name, VTY_NEWLINE);
 | 
			
		||||
	struct tunend_vty_cfg_dev *d = tunend_dev_add(argc, argv, true);
 | 
			
		||||
	vty_out(vty, "Added GTP device %s on %s (create new)%s", d->dev_name, d->local_addr ? : "0.0.0.0", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp_dev_use, cfg_gtp_dev_use_cmd,
 | 
			
		||||
      "dev use DEVNAME",
 | 
			
		||||
DEFUN(cfg_tunend_dev_use, cfg_tunend_dev_use_cmd,
 | 
			
		||||
      "dev use DEVNAME [LOCAL_ADDR]",
 | 
			
		||||
      DEV_STR
 | 
			
		||||
      "Add GTP device, using an existing Linux kernel GTP device, e.g. created by 'gtp-link'\n"
 | 
			
		||||
      "device name, e.g. 'apn0'\n")
 | 
			
		||||
      "device name, e.g. 'apn0'\n"
 | 
			
		||||
      "The local GTP address this device listens on. It is assumed to be ANY when omitted."
 | 
			
		||||
      " LOCAL_ADDR is used to pick a GTP device matching the local address for a PFCP Network Instance,"
 | 
			
		||||
      " which are configured in the 'netinst' node.\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gtp_vty_cfg_dev *d = talloc_zero(g_upf, struct gtp_vty_cfg_dev);
 | 
			
		||||
	d->create = false;
 | 
			
		||||
	d->dev_name = talloc_strdup(d, argv[0]);
 | 
			
		||||
	llist_add(&d->entry, >p_vty.devs);
 | 
			
		||||
	vty_out(vty, "Added GTP device %s (use existing)%s", d->dev_name, VTY_NEWLINE);
 | 
			
		||||
	struct tunend_vty_cfg_dev *d = tunend_dev_add(argc, argv, false);
 | 
			
		||||
	vty_out(vty, "Added GTP device %s on %s (use existing)%s", d->dev_name, d->local_addr ? : "0.0.0.0",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gtp_dev_del, cfg_gtp_dev_del_cmd,
 | 
			
		||||
DEFUN(cfg_tunend_dev_del, cfg_tunend_dev_del_cmd,
 | 
			
		||||
      "dev delete DEVNAME",
 | 
			
		||||
      DEV_STR
 | 
			
		||||
      "Remove a GTP device from the configuration, and delete the Linux kernel GTP device if it was created here.\n"
 | 
			
		||||
      "device name, e.g. 'apn0'\n")
 | 
			
		||||
{
 | 
			
		||||
	const char *dev_name = argv[0];
 | 
			
		||||
	struct gtp_vty_cfg_dev *d;
 | 
			
		||||
	struct tunend_vty_cfg_dev *d;
 | 
			
		||||
	struct upf_gtp_dev *dev;
 | 
			
		||||
 | 
			
		||||
	/* remove from VTY cfg */
 | 
			
		||||
	llist_for_each_entry(d, >p_vty.devs, entry) {
 | 
			
		||||
	llist_for_each_entry(d, &tunend_vty.devs, entry) {
 | 
			
		||||
		if (strcmp(d->dev_name, dev_name))
 | 
			
		||||
			continue;
 | 
			
		||||
		llist_del(&d->entry);
 | 
			
		||||
@@ -191,57 +204,197 @@ DEFUN(cfg_gtp_dev_del, cfg_gtp_dev_del_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cmd_node cfg_nft_node = {
 | 
			
		||||
	NFT_NODE,
 | 
			
		||||
	"%s(config-nft)# ",
 | 
			
		||||
static struct cmd_node cfg_tunmap_node = {
 | 
			
		||||
	TUNMAP_NODE,
 | 
			
		||||
	"%s(config-tunmap)# ",
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_nft, cfg_nft_cmd,
 | 
			
		||||
      "nft",
 | 
			
		||||
      "Enter the 'nft' node to configure nftables usage\n")
 | 
			
		||||
#define TUNMAP_NODE_STR "Enter the 'tunmap' node to configure nftables usage\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_tunmap, cfg_tunmap_cmd, "tunmap", TUNMAP_NODE_STR)
 | 
			
		||||
{
 | 
			
		||||
	vty->node = NFT_NODE;
 | 
			
		||||
	vty->node = TUNMAP_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_nft(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "nft%s", VTY_NEWLINE);
 | 
			
		||||
/* legacy compat: "tunmap" was originally named "nft" */
 | 
			
		||||
DEFUN_CMD_ELEMENT(cfg_tunmap, cfg_nft_cmd, "nft", TUNMAP_NODE_STR, CMD_ATTR_HIDDEN, 0);
 | 
			
		||||
 | 
			
		||||
	if (g_upf->nft.mockup)
 | 
			
		||||
static int config_write_tunmap(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "tunmap%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_nft_mockup, cfg_nft_mockup_cmd,
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_nft_no_mockup, cfg_nft_no_mockup_cmd,
 | 
			
		||||
DEFUN(cfg_tunmap_no_mockup, cfg_tunmap_no_mockup_cmd,
 | 
			
		||||
      "no mockup",
 | 
			
		||||
      NO_STR
 | 
			
		||||
      "operate nftables rulesets normally\n")
 | 
			
		||||
{
 | 
			
		||||
	g_upf->nft.mockup = false;
 | 
			
		||||
	g_upf->tunmap.mockup = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_nft_table_name, cfg_nft_table_name_cmd,
 | 
			
		||||
DEFUN(cfg_tunmap_table_name, cfg_tunmap_table_name_cmd,
 | 
			
		||||
      "table-name TABLE_NAME",
 | 
			
		||||
      "Set the nft inet table name to create and place GTP tunnel forwarding chains in"
 | 
			
		||||
      " (as in 'nft add table inet foo'). If multiple instances of osmo-upf are running on the same system, each"
 | 
			
		||||
      " osmo-upf must have its own table name. Otherwise the names of created forwarding chains will collide.\n"
 | 
			
		||||
      " osmo-upf must have its own table name. Otherwise the names of created forwarding chains will collide."
 | 
			
		||||
      " 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NFT_RULE_STR "nftables rule specifics\n"
 | 
			
		||||
#define TUNMAP_STR "GTP tunmap use case (a.k.a. forwarding between two GTP tunnels)\n"
 | 
			
		||||
#define TUNMAP_APPEND_STR "'tunmap append' feature is no longer available.\n"
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_tunmap_nft_rule_append, cfg_tunmap_nft_rule_append_cmd,
 | 
			
		||||
      "nft-rule tunmap append .NFT_RULE",
 | 
			
		||||
      NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR TUNMAP_APPEND_STR)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% deprecated config option: 'nft-rule tunmap append'%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_tunmap_no_nft_rule_append, cfg_tunmap_no_nft_rule_append_cmd,
 | 
			
		||||
      "no nft-rule tunmap append",
 | 
			
		||||
      NO_STR NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR TUNMAP_APPEND_STR)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% deprecated config option: 'no nft-rule tunmap append'%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(show_nft_rule_append, show_nft_rule_append_cmd,
 | 
			
		||||
      "show nft-rule tunmap append",
 | 
			
		||||
      SHOW_STR NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% deprecated config option: 'show nft-rule tunmap append'%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_nft_rule_tunmap_example, show_nft_rule_tunmap_example_cmd,
 | 
			
		||||
      "show nft-rule tunmap example",
 | 
			
		||||
      SHOW_STR NFT_RULE_STR TUNMAP_STR
 | 
			
		||||
      "Print a complete nftables ruleset for a tunmap filled with example IP addresses and TEIDs\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sockaddr_str str = {};
 | 
			
		||||
	struct upf_tunmap tunmap = {
 | 
			
		||||
		.access = {
 | 
			
		||||
			.tun = {
 | 
			
		||||
				.local.teid = 0x201,
 | 
			
		||||
				.remote.teid = 0x102,
 | 
			
		||||
			},
 | 
			
		||||
			.chain_id = 123,
 | 
			
		||||
		},
 | 
			
		||||
		.core = {
 | 
			
		||||
			.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, &tunmap.access.tun.remote.addr.u.sas);
 | 
			
		||||
 | 
			
		||||
	osmo_sockaddr_str_from_str2(&str, "2.2.2.1");
 | 
			
		||||
	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, &tunmap.core.tun.local.addr.u.sas);
 | 
			
		||||
 | 
			
		||||
	osmo_sockaddr_str_from_str2(&str, "3.3.3.3");
 | 
			
		||||
	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, &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, &tunmap), VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cmd_node cfg_netinst_node = {
 | 
			
		||||
	NETINST_NODE,
 | 
			
		||||
	"%s(config-netinst)# ",
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_netinst, cfg_netinst_cmd,
 | 
			
		||||
      "netinst",
 | 
			
		||||
      "Enter the Network Instance configuration node\n")
 | 
			
		||||
{
 | 
			
		||||
	vty->node = NETINST_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_netinst(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "netinst%s", VTY_NEWLINE);
 | 
			
		||||
	netinst_vty_write(vty, &g_upf->netinst, " ", NULL);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_netinst_add, cfg_netinst_add_cmd,
 | 
			
		||||
      "add NAME ADDR",
 | 
			
		||||
      "add Network Instance: associate a PFCP Network Instance name with a local IP address\n"
 | 
			
		||||
      "Network Instance name as received in PFCP Network Instance IE\n"
 | 
			
		||||
      "IP address of a local interface\n")
 | 
			
		||||
{
 | 
			
		||||
	const char *errmsg;
 | 
			
		||||
	if (!netinst_add(g_upf, &g_upf->netinst, argv[0], argv[1], &errmsg)) {
 | 
			
		||||
		vty_out(vty, "%% Error: netinst: cannot add %s %s: %s%s", argv[0], argv[1],
 | 
			
		||||
			errmsg ? : "(unknown error)", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_netinst, show_netinst_cmd,
 | 
			
		||||
      "show netinst [NAME]",
 | 
			
		||||
      SHOW_STR "List configured Network Instance entries\n"
 | 
			
		||||
      "Show the Network Instance with this name (show all when omitted)\n")
 | 
			
		||||
{
 | 
			
		||||
	const char *name_or_null = argc > 0 ? argv[0] : NULL;
 | 
			
		||||
 | 
			
		||||
	if (!netinst_vty_write(vty, &g_upf->netinst, " ", name_or_null)) {
 | 
			
		||||
		if (name_or_null)
 | 
			
		||||
			vty_out(vty, "%% No such Network Instance entry%s", VTY_NEWLINE);
 | 
			
		||||
		else
 | 
			
		||||
			vty_out(vty, "%% No Network Instance entries configured%s", VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_netinst_clear, cfg_netinst_clear_cmd,
 | 
			
		||||
      "clear",
 | 
			
		||||
      "Remove all Network Instance entries\n")
 | 
			
		||||
{
 | 
			
		||||
	int count = netinst_clear(&g_upf->netinst);
 | 
			
		||||
	vty_out(vty, "netinst entries removed: %d%s", count, VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -282,16 +435,11 @@ DEFUN(show_pdr, show_pdr_cmd,
 | 
			
		||||
DEFUN(show_gtp, show_gtp_cmd,
 | 
			
		||||
      "show gtp",
 | 
			
		||||
      SHOW_STR
 | 
			
		||||
      "Active GTP tunnels and forwardings\n")
 | 
			
		||||
      "Active GTP tunnels, both tunend and tunmap\n")
 | 
			
		||||
{
 | 
			
		||||
	struct up_peer *peer;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
 | 
			
		||||
	if (!upf_gtp_dev_first()) {
 | 
			
		||||
		vty_out(vty, "No GTP device open%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
 | 
			
		||||
		struct up_session *session;
 | 
			
		||||
		int bkt;
 | 
			
		||||
@@ -334,11 +482,110 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* variant:
 | 
			
		||||
 *  0 "gtp1u-echo send to (A.B.C.D|X:X::X:X)"
 | 
			
		||||
 *  1 "gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X)"
 | 
			
		||||
 *  2 "gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME"
 | 
			
		||||
 */
 | 
			
		||||
static int _gtp_echo_tx(struct vty *vty, int variant, int argc, const char **argv)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sockaddr_str addr;
 | 
			
		||||
	struct osmo_sockaddr osa_remote;
 | 
			
		||||
	struct osmo_sockaddr osa_local;
 | 
			
		||||
	struct upf_gtp_dev *gtp_dev = NULL;
 | 
			
		||||
	const char *remote_str = argv[0];
 | 
			
		||||
	const char *local_str = NULL;
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		local_str = argv[1];
 | 
			
		||||
 | 
			
		||||
	/* GTP can be received on port 2152 only, i.e. the remote port must be 2152. (The sending port is allowed to
 | 
			
		||||
	 * differ). */
 | 
			
		||||
	if (osmo_sockaddr_str_from_str(&addr, remote_str, 2152)
 | 
			
		||||
	    || osmo_sockaddr_str_to_osa(&addr, &osa_remote)) {
 | 
			
		||||
		vty_out(vty, "%% Error: cannot send Echo: invalid IP address: %s%s",
 | 
			
		||||
			osmo_quote_str(remote_str, -1), VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (variant) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		gtp_dev = llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry);
 | 
			
		||||
		if (!gtp_dev) {
 | 
			
		||||
			vty_out(vty, "%% Error: cannot send Echo: there is no GTP device%s",
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 1:
 | 
			
		||||
		if (osmo_sockaddr_str_from_str(&addr, local_str, 2152)
 | 
			
		||||
		    || osmo_sockaddr_str_to_osa(&addr, &osa_local)) {
 | 
			
		||||
			vty_out(vty, "%% Error: cannot send Echo: invalid IP address: %s%s",
 | 
			
		||||
				osmo_quote_str(local_str, -1), VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		gtp_dev = upf_gtp_dev_find_by_local_addr(&osa_local);
 | 
			
		||||
		if (!gtp_dev) {
 | 
			
		||||
			vty_out(vty, "%% Error: cannot send Echo: this does not seem to be a locally bound GTP address: %s%s",
 | 
			
		||||
				osmo_sockaddr_to_str_c(OTC_SELECT, &osa_local), VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		gtp_dev = upf_gtp_dev_find_by_name(local_str);
 | 
			
		||||
		if (!gtp_dev) {
 | 
			
		||||
			vty_out(vty, "%% Error: cannot send Echo: there is no GTP device by the name of '%s'%s",
 | 
			
		||||
				local_str, VTY_NEWLINE);
 | 
			
		||||
			return CMD_WARNING;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(gtp_dev);
 | 
			
		||||
 | 
			
		||||
	if (upf_gtpu_echo_req_tx(gtp_dev, &osa_remote, g_upf->gtp.next_echo_seq_nr++)) {
 | 
			
		||||
		vty_out(vty, "%% Error: Failed to transmit Echo Request (see DGTP logging)%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	vty_out(vty, "%s -> %s tx Echo Request; for responses, see DGTP logging level INFO%s",
 | 
			
		||||
		gtp_dev->name, osmo_sockaddr_to_str_c(OTC_SELECT, &osa_remote), VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define IP46_STR "IPv4 address\nIPv6 address\n"
 | 
			
		||||
#define GTP_ECHO_TX_STR \
 | 
			
		||||
      "GTP1-U Echo probing\n" \
 | 
			
		||||
      "Send a GTP1-U Echo Request to a remote peer\n" \
 | 
			
		||||
      "Send to remote peer's GTP address\n" IP46_STR
 | 
			
		||||
 | 
			
		||||
DEFUN(gtp_echo_tx, gtp_echo_tx_cmd,
 | 
			
		||||
      "gtp1u-echo send to " VTY_IPV46_CMD,
 | 
			
		||||
      GTP_ECHO_TX_STR)
 | 
			
		||||
{
 | 
			
		||||
	return _gtp_echo_tx(vty, 0, argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(gtp_echo_tx_local_ip, gtp_echo_tx_local_ip_cmd,
 | 
			
		||||
      "gtp1u-echo send to " VTY_IPV46_CMD " local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      GTP_ECHO_TX_STR
 | 
			
		||||
      "Send from local GTP device, chosen by IP address\n"
 | 
			
		||||
      IP46_STR)
 | 
			
		||||
{
 | 
			
		||||
	return _gtp_echo_tx(vty, 1, argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(gtp_echo_tx_local_dev, gtp_echo_tx_local_dev_cmd,
 | 
			
		||||
      "gtp1u-echo send to " VTY_IPV46_CMD " local-dev DEV_NAME",
 | 
			
		||||
      GTP_ECHO_TX_STR
 | 
			
		||||
      "Send from local GTP device, chosen by name as configured in 'dev create' or 'dev use'.\n"
 | 
			
		||||
      "A GTP device name as it appears in the cfg\n")
 | 
			
		||||
{
 | 
			
		||||
	return _gtp_echo_tx(vty, 2, argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upf_vty_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(g_upf != NULL);
 | 
			
		||||
@@ -346,25 +593,43 @@ void upf_vty_init()
 | 
			
		||||
	install_element_ve(&show_pdr_cmd);
 | 
			
		||||
	install_element_ve(&show_gtp_cmd);
 | 
			
		||||
	install_element_ve(&show_session_cmd);
 | 
			
		||||
	install_element_ve(&show_netinst_cmd);
 | 
			
		||||
	install_element_ve(&show_nft_rule_append_cmd);
 | 
			
		||||
	install_element_ve(>p_echo_tx_cmd);
 | 
			
		||||
	install_element_ve(>p_echo_tx_local_ip_cmd);
 | 
			
		||||
	install_element_ve(>p_echo_tx_local_dev_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&cfg_pfcp_node, config_write_pfcp);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_pfcp_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(PFCP_NODE, &cfg_pfcp_local_addr_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&cfg_gtp_node, config_write_gtp);
 | 
			
		||||
	install_node(&cfg_tunend_node, config_write_tunend);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_tunend_cmd);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_gtp_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(GTP_NODE, &cfg_gtp_mockup_cmd);
 | 
			
		||||
	install_element(GTP_NODE, &cfg_gtp_no_mockup_cmd);
 | 
			
		||||
	install_element(GTP_NODE, &cfg_gtp_dev_create_cmd);
 | 
			
		||||
	install_element(GTP_NODE, &cfg_gtp_dev_use_cmd);
 | 
			
		||||
	install_element(GTP_NODE, &cfg_gtp_dev_del_cmd);
 | 
			
		||||
	install_element(TUNEND_NODE, &cfg_tunend_mockup_cmd);
 | 
			
		||||
	install_element(TUNEND_NODE, &cfg_tunend_no_mockup_cmd);
 | 
			
		||||
	install_element(TUNEND_NODE, &cfg_tunend_dev_create_cmd);
 | 
			
		||||
	install_element(TUNEND_NODE, &cfg_tunend_dev_use_cmd);
 | 
			
		||||
	install_element(TUNEND_NODE, &cfg_tunend_dev_del_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&cfg_nft_node, config_write_nft);
 | 
			
		||||
	install_node(&cfg_tunmap_node, config_write_tunmap);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_tunmap_cmd);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_nft_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(NFT_NODE, &cfg_nft_mockup_cmd);
 | 
			
		||||
	install_element(NFT_NODE, &cfg_nft_no_mockup_cmd);
 | 
			
		||||
	install_element(NFT_NODE, &cfg_nft_table_name_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &cfg_tunmap_mockup_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &cfg_tunmap_no_mockup_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &cfg_tunmap_table_name_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &cfg_tunmap_nft_rule_append_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &cfg_tunmap_no_nft_rule_append_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &show_nft_rule_append_cmd);
 | 
			
		||||
	install_element(TUNMAP_NODE, &show_nft_rule_tunmap_example_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&cfg_netinst_node, config_write_netinst);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_netinst_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(NETINST_NODE, &cfg_netinst_clear_cmd);
 | 
			
		||||
	install_element(NETINST_NODE, &cfg_netinst_add_cmd);
 | 
			
		||||
	install_element(NETINST_NODE, &show_netinst_cmd);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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) \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								tests/gtp-echo.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/gtp-echo.vty
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
OsmoUPF> list
 | 
			
		||||
...
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X)
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X)
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME
 | 
			
		||||
...
 | 
			
		||||
OsmoUPF> enable
 | 
			
		||||
OsmoUPF# list
 | 
			
		||||
...
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X)
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X)
 | 
			
		||||
  gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME
 | 
			
		||||
...
 | 
			
		||||
OsmoUPF# configure terminal
 | 
			
		||||
OsmoUPF(config)# list
 | 
			
		||||
... !gtp1u-echo
 | 
			
		||||
OsmoUPF(config)# end
 | 
			
		||||
 | 
			
		||||
OsmoUPF# gtp1u-echo?
 | 
			
		||||
  gtp1u-echo  GTP1-U Echo probing
 | 
			
		||||
OsmoUPF# gtp1u-echo ?
 | 
			
		||||
  send  Send a GTP1-U Echo Request to a remote peer
 | 
			
		||||
OsmoUPF# gtp1u-echo send ?
 | 
			
		||||
  to  Send to remote peer's GTP address
 | 
			
		||||
OsmoUPF# gtp1u-echo send to ?
 | 
			
		||||
  A.B.C.D   IPv4 address
 | 
			
		||||
  X:X::X:X  IPv6 address
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4 ?
 | 
			
		||||
  local-ip   Send from local GTP device, chosen by IP address
 | 
			
		||||
  local-dev  Send from local GTP device, chosen by name as configured in 'dev create' or 'dev use'.
 | 
			
		||||
  <cr>       
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4 local-ip ?
 | 
			
		||||
  A.B.C.D   IPv4 address
 | 
			
		||||
  X:X::X:X  IPv6 address
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4 local-dev ?
 | 
			
		||||
  DEV_NAME  A GTP device name as it appears in the cfg
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4
 | 
			
		||||
% Error: cannot send Echo: there is no GTP device
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4 local-ip 1.2.3.4
 | 
			
		||||
% Error: cannot send Echo: this does not seem to be a locally bound GTP address: 1.2.3.4:2152
 | 
			
		||||
OsmoUPF# gtp1u-echo send to 1.2.3.4 local-dev apn0
 | 
			
		||||
% Error: cannot send Echo: there is no GTP device by the name of 'apn0'
 | 
			
		||||
							
								
								
									
										80
									
								
								tests/netinst.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								tests/netinst.vty
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
OsmoUPF> show ?
 | 
			
		||||
...
 | 
			
		||||
  netinst         List configured Network Instance entries
 | 
			
		||||
...
 | 
			
		||||
OsmoUPF> show netinst?
 | 
			
		||||
  netinst  List configured Network Instance entries
 | 
			
		||||
OsmoUPF> show netinst ?
 | 
			
		||||
  [NAME]  Show the Network Instance with this name (show all when omitted)
 | 
			
		||||
 | 
			
		||||
OsmoUPF> show netinst
 | 
			
		||||
% No Network Instance entries configured
 | 
			
		||||
OsmoUPF> show netinst foo
 | 
			
		||||
% No such Network Instance entry
 | 
			
		||||
 | 
			
		||||
OsmoUPF> enable
 | 
			
		||||
 | 
			
		||||
OsmoUPF# show netinst
 | 
			
		||||
% No Network Instance entries configured
 | 
			
		||||
 | 
			
		||||
OsmoUPF# configure terminal
 | 
			
		||||
OsmoUPF(config)# netinst
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# list
 | 
			
		||||
...
 | 
			
		||||
  clear
 | 
			
		||||
  add NAME ADDR
 | 
			
		||||
  show netinst [NAME]
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# clear?
 | 
			
		||||
  clear  Remove all Network Instance entries
 | 
			
		||||
OsmoUPF(config-netinst)# clear ?
 | 
			
		||||
  <cr>  
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# add?
 | 
			
		||||
  add  add Network Instance: associate a PFCP Network Instance name with a local IP address
 | 
			
		||||
OsmoUPF(config-netinst)# add ?
 | 
			
		||||
  NAME  Network Instance name as received in PFCP Network Instance IE
 | 
			
		||||
OsmoUPF(config-netinst)# add foo ?
 | 
			
		||||
  ADDR  IP address of a local interface
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# add foo bar
 | 
			
		||||
% Error: netinst: cannot add foo bar: Network Instance address is not a valid IP address string
 | 
			
		||||
OsmoUPF(config-netinst)# add foo 1.2.3.4
 | 
			
		||||
OsmoUPF(config-netinst)# add foo 2.3.4.5
 | 
			
		||||
% Error: netinst: cannot add foo 2.3.4.5: Network Instance entry with this name already exists
 | 
			
		||||
OsmoUPF(config-netinst)# add bar 2.3.4.5
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst
 | 
			
		||||
 add foo 1.2.3.4
 | 
			
		||||
 add bar 2.3.4.5
 | 
			
		||||
OsmoUPF(config-netinst)# add baz 1:2:3:4::0
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst
 | 
			
		||||
 add foo 1.2.3.4
 | 
			
		||||
 add bar 2.3.4.5
 | 
			
		||||
 add baz 1:2:3:4::0
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst foo
 | 
			
		||||
 add foo 1.2.3.4
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst bar
 | 
			
		||||
 add bar 2.3.4.5
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst baz
 | 
			
		||||
 add baz 1:2:3:4::0
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# show running-config
 | 
			
		||||
...
 | 
			
		||||
netinst
 | 
			
		||||
 add foo 1.2.3.4
 | 
			
		||||
 add bar 2.3.4.5
 | 
			
		||||
 add baz 1:2:3:4::0
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# clear
 | 
			
		||||
netinst entries removed: 3
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst
 | 
			
		||||
% No Network Instance entries configured
 | 
			
		||||
OsmoUPF(config-netinst)# clear
 | 
			
		||||
netinst entries removed: 0
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst?
 | 
			
		||||
  netinst  List configured Network Instance entries
 | 
			
		||||
OsmoUPF(config-netinst)# show netinst ?
 | 
			
		||||
  [NAME]  Show the Network Instance with this name (show all when omitted)
 | 
			
		||||
							
								
								
									
										46
									
								
								tests/nft-rule.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								tests/nft-rule.vty
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
OsmoUPF> enable
 | 
			
		||||
OsmoUPF# configure terminal
 | 
			
		||||
OsmoUPF(config)# tunmap
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# show nft-rule tunmap example
 | 
			
		||||
% init verdict map:
 | 
			
		||||
add table inet osmo-upf { flags owner; };
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
% add tunmap:
 | 
			
		||||
% ACCESS 1.1.1.1:0x102 <---> 2.2.2.1:0x201 UPF 2.2.2.3:0x203 <---> 3.3.3.3:0x302 CORE
 | 
			
		||||
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 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 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 };
 | 
			
		||||
 | 
			
		||||
% delete tunmap:
 | 
			
		||||
delete element inet osmo-upf tunmap-pre { 2.2.2.1 . 0x201 };
 | 
			
		||||
delete element inet osmo-upf tunmap-post { 123 };
 | 
			
		||||
delete chain inet osmo-upf tunmap-pre-123;
 | 
			
		||||
delete chain inet osmo-upf tunmap-post-123;
 | 
			
		||||
delete element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x203 };
 | 
			
		||||
delete element inet osmo-upf tunmap-post { 321 };
 | 
			
		||||
delete chain inet osmo-upf tunmap-pre-321;
 | 
			
		||||
delete chain inet osmo-upf tunmap-post-321;
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# show nft-rule tunmap append
 | 
			
		||||
% deprecated config option: 'show nft-rule tunmap append'
 | 
			
		||||
OsmoUPF(config-tunmap)# nft-rule tunmap append meta nftrace set 1
 | 
			
		||||
% deprecated config option: 'nft-rule tunmap append'
 | 
			
		||||
OsmoUPF(config-tunmap)# no nft-rule tunmap append
 | 
			
		||||
% deprecated config option: 'no nft-rule tunmap append'
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										436
									
								
								tests/unique_ids/unique_ids_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								tests/unique_ids/unique_ids_test.err
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,436 @@
 | 
			
		||||
 | 
			
		||||
===== 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 ran nft ruleset, 42 chars: "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 ran nft ruleset, 465 chars: "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;..."
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 1 avg rules per flush: 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 ran nft ruleset, 847 chars: "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..."
 | 
			
		||||
DGTP INFO 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 INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 2 avg rules per flush: 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 ran nft ruleset, 847 chars: "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..."
 | 
			
		||||
DGTP INFO 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 INFO 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 INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:381
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:381 (flush count: 3 avg rules per flush: 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 ran nft ruleset, 381 chars: "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 ..."
 | 
			
		||||
DGTP INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 4 avg rules per flush: 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 ran nft ruleset, 847 chars: "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..."
 | 
			
		||||
DGTP INFO 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 INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 5 avg rules per flush: 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 ran nft ruleset, 847 chars: "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..."
 | 
			
		||||
DGTP INFO 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 INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:381
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:381 (flush count: 6 avg rules per flush: 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 ran nft ruleset, 381 chars: "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 ..."
 | 
			
		||||
DGTP INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:381
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:381 (flush count: 7 avg rules per flush: 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 ran nft ruleset, 381 chars: "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 ..."
 | 
			
		||||
DGTP INFO 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
 | 
			
		||||
DNFT INFO Added NFT ruleset to queue: n:1 strlen:381
 | 
			
		||||
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:381 (flush count: 8 avg rules per flush: 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 ran nft ruleset, 381 chars: "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 ..."
 | 
			
		||||
DGTP INFO 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
									
								
							@@ -13,23 +13,86 @@ OsmoUPF(config-pfcp)# local-addr ?
 | 
			
		||||
  IP_ADDR  IP address
 | 
			
		||||
OsmoUPF(config-pfcp)# exit
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config)# # ensure its old name "gtp" enters the tunend node
 | 
			
		||||
OsmoUPF(config)# gtp
 | 
			
		||||
OsmoUPF(config-gtp)# list
 | 
			
		||||
OsmoUPF(config-tunend)# list
 | 
			
		||||
...
 | 
			
		||||
  mockup
 | 
			
		||||
  no mockup
 | 
			
		||||
  dev create DEVNAME [LISTEN_ADDR]
 | 
			
		||||
  dev use DEVNAME
 | 
			
		||||
  dev use DEVNAME [LOCAL_ADDR]
 | 
			
		||||
  dev delete DEVNAME
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-gtp)# dev?
 | 
			
		||||
OsmoUPF(config-tunend)# exit
 | 
			
		||||
OsmoUPF(config)# tunend
 | 
			
		||||
OsmoUPF(config-tunend)# list
 | 
			
		||||
...
 | 
			
		||||
  dev create DEVNAME [LISTEN_ADDR]
 | 
			
		||||
  dev use DEVNAME [LOCAL_ADDR]
 | 
			
		||||
  dev delete DEVNAME
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunend)# dev?
 | 
			
		||||
  dev  Configure the GTP device to use for encaps/decaps.
 | 
			
		||||
OsmoUPF(config-gtp)# dev ?
 | 
			
		||||
  create  Add GTP device, creating a new Linux kernel GTP device. Will listen on GTPv1 port 2152 and GTPv0 port 3386 on the specified interface, or on ANY if LISTEN_ADDR is omitted.
 | 
			
		||||
OsmoUPF(config-tunend)# dev ?
 | 
			
		||||
  create  Add GTP device, creating a new Linux kernel GTP device. Will listen on GTPv1 port 2152 and GTPv0 port 3386 on the specified LISTEN_ADDR
 | 
			
		||||
  use     Add GTP device, using an existing Linux kernel GTP device, e.g. created by 'gtp-link'
 | 
			
		||||
  delete  Remove a GTP device from the configuration, and delete the Linux kernel GTP device if it was created here.
 | 
			
		||||
OsmoUPF(config-gtp)# dev create ?
 | 
			
		||||
OsmoUPF(config-tunend)# dev create ?
 | 
			
		||||
  DEVNAME  device name, e.g. 'apn0'
 | 
			
		||||
OsmoUPF(config-gtp)# dev create foo ?
 | 
			
		||||
  [LISTEN_ADDR]  IPv4 or IPv6 address to listen on, omit for ANY
 | 
			
		||||
OsmoUPF(config-gtp)# dev delete ?
 | 
			
		||||
OsmoUPF(config-tunend)# dev create foo ?
 | 
			
		||||
  [LISTEN_ADDR]  IPv4 or IPv6 address to listen on, omit for ANY. LISTEN_ADDR is used to pick a GTP device matching the local address for a PFCP Network Instance, which are configured in the 'netinst' node.
 | 
			
		||||
OsmoUPF(config-tunend)# dev use ?
 | 
			
		||||
  DEVNAME  device name, e.g. 'apn0'
 | 
			
		||||
OsmoUPF(config-gtp)# exit
 | 
			
		||||
OsmoUPF(config-tunend)# dev use foo ?
 | 
			
		||||
  [LOCAL_ADDR]  The local GTP address this device listens on. It is assumed to be ANY when omitted. LOCAL_ADDR is used to pick a GTP device matching the local address for a PFCP Network Instance, which are configured in the 'netinst' node.
 | 
			
		||||
OsmoUPF(config-tunend)# dev delete ?
 | 
			
		||||
  DEVNAME  device name, e.g. 'apn0'
 | 
			
		||||
OsmoUPF(config-tunend)# exit
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config)# # ensure its old name "nft" enters the tunmap node
 | 
			
		||||
OsmoUPF(config)# nft
 | 
			
		||||
OsmoUPF(config-tunmap)# list
 | 
			
		||||
...
 | 
			
		||||
  mockup
 | 
			
		||||
  no mockup
 | 
			
		||||
  table-name TABLE_NAME
 | 
			
		||||
  show nft-rule tunmap example
 | 
			
		||||
OsmoUPF(config-tunmap)# exit
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config)# tunmap
 | 
			
		||||
OsmoUPF(config-tunmap)# list
 | 
			
		||||
...
 | 
			
		||||
  mockup
 | 
			
		||||
  no mockup
 | 
			
		||||
  table-name TABLE_NAME
 | 
			
		||||
  show nft-rule tunmap example
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# mockup?
 | 
			
		||||
  mockup  don't actually send rulesets to nftables, just return success
 | 
			
		||||
OsmoUPF(config-tunmap)# no ?
 | 
			
		||||
  mockup  operate nftables rulesets normally
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# table-name?
 | 
			
		||||
  table-name  Set the nft inet table name to create and place GTP tunnel forwarding chains in (as in 'nft add table inet foo'). If multiple instances of osmo-upf are running on the same system, each osmo-upf must have its own table name. Otherwise the names of created forwarding chains will collide. The default table name is "osmo-upf".
 | 
			
		||||
OsmoUPF(config-tunmap)# table-name ?
 | 
			
		||||
  TABLE_NAME  nft inet table name
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# nft-rule?
 | 
			
		||||
% There is no matched command.
 | 
			
		||||
OsmoUPF(config-tunmap)# nft-rule ?
 | 
			
		||||
% There is no matched command.
 | 
			
		||||
OsmoUPF(config-tunmap)# nft-rule tunmap ?
 | 
			
		||||
% There is no matched command.
 | 
			
		||||
OsmoUPF(config-tunmap)# nft-rule tunmap append ?
 | 
			
		||||
% There is no matched command.
 | 
			
		||||
 | 
			
		||||
OsmoUPF(config-tunmap)# show?
 | 
			
		||||
  show  Show running system information
 | 
			
		||||
OsmoUPF(config-tunmap)# show ?
 | 
			
		||||
...
 | 
			
		||||
  nft-rule        nftables rule specifics
 | 
			
		||||
...
 | 
			
		||||
OsmoUPF(config-tunmap)# show nft-rule ?
 | 
			
		||||
  tunmap  GTP tunmap use case (a.k.a. forwarding between two GTP tunnels)
 | 
			
		||||
OsmoUPF(config-tunmap)# show nft-rule tunmap ?
 | 
			
		||||
  example  Print a complete nftables ruleset for a tunmap filled with example IP addresses and TEIDs
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user