mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn.git
				synced 2025-11-04 06:03:15 +00:00 
			
		
		
		
	Compare commits
	
		
			163 Commits
		
	
	
		
			osmith/fix
			...
			osmith/rel
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					622148bbf6 | ||
| 
						 | 
					55701c111b | ||
| 
						 | 
					a71826878e | ||
| 
						 | 
					80adb30e93 | ||
| 
						 | 
					ebd39830cb | ||
| 
						 | 
					b05c1d0ce4 | ||
| 
						 | 
					999a776b70 | ||
| 
						 | 
					11ccc4305d | ||
| 
						 | 
					901ed14c89 | ||
| 
						 | 
					4be5ab3707 | ||
| 
						 | 
					c999c223ae | ||
| 
						 | 
					2ce050ba46 | ||
| 
						 | 
					43e5f8a2c6 | ||
| 
						 | 
					caf73b803c | ||
| 
						 | 
					0018d3e0ec | ||
| 
						 | 
					f955d078ed | ||
| 
						 | 
					93ccc3cf5d | ||
| 
						 | 
					10e0fcaae2 | ||
| 
						 | 
					592eb140f0 | ||
| 
						 | 
					c7b6aabac1 | ||
| 
						 | 
					873c8a55e7 | ||
| 
						 | 
					e0876bda26 | ||
| 
						 | 
					21afdf9a32 | ||
| 
						 | 
					c09cb29d78 | ||
| 
						 | 
					6ad4040f55 | ||
| 
						 | 
					45bf92feb4 | ||
| 
						 | 
					d367b8bc78 | ||
| 
						 | 
					5037b6817f | ||
| 
						 | 
					d245c0e542 | ||
| 
						 | 
					4380f94cf2 | ||
| 
						 | 
					0d170d61aa | ||
| 
						 | 
					fb5ccb468a | ||
| 
						 | 
					b96d5a6ead | ||
| 
						 | 
					6ff8d21fed | ||
| 
						 | 
					5b1122f717 | ||
| 
						 | 
					914484d561 | ||
| 
						 | 
					b6343a72d8 | ||
| 
						 | 
					ef0c2a4b85 | ||
| 
						 | 
					02b24c5a44 | ||
| 
						 | 
					cd7c7a74b7 | ||
| 
						 | 
					ddb3fbb0f2 | ||
| 
						 | 
					e245677cdd | ||
| 
						 | 
					1c33e4af05 | ||
| 
						 | 
					3844da98f8 | ||
| 
						 | 
					dee0bcc8e9 | ||
| 
						 | 
					51730f7a8c | ||
| 
						 | 
					f23e2db752 | ||
| 
						 | 
					8a33528854 | ||
| 
						 | 
					00c1f91ed7 | ||
| 
						 | 
					8d382c5337 | ||
| 
						 | 
					ef3c9af0f9 | ||
| 
						 | 
					5a21f07dff | ||
| 
						 | 
					ea0b5d0df6 | ||
| 
						 | 
					3054213e87 | ||
| 
						 | 
					3326ba7d4c | ||
| 
						 | 
					b6b2f14197 | ||
| 
						 | 
					959f77e34b | ||
| 
						 | 
					9e917647ae | ||
| 
						 | 
					784c59f87e | ||
| 
						 | 
					1aa0ae9db1 | ||
| 
						 | 
					cab8588242 | ||
| 
						 | 
					4b4c997dc5 | ||
| 
						 | 
					c4c1db9e78 | ||
| 
						 | 
					61ff273365 | ||
| 
						 | 
					bf69833b6d | ||
| 
						 | 
					7cb76a4321 | ||
| 
						 | 
					c91f53ca0a | ||
| 
						 | 
					5687ae65fa | ||
| 
						 | 
					7c86a1efce | ||
| 
						 | 
					453a51d1a1 | ||
| 
						 | 
					a54ed46bac | ||
| 
						 | 
					052d855449 | ||
| 
						 | 
					11ad5713f2 | ||
| 
						 | 
					d651edcce0 | ||
| 
						 | 
					56f5e74d7d | ||
| 
						 | 
					4bf53ef19a | ||
| 
						 | 
					91bb720449 | ||
| 
						 | 
					993d3f4d9a | ||
| 
						 | 
					78db244b42 | ||
| 
						 | 
					0e1b791c81 | ||
| 
						 | 
					8cd74407ab | ||
| 
						 | 
					6c4c6f08ae | ||
| 
						 | 
					fb7f8c5f07 | ||
| 
						 | 
					9a2fc908df | ||
| 
						 | 
					e794c1f00b | ||
| 
						 | 
					bd356a6d84 | ||
| 
						 | 
					e8c8ec9683 | ||
| 
						 | 
					55253716d2 | ||
| 
						 | 
					1239cf457e | ||
| 
						 | 
					ac44d6b2a2 | ||
| 
						 | 
					c8d98ac8f9 | ||
| 
						 | 
					6626bbc215 | ||
| 
						 | 
					103a7ec033 | ||
| 
						 | 
					6e8ed2784e | ||
| 
						 | 
					7418797027 | ||
| 
						 | 
					d97ff681c3 | ||
| 
						 | 
					83142beca2 | ||
| 
						 | 
					8c3d7fd263 | ||
| 
						 | 
					9e583c8d89 | ||
| 
						 | 
					5937dfd39f | ||
| 
						 | 
					cafa3881ad | ||
| 
						 | 
					447ad441e6 | ||
| 
						 | 
					f7a1aed0e6 | ||
| 
						 | 
					eb4233e505 | ||
| 
						 | 
					2636e89ff0 | ||
| 
						 | 
					bcd7709452 | ||
| 
						 | 
					7ffc6603e2 | ||
| 
						 | 
					826eaa327b | ||
| 
						 | 
					952fbf20c9 | ||
| 
						 | 
					8553f5532a | ||
| 
						 | 
					3375fa4d64 | ||
| 
						 | 
					e1ba4239b4 | ||
| 
						 | 
					638cddd8f1 | ||
| 
						 | 
					58937ce333 | ||
| 
						 | 
					02f2c34f8a | ||
| 
						 | 
					62fa6198ae | ||
| 
						 | 
					c42331f359 | ||
| 
						 | 
					15c9da226b | ||
| 
						 | 
					8bca8de5cb | ||
| 
						 | 
					82182d09c7 | ||
| 
						 | 
					08395b3369 | ||
| 
						 | 
					25998ddcc5 | ||
| 
						 | 
					60581ae7c9 | ||
| 
						 | 
					c70e8388c7 | ||
| 
						 | 
					5ce54ba1e6 | ||
| 
						 | 
					ff5b59a821 | ||
| 
						 | 
					be2330fde4 | ||
| 
						 | 
					8d2d7db818 | ||
| 
						 | 
					92ef0c8675 | ||
| 
						 | 
					68d9c5a468 | ||
| 
						 | 
					ef6205ba00 | ||
| 
						 | 
					86336af2a3 | ||
| 
						 | 
					6d92f148aa | ||
| 
						 | 
					27a0bf70e7 | ||
| 
						 | 
					d3c3ddeb51 | ||
| 
						 | 
					e6c5b4a970 | ||
| 
						 | 
					aae7daff81 | ||
| 
						 | 
					b32936d823 | ||
| 
						 | 
					d5dc143001 | ||
| 
						 | 
					65a5a0a27b | ||
| 
						 | 
					9d16b14345 | ||
| 
						 | 
					dd930a25ad | ||
| 
						 | 
					b3e10aa8eb | ||
| 
						 | 
					36ecddb705 | ||
| 
						 | 
					90dedcb2e7 | ||
| 
						 | 
					cfd307b4e8 | ||
| 
						 | 
					b26a5a82db | ||
| 
						 | 
					7369d449f1 | ||
| 
						 | 
					5e1a486a72 | ||
| 
						 | 
					627e285fd0 | ||
| 
						 | 
					5e0b829884 | ||
| 
						 | 
					bd6d677179 | ||
| 
						 | 
					482bb07301 | ||
| 
						 | 
					b63e19d7d2 | ||
| 
						 | 
					c6548bbaab | ||
| 
						 | 
					b2ebc59f30 | ||
| 
						 | 
					9d550cb369 | ||
| 
						 | 
					e6b2883f10 | ||
| 
						 | 
					6528e8c1cb | ||
| 
						 | 
					6e4cd085b5 | ||
| 
						 | 
					91a8bbd5db | ||
| 
						 | 
					d999e54aa2 | ||
| 
						 | 
					394aa533e9 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -14,6 +14,7 @@ bscconfig.h.in
 | 
			
		||||
*.pyc
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
#configure
 | 
			
		||||
aclocal.m4
 | 
			
		||||
@@ -40,7 +41,6 @@ ltmain.sh
 | 
			
		||||
 | 
			
		||||
# apps and app data
 | 
			
		||||
src/sgsn/osmo-sgsn
 | 
			
		||||
src/gbproxy/osmo-gbproxy
 | 
			
		||||
src/gtphub/osmo-gtphub
 | 
			
		||||
src/libcommon/gsup_test_client
 | 
			
		||||
 | 
			
		||||
@@ -69,3 +69,5 @@ doc/manuals/generated/
 | 
			
		||||
doc/manuals/osmomsc-usermanual.xml
 | 
			
		||||
doc/manuals/common
 | 
			
		||||
doc/manuals/build
 | 
			
		||||
 | 
			
		||||
contrib/osmo-sgsn.spec
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Makefile.am
									
									
									
									
									
								
							@@ -9,15 +9,21 @@ AM_CPPFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	doc \
 | 
			
		||||
	include \
 | 
			
		||||
	src \
 | 
			
		||||
	contrib \
 | 
			
		||||
	tests \
 | 
			
		||||
	doc \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     contrib/osmo-sgsn.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
	     $(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1 @@
 | 
			
		||||
#component	what		description / commit summary line
 | 
			
		||||
osmo-sgsn	libgtp		We dropped libgtp gtp_retranstimeout timer code which became a
 | 
			
		||||
				no-op in osmo-ggsn.git c94837c6a401bf0f80791b619a9b4cfbe9160afd,
 | 
			
		||||
				which means next osmo-sgsn release will require libgtp > 1.4.0.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								configure.ac
									
									
									
									
									
								
							@@ -22,6 +22,11 @@ AC_PROG_CC
 | 
			
		||||
AC_PROG_INSTALL
 | 
			
		||||
LT_INIT
 | 
			
		||||
 | 
			
		||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
 | 
			
		||||
AS_CASE(["$LD"],[*clang*],
 | 
			
		||||
  [AS_CASE(["${host_os}"],
 | 
			
		||||
     [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
 | 
			
		||||
 | 
			
		||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
			
		||||
@@ -34,34 +39,40 @@ m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [
 | 
			
		||||
	AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.])
 | 
			
		||||
	])
 | 
			
		||||
 | 
			
		||||
dnl use a defined standard across all builds and don't depend on compiler default
 | 
			
		||||
CFLAGS="$CFLAGS -std=gnu11"
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DL)
 | 
			
		||||
 | 
			
		||||
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DLSYM)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.0.0)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.3.0)
 | 
			
		||||
 | 
			
		||||
# Enable/disable 3G aka IuPS + IuCS support?
 | 
			
		||||
AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS and IuCS interfaces])],
 | 
			
		||||
    [osmo_ac_iu="$enableval"],[osmo_ac_iu="no"])
 | 
			
		||||
if test "x$osmo_ac_iu" = "xyes" ; then
 | 
			
		||||
    PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.0.0)
 | 
			
		||||
    PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.4.0)
 | 
			
		||||
    PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
 | 
			
		||||
    PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 0.4.0)
 | 
			
		||||
    PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 0.7.0)
 | 
			
		||||
    AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support])
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes")
 | 
			
		||||
AC_SUBST(osmo_ac_iu)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.4.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBCARES, libcares)
 | 
			
		||||
 | 
			
		||||
dnl checks for header files
 | 
			
		||||
@@ -110,8 +121,8 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
 | 
			
		||||
CFLAGS="$saved_CFLAGS"
 | 
			
		||||
AC_SUBST(SYMBOL_VISIBILITY)
 | 
			
		||||
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -Wall"
 | 
			
		||||
CFLAGS="$CFLAGS -Wall"
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -Wall -Wno-trigraphs"
 | 
			
		||||
CFLAGS="$CFLAGS -Wall -Wno-trigraphs"
 | 
			
		||||
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
 | 
			
		||||
@@ -164,9 +175,9 @@ AC_ARG_ENABLE([external_tests],
 | 
			
		||||
				[Include the VTY/CTRL tests in make check [default=no]]),
 | 
			
		||||
		[enable_ext_tests="$enableval"],[enable_ext_tests="no"])
 | 
			
		||||
if test "x$enable_ext_tests" = "xyes" ; then
 | 
			
		||||
	AC_CHECK_PROG(PYTHON2_AVAIL,python2,yes)
 | 
			
		||||
	 if test "x$PYTHON2_AVAIL" != "xyes" ; then
 | 
			
		||||
		AC_MSG_ERROR([Please install python2 to run the VTY/CTRL tests.])
 | 
			
		||||
	AC_CHECK_PROG(PYTHON3_AVAIL,python3,yes)
 | 
			
		||||
	 if test "x$PYTHON3_AVAIL" != "xyes" ; then
 | 
			
		||||
		AC_MSG_ERROR([Please install python3 to run the VTY/CTRL tests.])
 | 
			
		||||
	fi
 | 
			
		||||
	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
			
		||||
	 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
 | 
			
		||||
@@ -248,12 +259,10 @@ AC_OUTPUT(
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    src/gprs/Makefile
 | 
			
		||||
    src/sgsn/Makefile
 | 
			
		||||
    src/gbproxy/Makefile
 | 
			
		||||
    src/gtphub/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/gprs/Makefile
 | 
			
		||||
    tests/gbproxy/Makefile
 | 
			
		||||
    tests/sgsn/Makefile
 | 
			
		||||
    tests/gtphub/Makefile
 | 
			
		||||
    tests/xid/Makefile
 | 
			
		||||
@@ -265,4 +274,5 @@ AC_OUTPUT(
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    contrib/osmo-sgsn.spec
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
demonstrate a unblock bug on the GB Proxy..
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7"
 | 
			
		||||
ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7"
 | 
			
		||||
 | 
			
		||||
bts_ns_unblock = "\x06"
 | 
			
		||||
ns_unblock_ack = "\x07"
 | 
			
		||||
 | 
			
		||||
bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02"
 | 
			
		||||
ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00"
 | 
			
		||||
 | 
			
		||||
bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import socket
 | 
			
		||||
socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 | 
			
		||||
socket.bind(("0.0.0.0", 0))
 | 
			
		||||
socket.setblocking(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
port = int(sys.argv[1])
 | 
			
		||||
print "Sending data to port: %d" % port
 | 
			
		||||
 | 
			
		||||
def send_and_receive(packet):
 | 
			
		||||
    socket.sendto(packet, ("127.0.0.1", port))
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        data, addr = socket.recvfrom(4096)
 | 
			
		||||
    except socket.error, e:
 | 
			
		||||
        print "ERROR", e
 | 
			
		||||
        import sys
 | 
			
		||||
        sys.exit(0)
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
#send stuff once
 | 
			
		||||
 | 
			
		||||
to_send = [
 | 
			
		||||
    (bts_ns_reset, ns_reset_ack, "reset ack"),
 | 
			
		||||
    (bts_ns_unblock, ns_unblock_ack, "unblock ack"),
 | 
			
		||||
    (bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
for (out, inp, type) in to_send:
 | 
			
		||||
    res = send_and_receive(out)
 | 
			
		||||
    if res != inp:
 | 
			
		||||
        print "Failed to get the %s" % type
 | 
			
		||||
        sys.exit(-1)
 | 
			
		||||
 | 
			
		||||
import time
 | 
			
		||||
time.sleep(3)
 | 
			
		||||
res = send_and_receive(bts_bvc_reset_8167)
 | 
			
		||||
print "Sent all messages... check wireshark for the last response"
 | 
			
		||||
@@ -50,7 +50,6 @@ fi
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@@ -70,12 +69,12 @@ LD_LIBRARY_PATH="$inst/lib" $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" \
 | 
			
		||||
  DISTCHECK_CONFIGURE_FLAGS="$enable_werror $IU --enable-external-tests $CONFIG" \
 | 
			
		||||
  $MAKE distcheck \
 | 
			
		||||
  $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
			
		||||
	make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE maintainer-clean
 | 
			
		||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								contrib/osmo-sgsn.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								contrib/osmo-sgsn.spec.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-sgsn
 | 
			
		||||
#
 | 
			
		||||
# 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/4116
 | 
			
		||||
%define _lto_cflags %{nil}
 | 
			
		||||
 | 
			
		||||
%define with_iu 1
 | 
			
		||||
Name:           osmo-sgsn
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        Osmocom's SGSN for 2G and 3G packet-switched mobile networks
 | 
			
		||||
License:        AGPL-3.0-or-later AND GPL-2.0-or-later
 | 
			
		||||
Group:          Productivity/Telephony/Servers
 | 
			
		||||
URL:            https://osmocom.org/projects/osmosgsn
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  autoconf
 | 
			
		||||
BuildRequires:  automake
 | 
			
		||||
BuildRequires:  libtool
 | 
			
		||||
BuildRequires:  pkgconfig
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libcares)
 | 
			
		||||
BuildRequires:  pkgconfig(libcrypto) >= 0.9.5
 | 
			
		||||
BuildRequires:  pkgconfig(libgtp) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-gsup-client) >= 1.3.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-netif) >= 1.1.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.1.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogb) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.5.0
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
%if %{with_iu}
 | 
			
		||||
BuildRequires:  pkgconfig(libasn1c)
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-ranap) >= 0.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-sigtran) >= 1.4.0
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
OsmoSGSN is Osmocom's Serving GPRS Support Node for 2G and 3G
 | 
			
		||||
packet-switched mobile networks.
 | 
			
		||||
 | 
			
		||||
%package -n osmo-gtphub
 | 
			
		||||
Summary:        Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs
 | 
			
		||||
Group:          Productivity/Telephony/Servers
 | 
			
		||||
 | 
			
		||||
%description -n osmo-gtphub
 | 
			
		||||
Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "%{version}" >.tarball-version
 | 
			
		||||
autoreconf -fi
 | 
			
		||||
%configure \
 | 
			
		||||
%if %{with_iu}
 | 
			
		||||
  --enable-iu \
 | 
			
		||||
%endif
 | 
			
		||||
  --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
 | 
			
		||||
%preun  -n osmo-gtphub %service_del_preun   osmo-gtphub.service
 | 
			
		||||
%postun -n osmo-gtphub %service_del_postun  osmo-gtphub.service
 | 
			
		||||
%pre    -n osmo-gtphub %service_add_pre     osmo-gtphub.service
 | 
			
		||||
%post   -n osmo-gtphub %service_add_post    osmo-gtphub.service
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%doc AUTHORS README
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%dir %{_docdir}/%{name}/examples/osmo-sgsn
 | 
			
		||||
%exclude %{_docdir}/%{name}/examples/osmo-gtphub
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn_custom-sccp.cfg
 | 
			
		||||
%{_bindir}/osmo-sgsn
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-sgsn.cfg
 | 
			
		||||
%{_unitdir}/%{name}.service
 | 
			
		||||
 | 
			
		||||
%files -n osmo-gtphub
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%dir %{_docdir}/%{name}/examples/osmo-gtphub
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub.cfg
 | 
			
		||||
%{_bindir}/osmo-gtphub
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-gtphub.cfg
 | 
			
		||||
%{_unitdir}/osmo-gtphub.service
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
@@ -1,11 +1,9 @@
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
  osmo-gbproxy.service \
 | 
			
		||||
  osmo-gtphub.service \
 | 
			
		||||
  osmo-sgsn.service
 | 
			
		||||
 | 
			
		||||
if HAVE_SYSTEMD
 | 
			
		||||
SYSTEMD_SERVICES = \
 | 
			
		||||
  osmo-gbproxy.service \
 | 
			
		||||
  osmo-gtphub.service \
 | 
			
		||||
  osmo-sgsn.service
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom Gb proxy
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/usr/bin/osmo-gbproxy -c /etc/osmocom/osmo-gbproxy.cfg
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=2
 | 
			
		||||
RestartPreventExitStatus=1
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
							
								
								
									
										295
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										295
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,298 @@
 | 
			
		||||
osmo-sgsn (1.7.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * gtphub: remove llist_first, llist_last macros
 | 
			
		||||
  * debian/control: remove dh-systemd build-depend
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Mon, 06 Sep 2021 17:07:35 +0200
 | 
			
		||||
 | 
			
		||||
osmo-sgsn (1.7.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Daniel Willmann ]
 | 
			
		||||
  * gprs_gmm: Check for RAT change and ensure this only happens for RAU/ATT
 | 
			
		||||
  * gbproxy-usermanual: Explain BSSGP/BVC handling
 | 
			
		||||
  * gbproxy: Whitespace fixes
 | 
			
		||||
  * gbproxy: Add todo for BVC RESET logic
 | 
			
		||||
  * gbproxy: Get the peer if paging by BVCI on SIG_BVC
 | 
			
		||||
  * Let libosmocore handle VTY parent node tracking
 | 
			
		||||
  * osmo-gbproxy: Free msgb in ns2 prim callback
 | 
			
		||||
  * configure.ac: Require python3 for ext_tests
 | 
			
		||||
  * osmo-gbproxy: Implement nsvc-state ctrl command
 | 
			
		||||
  * gbproxy: Remove test testing NSVCI change
 | 
			
		||||
  * gbproxy: Add NSE peer that can have multiple gbproxy_peers
 | 
			
		||||
  * gbproxy: Ensure BVC0 is reset before handling PtP BVCs
 | 
			
		||||
  * gbproxy: Only send paging to each matching NSE once
 | 
			
		||||
  * gbproxy: Separate function to move gbproxy_peer to different nse
 | 
			
		||||
  * gbproxy: Add logging macros for NSE and BVC
 | 
			
		||||
  * gbproxy: Use LOG macros for NSE/BVC
 | 
			
		||||
  * gbproxy: Change generic LOG messages so BVCI/NSEI fmt is consistent
 | 
			
		||||
  * gbproxy: Add todos encountered while going through the code
 | 
			
		||||
  * gbproxy: Add BVC log filters
 | 
			
		||||
  * gbproxy: Add newline to log message
 | 
			
		||||
  * gbproxy: Allow gbproxy_nse_free(NULL)
 | 
			
		||||
  * gbproxy: Delete gbproxy_nse in delete-gbproxy-peer VTY command
 | 
			
		||||
  * mm_state_gb_fsm: Handle implicit detach from mm_standby
 | 
			
		||||
  * gbproxy: Fix bvci check in gbprox_rx_ptp_from_*
 | 
			
		||||
  * osmo-gbproxy: Initialize all hash_maps
 | 
			
		||||
  * gbproxy: Fix confusing log message in gbprox_relay2nse
 | 
			
		||||
  * gbproxy: Add SGSN NRI configuration
 | 
			
		||||
  * gbproxy: Add SGSN pooling support
 | 
			
		||||
  * gbproxy: Add comments to sgsn functions
 | 
			
		||||
  * gbproxy: Add config option to name an SGSN
 | 
			
		||||
  * gbproxy: Add VTY command to override the node selection function
 | 
			
		||||
  * Fix gbproxy_sgsn_by_tlli wraparound
 | 
			
		||||
  * gbproxy: Implement TLLI cache and use it for SUSPEND/RESUME
 | 
			
		||||
  * gbproxy: Increase TLLI cache timeout to 10s
 | 
			
		||||
  * gbproxy: Implement IMSI cache
 | 
			
		||||
  * gbproxy: Use IMSI cache to handle PAGING_PS_REJECT
 | 
			
		||||
  * gbproxy: Use C-style comments
 | 
			
		||||
  * gbproxy: Move helper function to a more logical place
 | 
			
		||||
  * gbproxy: Remove unused variable assignment
 | 
			
		||||
  * gbproxy: Fix VTY cmd name
 | 
			
		||||
  * gbproxy: Define and use help string for gbproxy
 | 
			
		||||
  * gbproxy: Add VTY commands to query the TLLI/IMSI cache
 | 
			
		||||
  * gbproxy: Use IMSI cache for PTP paging and implement DUMMY_PAGING_PS
 | 
			
		||||
  * gbproxy: Print the correct message type for dummy paging/paging reject
 | 
			
		||||
  * gbproxy: Improve log messages in gbproxy_select_sgsn
 | 
			
		||||
  * gbproxy: Fix radio status routing by TMSI
 | 
			
		||||
  * manual/gbproxy: Update overview chapter
 | 
			
		||||
  * Rename OsmoGbPROXY -> *Proxy
 | 
			
		||||
  * manuals/gbproxy: Update configuration chapter
 | 
			
		||||
  * manuals/gbproxy: Add osmo-bsc MSC pooling chapter from Neels as a base
 | 
			
		||||
  * manuals/gbproxy: MSC -> SGSN for pooling chapter
 | 
			
		||||
  * manuals/gbproxy: Move pooling to separate chapter
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * gprs_gmm_fsm.c: Implement RAT change between 2g and 3g
 | 
			
		||||
  * gtphub: rename sgsn's oww osmo_sockaddr into sgsn_sockaddr
 | 
			
		||||
  * gprs_llc: _bssgp_tx_dl_ud: ensure the LLME is valid before using it
 | 
			
		||||
  * gmm: on invalid RA id reject the MS with an implicit detach
 | 
			
		||||
  * gtphub_test: fix compilation error on gcc 10.2.0
 | 
			
		||||
  * gtphub: fix compilation with gcc 10.2.0
 | 
			
		||||
  * Port gbproxy to NS2
 | 
			
		||||
  * sgsn: check for NULL of gprs_subscr_get_or_create()
 | 
			
		||||
  * sgsn: Use the new NS2 api
 | 
			
		||||
  * gbproxy: use ns2 vty2
 | 
			
		||||
  * configure.ac: define a c standard instead of using the compilers default
 | 
			
		||||
  * follow libosmocore/gprs_ns2 API changes of GPRS enums
 | 
			
		||||
  * gbproxy: follow gprs_ns2 API vty changes
 | 
			
		||||
  * sgsn: migrate to the new gprs_ns2_vty configuration
 | 
			
		||||
  * follow libosmocore/gprs_ns2 API changes (gprs_ns2_dynamic_create_nse)
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * manual: explain IuPS, add SCCP/M3UA section from common chapters
 | 
			
		||||
  * fix nullpointer: in gsm48_rx_gmm_ra_upd_req()
 | 
			
		||||
  * gsup: send RAT type on LU
 | 
			
		||||
  * gbproxy_test.c: fix mobile identity test data
 | 
			
		||||
  * use new osmo_mobile_identity API everywhere
 | 
			
		||||
 | 
			
		||||
  [ Eric ]
 | 
			
		||||
  * tests: dlopen does not imply availability of dlsym..
 | 
			
		||||
  * configure.ac: fix libtool issue with clang and sanitizer
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * gtphub_test: Fix compilation with gcc-10
 | 
			
		||||
  * Fix memory leak when SNDCP de-fragmentation is used
 | 
			
		||||
  * Treat RAU as implicit RESUME if GMM is suspended
 | 
			
		||||
  * *.spec.in: Use %config(noreplace) to retain current config file
 | 
			
		||||
  * Send a BVC-RESET to all persistent Gb interfaces at start-up
 | 
			
		||||
  * Use osmo_fd_setup() whenever applicable
 | 
			
		||||
  * Use osmo_fd_*_{disable,enable}
 | 
			
		||||
  * gbproxy: Properly implement paging to LAC/RAC
 | 
			
		||||
  * gbproxy: Implement paging to entire BSS area
 | 
			
		||||
  * gprs_gb_parse: Add function to determine TLLI from encoded BSSGP
 | 
			
		||||
  * gbproxy: Pass TLLI as LSP towards NS to facilitate load sharing
 | 
			
		||||
  * gb_proxy_peer: Add some FIXMEs regarding invalid assumptions
 | 
			
		||||
  * gb_proxy: More precise + readable log messages
 | 
			
		||||
  * gb_proxy: Broadcast SGSN-INVOKE-TRACE and OVERLOAD
 | 
			
		||||
  * gbproxy: Move BSS-side BVC-RESET processing to its own function
 | 
			
		||||
  * gb_proxy: Slightly restructure processing of BSS-originated BVC-RESET
 | 
			
		||||
  * gbproxy: Cosmetics: use longer lines
 | 
			
		||||
  * gbproxy: Send BVC-STATUS if BSS sends us BVC-RESET without mandatory IEs
 | 
			
		||||
  * gb_proxy: Use TLVP_PRES_LEN instead of TLVP_PRESENT
 | 
			
		||||
  * gb_proxy: Rename gbproxy_peer to gbproxy_bvc
 | 
			
		||||
  * gbproxy: Rename gbproxy_cfg.nses to gbproxy_cfg.bss_nses
 | 
			
		||||
  * gbproxy: convert bss_nses from llist_head to hashtable
 | 
			
		||||
  * gbproxy: convert nse->bvcs from llist_head to hashtable
 | 
			
		||||
  * gbproxy: Remove patching, TLLI-tracking and SGSN2 support
 | 
			
		||||
  * gb_proxy: cosmetic: Use function rather than open-coding is_sgsn
 | 
			
		||||
  * gbproxy: Delete gbproxy_test
 | 
			
		||||
  * gb_proxy: Introduce more validation / constraint checks
 | 
			
		||||
  * gbproxy: use gbprox_relay2peer() whenever possible
 | 
			
		||||
  * gb_proxy: Use osmo_tlv_prot_parse() to validate mandatory IEs
 | 
			
		||||
  * gbproxy: Log FSM timeouts
 | 
			
		||||
  * migrate to DLBSSGP as log sub-system for BSSGP
 | 
			
		||||
  * gbproxy major rewrite for SGSN pool support
 | 
			
		||||
  * gbproxy: Use "(nsei << 16) | bvci" as rate_ctr_group index
 | 
			
		||||
  * gbproxy: Introduce new DOBJ log category; log object allocation/release
 | 
			
		||||
  * gbproxy: Don't create an extra msgb copy for SGSN DL SIG
 | 
			
		||||
  * gbproxy: Implement handling of BVC Flow Control
 | 
			
		||||
  * gbproxy: Copy RA-ID from BSS side BVC to CELL and SGSN-side BVC
 | 
			
		||||
  * gbproxy: (Re)allocate SGSN-side PTP BVC even if CELL already exists
 | 
			
		||||
  * gbproxy: Fix segfault when receiving PAGING for unknown destination
 | 
			
		||||
  * gbproxy: Add FSM related VTY commands
 | 
			
		||||
  * gbproxy: Implement scaling of BVC flow control in SGSN pool
 | 
			
		||||
  * gbproxy: Improve VTY state introspection
 | 
			
		||||
  * gbproxy: rename vty command "show gbproxy ..." to "show gbproxy bvc ..."
 | 
			
		||||
  * gbproxy: Add "show gbproxy cell ..." VTY command
 | 
			
		||||
  * gbproxy: Fix build on Deiban 8
 | 
			
		||||
  * gb_proxy: Don't use orphan log subsystem DPCU
 | 
			
		||||
  * gbproxy: Avoid depending on any of the SGSN code
 | 
			
		||||
  * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
 | 
			
		||||
  * manuals: generate vty reference xml at build time
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Use OSMO_FD_* instead of deprecated BSC_FD_*
 | 
			
		||||
  * sgsn_libgtp: Improve ps-paging logging
 | 
			
		||||
  * gprs_gmm_fsm.c: Add missing license header
 | 
			
		||||
  * sgsn_libgtp: Avoid ps-paging MS on GMM Suspended state
 | 
			
		||||
  * configure.ac: Fix trailing whitespace
 | 
			
		||||
  * doc: Update VTY reference xml file
 | 
			
		||||
  * Support setting rt-prio and cpu-affinity mask through VTY
 | 
			
		||||
  * Change default SCTP conn NULL->127.0.0.1 to localhost->localhost
 | 
			
		||||
  * contrib/jenkins: Enable parallel make in make distcheck
 | 
			
		||||
  * Log error if pdp ctx is freed while holding an active timer
 | 
			
		||||
  * Fix crash rx DeactPdpReq while waiting for DeactPdpAck after gtp side is freed
 | 
			
		||||
  * sgsn_delete_pdp_ctx: Add documentation and assert assumptions
 | 
			
		||||
  * process_ms_ctx_status: refactor to avoid code duplication
 | 
			
		||||
  * process_ms_ctx_status: Fix crash deleting PDP Ctx if GTP side was already released
 | 
			
		||||
  * gbproxy: generate coredump and exit upon SIGABRT received
 | 
			
		||||
  * gtphub: generate coredump and exit upon SIGABRT received
 | 
			
		||||
  * sgsn: generate coredump and exit upon SIGABRT received
 | 
			
		||||
  * gmm: fix build without define PTMSI_ALLOC
 | 
			
		||||
  * gmm: Introduce comment to ease addition of Network feature support IE later
 | 
			
		||||
  * .gitignore: Ignore new autofoo tmp files
 | 
			
		||||
  * sndcp: Fix struct bit fields on big endian
 | 
			
		||||
  * Fix nsei+bvci not updated on rx UL SNDCP data
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * contrib: import RPM spec
 | 
			
		||||
  * contrib: integrate RPM spec
 | 
			
		||||
  * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
 | 
			
		||||
  * contrib/jenkins: don't build osmo-gsm-manuals
 | 
			
		||||
  * gbproxy: remove (moved to own repository)
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * debian/control: change maintainer to the Osmocom team / mailing list
 | 
			
		||||
  * gb_proxy_peer: sgsn can never be NULL in gbproxy_sgsn_by_nri()
 | 
			
		||||
  * gb_proxy_peer: fix NULL pointer dereference in gbproxy_sgsn_alloc()
 | 
			
		||||
 | 
			
		||||
  [ Keith ]
 | 
			
		||||
  * Fix Radio Priority in MM Attach and PDP Context Activation
 | 
			
		||||
  * VTY: Add gtp state-dir command
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * gprs_sndcp: fix use after free
 | 
			
		||||
  * sgsn_rim: Add routing for (GERAN) BSSGP RIM messages
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 23 Feb 2021 20:29:33 +0100
 | 
			
		||||
 | 
			
		||||
osmo-sgsn (1.6.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * gprs_gmm: Introduce macros to access msgb's associated IU UE ctx
 | 
			
		||||
  * gprs_llc.h: Improve documentation of some structs
 | 
			
		||||
  * gprs_gmm.c: Improve doc on nullable parameters
 | 
			
		||||
  * gprs_gmm.c: Drop unneeded brackets
 | 
			
		||||
  * Introduce define TLLI_UNASSIGNED
 | 
			
		||||
  * gprs_sgsn.c: Warn upon llme free unexpected scenarios
 | 
			
		||||
  * gprs_gmm: Clarify comment during TLLI assignment
 | 
			
		||||
  * gprs_sgsn.c: Remove recently introduced assert
 | 
			
		||||
  * gprs_gmm: Introduce assert to guard against unexpected condition
 | 
			
		||||
  * gprs_gmm.c: Use correct function to set MM_IDLE state during cleanup
 | 
			
		||||
  * gprs_sgsn.h: Flag MM_CTX_T_GERAN_Iu as not supported
 | 
			
		||||
  * gprs_gmm.c: Flag mmctx_set_(p)mm_state() functions static
 | 
			
		||||
  * sgsn: use sccp_simple_client API to setup ss7 id 0 instead of 1
 | 
			
		||||
  * gprs_gmm.c: Fix typo in log message
 | 
			
		||||
  * gprs_gmm.c: Call mmctx_set_(p)mm_state only on related ran_type
 | 
			
		||||
  * Introduce and use log macros when no mm ctx available
 | 
			
		||||
  * gprs_gmm: Avoid spaces in fsm events and enum strings
 | 
			
		||||
  * tests: Verify that timers can be set over VTY
 | 
			
		||||
  * Replace own timer infra with libosmocore osmo_tdef
 | 
			
		||||
  * tests: Introduce vty-transcript-test tests
 | 
			
		||||
  * gprs_gmm.c: Use osmo_rai_name() in log line
 | 
			
		||||
  * examples: Add osmo-sgsn_custom-sccp.cfg
 | 
			
		||||
  * vty: Introduce cs7-instance-iu
 | 
			
		||||
  * gprs_gmm.c: Print value_string of reject cause
 | 
			
		||||
  * gprs_gmm.c: Add spec document to function documentation
 | 
			
		||||
  * gtp: make echo_interval unsigned
 | 
			
		||||
  * gtp: cb_delete_context(): Clarify why pdp->lib is set to NULL
 | 
			
		||||
  * Introduce log helper LOGGGSN and log category DGTP
 | 
			
		||||
  * sgsn: gtp: Drop related pdp contexts on echo timeout against GGSN
 | 
			
		||||
  * Move lots of Iu/ranap specific code into its own file
 | 
			
		||||
  * Move llc->MM/SM Gb specific glue code to its own file
 | 
			
		||||
  * Split enum gprs_pmm_state into Iu and Gb counterparts
 | 
			
		||||
  * Merge common allocation steps for Gb and Iu ctx
 | 
			
		||||
  * gprs_gmm.c: Replace inet_ntoa with inet_ntop
 | 
			
		||||
  * sgsn_cdr.c: Fix ip addr string buffer size
 | 
			
		||||
  * sgsn_vty: Fix mmctx rate_ctr output indentation
 | 
			
		||||
  * sgsn_vty: Print correct Iu mmctx id in 'show mm-context'
 | 
			
		||||
  * Introduce FSM mm_state_gb_fsm
 | 
			
		||||
  * Introduce FSM mm_state_iu_fsm
 | 
			
		||||
  * vty: Print MM state and RAN type in show mm-context
 | 
			
		||||
  * src/gprs/Makefile.am: Move build of shared .c files to an internal lib
 | 
			
		||||
  * Move out gbproxy to its own subdir
 | 
			
		||||
  * Move out gtphub to its own subdir
 | 
			
		||||
  * Move out sgsn to its own subdir
 | 
			
		||||
  * gmm: Move code handling GMM Attach Complete to its own function
 | 
			
		||||
  * gmm: Move code handling GMM Routing Area Update Complete to its own function
 | 
			
		||||
  * gmm: Move code handling GMM PTMSI Realloc Complete to its own function
 | 
			
		||||
  * enum gprs_gmm_state: Fix spec reference
 | 
			
		||||
  * Implement GMM State using osmocom FSM
 | 
			
		||||
  * Split out GPRS SM layer into its own file
 | 
			
		||||
  * sgsn: Reject PdpActReq if no GTP pdp ctx exists
 | 
			
		||||
  * Introduce TODO-RELEASE file
 | 
			
		||||
  * sgsn_libgtp.c: Drop use of deprecated libgtp APIs gtp_retrans*()
 | 
			
		||||
  * gmm: Fix assertion hit during RA UPD REQ before completting gmm attach
 | 
			
		||||
  * Improve logging in gprs_llc.c code
 | 
			
		||||
  * gprs_llc.c: Use enum instead of hardcoded value
 | 
			
		||||
  * gprs_gmm.c: Send XID reset with received TLLI
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * gprs/gprs_gmm: implement T3314. Timeout to reset MM state READY->STANDBY
 | 
			
		||||
  * gprs_gmm: only update gb/iu cell information when Iu/Gb present
 | 
			
		||||
  * gprs_gmm: clarify comment of Iu follow-on request
 | 
			
		||||
  * gprs_gmm: gsm48_rx_gmm_att_req(): refactor duplicated code
 | 
			
		||||
  * sgsn_pdp_ctx_terminate: check llme before accessing
 | 
			
		||||
  * gprs_ranap: send CommonId after receiving Security Mode Complete
 | 
			
		||||
  * mm_gb_fsm: unassign the llme when entering MM_IDLE
 | 
			
		||||
  * gprs_ranap: refactor REQUIRE_MM define
 | 
			
		||||
  * sgsn: when receiving data PDU notify the Gb GMM fsm
 | 
			
		||||
  * Avoid compiling unneeded files when building without Iu
 | 
			
		||||
  * gprs_ranap: release Iu UE Context when exiting PMM Connected
 | 
			
		||||
  * Iu: implement a user inactivity timer
 | 
			
		||||
  * gprs_ranap: on Iu release, stop the attach fsm if running
 | 
			
		||||
  * gprs_mm_state_gb_fsm: ensure T3350 is not running when entering IDLE
 | 
			
		||||
  * gprs_ranap: add missing rc = 0
 | 
			
		||||
  * gprs_sgsn: always allocate Gb/Iu mm fsm
 | 
			
		||||
  * sgsn: MM Gb Fsm: fix event handling for implicit detach
 | 
			
		||||
  * ranap: add non-spec X1001
 | 
			
		||||
  * gprs_gmm: release Iu connection on RAU failures
 | 
			
		||||
  * sgsn_mm_ctx_alloc(): check for unallocated fsms
 | 
			
		||||
  * sgsn_libgtp: refactor ps paging into gprs_gb
 | 
			
		||||
  * sgsn: Gb: implementing PS Paging when MS is MM_STANDBY
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * gprs_mm_state_iu_fsm.c: fix: assign timer_cb to mm_state_iu_fsm
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Use libosmocore constant for IMSI length in ACL entry
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * LLC: Don't use hard-coded N201-U / N201-I values in XID
 | 
			
		||||
  * Initial OsmoGbPROXY user manual
 | 
			
		||||
  * check for osmo_fsm_register() error return values
 | 
			
		||||
  * check for osmo_ss7_init() error return value
 | 
			
		||||
  * manual: Fix copy+paste error
 | 
			
		||||
  * exit(2) on unsupported positional arguments on command line
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * gitignore: fix paths to binaries
 | 
			
		||||
  * doc: add OsmoGbProxy VTY reference
 | 
			
		||||
  * regen_doc.sh: support gbproxy, run without docker
 | 
			
		||||
  * osmoappdesc.py, tests: switch to python 3
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 19:17:56 +0100
 | 
			
		||||
 | 
			
		||||
osmo-sgsn (1.5.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -1,10 +1,9 @@
 | 
			
		||||
Source: osmo-sgsn
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: extra
 | 
			
		||||
Maintainer: Alexander Couzens <lynxis@fe80.eu>
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>=9),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               dh-systemd (>= 1.5),
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               autoconf,
 | 
			
		||||
               automake,
 | 
			
		||||
@@ -12,16 +11,16 @@ Build-Depends: debhelper (>=9),
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               libtalloc-dev,
 | 
			
		||||
               libc-ares-dev,
 | 
			
		||||
               libgtp-dev (>= 1.4.0),
 | 
			
		||||
               libosmocore-dev (>= 1.2.0),
 | 
			
		||||
               libosmo-abis-dev (>= 0.6.0),
 | 
			
		||||
               libosmo-netif-dev (>= 0.4.0),
 | 
			
		||||
               libosmo-gsup-client-dev (>= 1.0.0),
 | 
			
		||||
               libgtp-dev (>= 1.7.0),
 | 
			
		||||
               libosmocore-dev (>= 1.5.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.1.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.1.0),
 | 
			
		||||
               libosmo-gsup-client-dev (>= 1.3.0),
 | 
			
		||||
               libasn1c-dev (>= 0.9.30),
 | 
			
		||||
               libosmo-ranap-dev (>= 0.4.0),
 | 
			
		||||
               libosmo-sigtran-dev (>= 1.0.0),
 | 
			
		||||
               libosmo-sccp-dev (>= 1.0.0),
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
               libosmo-ranap-dev (>= 0.7.0),
 | 
			
		||||
               libosmo-sigtran-dev (>= 1.4.0),
 | 
			
		||||
               libosmo-sccp-dev (>= 1.4.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.1.0)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-sgsn.git
 | 
			
		||||
Vcs-Browser: https://git.osmocom.org/osmo-sgsn
 | 
			
		||||
@@ -53,22 +52,6 @@ Priority: extra
 | 
			
		||||
Depends: osmo-gtphub (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: Debug symbols for Osmocom GTP Hub
 | 
			
		||||
 | 
			
		||||
Package: osmo-gbproxy
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends},
 | 
			
		||||
         ${misc:Depends}
 | 
			
		||||
Recommends: osmo-sgsn
 | 
			
		||||
Description: Osmocom GPRS Gb Interface Proxy
 | 
			
		||||
 The purpose of the Gb proxy is to aggregate the Gb links of multiple
 | 
			
		||||
 BSS's and present them in one Gb link to the SGSN.
 | 
			
		||||
 | 
			
		||||
Package: osmo-gbproxy-dbg
 | 
			
		||||
Architecture: any
 | 
			
		||||
Section: debug
 | 
			
		||||
Priority: extra
 | 
			
		||||
Depends: osmo-gbproxy (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: Debug symbols for Osmocom GPRS Gb Interface Proxy
 | 
			
		||||
 | 
			
		||||
Package: osmo-sgsn-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -19,13 +19,10 @@ Files:     .gitignore
 | 
			
		||||
           contrib/ipa.py
 | 
			
		||||
           contrib/jenkins.sh
 | 
			
		||||
           contrib/soap.py
 | 
			
		||||
           contrib/systemd/osmo-gbproxy.service
 | 
			
		||||
           contrib/systemd/osmo-sgsn.service
 | 
			
		||||
           contrib/twisted_ipa.py
 | 
			
		||||
           doc/Makefile.am
 | 
			
		||||
           doc/examples/Makefile.am
 | 
			
		||||
           doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg
 | 
			
		||||
           doc/examples/osmo-gbproxy/osmo-gbproxy.cfg
 | 
			
		||||
           doc/examples/osmo-gtphub/gtphub-example.txt
 | 
			
		||||
           doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
 | 
			
		||||
           doc/examples/osmo-gtphub/osmo-gtphub.cfg
 | 
			
		||||
@@ -51,8 +48,6 @@ Files:     .gitignore
 | 
			
		||||
           src/gprs/osmo_sgsn.cfg
 | 
			
		||||
           tests/Makefile.am
 | 
			
		||||
           tests/atlocal.in
 | 
			
		||||
           tests/gbproxy/Makefile.am
 | 
			
		||||
           tests/gbproxy/gbproxy_test.ok
 | 
			
		||||
           tests/gprs/Makefile.am
 | 
			
		||||
           tests/gprs/gprs_test.c
 | 
			
		||||
           tests/gprs/gprs_test.ok
 | 
			
		||||
@@ -86,12 +81,6 @@ Files:     include/osmocom/sgsn/a_reset.h
 | 
			
		||||
           src/gprs/gprs_gb_parse.c
 | 
			
		||||
           src/gprs/gprs_utils.c
 | 
			
		||||
           src/gprs/sgsn_ares.c
 | 
			
		||||
           src/gbproxy/gb_proxy.c
 | 
			
		||||
           src/gbproxy/gb_proxy_main.c
 | 
			
		||||
           src/gbproxy/gb_proxy_patch.c
 | 
			
		||||
           src/gbproxy/gb_proxy_peer.c
 | 
			
		||||
           src/gbproxy/gb_proxy_tlli.c
 | 
			
		||||
           src/gbproxy/gb_proxy_vty.c
 | 
			
		||||
           src/gtphub/gtphub.c
 | 
			
		||||
           src/gtphub/gtphub_main.c
 | 
			
		||||
           src/gtphub/gtphub_vty.c
 | 
			
		||||
@@ -142,7 +131,6 @@ License:   AGPL-3.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/gtphub/gtphub_ares.c
 | 
			
		||||
           src/gtphub/gtphub_sock.c
 | 
			
		||||
           tests/gbproxy/gbproxy_test.c
 | 
			
		||||
Copyright: 2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
           2013 sysmocom s.f.m.c. GmbH
 | 
			
		||||
           2014 Holger Hans Peter Freyther
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								debian/osmo-gbproxy.init
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										151
									
								
								debian/osmo-gbproxy.init
									
									
									
									
										vendored
									
									
								
							@@ -1,151 +0,0 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
### BEGIN INIT INFO
 | 
			
		||||
# Provides:          osmo-gbproxy
 | 
			
		||||
# Required-Start:    $network $local_fs
 | 
			
		||||
# Required-Stop:
 | 
			
		||||
# Default-Start:     2 3 4 5
 | 
			
		||||
# Default-Stop:      0 1 6
 | 
			
		||||
# Short-Description: Osmocom GBproxy
 | 
			
		||||
# Description:       A tool to proxy the GPRS Gb interface.
 | 
			
		||||
### END INIT INFO
 | 
			
		||||
 | 
			
		||||
# Author: Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 | 
			
		||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
 | 
			
		||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
 | 
			
		||||
NAME=osmo-gbproxy                      # Introduce the short server's name here
 | 
			
		||||
DESC="Osmocom GBProxy" # Introduce a short description here
 | 
			
		||||
DAEMON=/usr/bin/osmo-gbproxy           # Introduce the server's location here
 | 
			
		||||
SCRIPTNAME=/etc/init.d/osmocom-gbproxy
 | 
			
		||||
CONFIG_FILE=/etc/osmocom/osmocom-gbproxy.cfg
 | 
			
		||||
 | 
			
		||||
# Exit if the package is not installed
 | 
			
		||||
[ -x $DAEMON ] || exit 0
 | 
			
		||||
 | 
			
		||||
# Read configuration variable file if it is present
 | 
			
		||||
[ -r /etc/default/osmocom-gbproxy ] && . /etc/default/osmocom-gbproxy
 | 
			
		||||
 | 
			
		||||
# Load the VERBOSE setting and other rcS variables
 | 
			
		||||
. /lib/init/vars.sh
 | 
			
		||||
 | 
			
		||||
# Define LSB log_* functions.
 | 
			
		||||
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
 | 
			
		||||
. /lib/lsb/init-functions
 | 
			
		||||
 | 
			
		||||
DAEMON_ARGS="-D -c $CONFIG_FILE"
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Function that starts the daemon/service
 | 
			
		||||
#
 | 
			
		||||
do_start()
 | 
			
		||||
{
 | 
			
		||||
	# Return
 | 
			
		||||
	#   0 if daemon has been started
 | 
			
		||||
	#   1 if daemon was already running
 | 
			
		||||
	#   2 if daemon could not be started
 | 
			
		||||
	start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
 | 
			
		||||
		|| return 1
 | 
			
		||||
	start-stop-daemon --start --quiet --exec $DAEMON -- \
 | 
			
		||||
		$DAEMON_ARGS \
 | 
			
		||||
		|| return 2
 | 
			
		||||
	# Add code here, if necessary, that waits for the process to be ready
 | 
			
		||||
	# to handle requests from services started subsequently which depend
 | 
			
		||||
	# on this one.  As a last resort, sleep for some time.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Function that stops the daemon/service
 | 
			
		||||
#
 | 
			
		||||
do_stop()
 | 
			
		||||
{
 | 
			
		||||
	# Return
 | 
			
		||||
	#   0 if daemon has been stopped
 | 
			
		||||
	#   1 if daemon was already stopped
 | 
			
		||||
	#   2 if daemon could not be stopped
 | 
			
		||||
	#   other if a failure occurred
 | 
			
		||||
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
 | 
			
		||||
	RETVAL="$?"
 | 
			
		||||
	[ "$RETVAL" = 2 ] && return 2
 | 
			
		||||
	# Wait for children to finish too if this is a daemon that forks
 | 
			
		||||
	# and if the daemon is only ever run from this initscript.
 | 
			
		||||
	# If the above conditions are not satisfied then add some other code
 | 
			
		||||
	# that waits for the process to drop all resources that could be
 | 
			
		||||
	# needed by services started subsequently.  A last resort is to
 | 
			
		||||
	# sleep for some time.
 | 
			
		||||
	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
 | 
			
		||||
	[ "$?" = 2 ] && return 2
 | 
			
		||||
	return "$RETVAL"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Function that sends a SIGHUP to the daemon/service
 | 
			
		||||
#
 | 
			
		||||
do_reload() {
 | 
			
		||||
	#
 | 
			
		||||
	# If the daemon can reload its configuration without
 | 
			
		||||
	# restarting (for example, when it is sent a SIGHUP),
 | 
			
		||||
	# then implement that here.
 | 
			
		||||
	#
 | 
			
		||||
	start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
case "$1" in
 | 
			
		||||
  start)
 | 
			
		||||
    [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
 | 
			
		||||
    do_start
 | 
			
		||||
    case "$?" in
 | 
			
		||||
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
 | 
			
		||||
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
 | 
			
		||||
	esac
 | 
			
		||||
  ;;
 | 
			
		||||
  stop)
 | 
			
		||||
	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
 | 
			
		||||
	do_stop
 | 
			
		||||
	case "$?" in
 | 
			
		||||
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
 | 
			
		||||
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
 | 
			
		||||
	esac
 | 
			
		||||
	;;
 | 
			
		||||
  status)
 | 
			
		||||
       status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
 | 
			
		||||
       ;;
 | 
			
		||||
  #reload|force-reload)
 | 
			
		||||
	#
 | 
			
		||||
	# If do_reload() is not implemented then leave this commented out
 | 
			
		||||
	# and leave 'force-reload' as an alias for 'restart'.
 | 
			
		||||
	#
 | 
			
		||||
	#log_daemon_msg "Reloading $DESC" "$NAME"
 | 
			
		||||
	#do_reload
 | 
			
		||||
	#log_end_msg $?
 | 
			
		||||
	#;;
 | 
			
		||||
  restart|force-reload)
 | 
			
		||||
	#
 | 
			
		||||
	# If the "reload" option is implemented then remove the
 | 
			
		||||
	# 'force-reload' alias
 | 
			
		||||
	#
 | 
			
		||||
	log_daemon_msg "Restarting $DESC" "$NAME"
 | 
			
		||||
	do_stop
 | 
			
		||||
	case "$?" in
 | 
			
		||||
	  0|1)
 | 
			
		||||
		do_start
 | 
			
		||||
		case "$?" in
 | 
			
		||||
			0) log_end_msg 0 ;;
 | 
			
		||||
			1) log_end_msg 1 ;; # Old process is still running
 | 
			
		||||
			*) log_end_msg 1 ;; # Failed to start
 | 
			
		||||
		esac
 | 
			
		||||
		;;
 | 
			
		||||
	  *)
 | 
			
		||||
	  	# Failed to stop
 | 
			
		||||
		log_end_msg 1
 | 
			
		||||
		;;
 | 
			
		||||
	esac
 | 
			
		||||
	;;
 | 
			
		||||
  *)
 | 
			
		||||
	#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
 | 
			
		||||
	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
 | 
			
		||||
	exit 3
 | 
			
		||||
	;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
:
 | 
			
		||||
							
								
								
									
										5
									
								
								debian/osmo-gbproxy.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/osmo-gbproxy.install
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +0,0 @@
 | 
			
		||||
etc/osmocom/osmo-gbproxy.cfg
 | 
			
		||||
lib/systemd/system/osmo-gbproxy.service
 | 
			
		||||
usr/bin/osmo-gbproxy
 | 
			
		||||
usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg usr/share/doc/osmo-gbproxy/examples
 | 
			
		||||
usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy.cfg usr/share/doc/osmo-gbproxy/examples
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -58,7 +58,6 @@ override_dh_auto_configure:
 | 
			
		||||
override_dh_strip:
 | 
			
		||||
	dh_strip -posmo-sgsn --dbg-package=osmo-sgsn-dbg
 | 
			
		||||
	dh_strip -posmo-gtphub --dbg-package=osmo-gtphub-dbg
 | 
			
		||||
	dh_strip -posmo-gbproxy --dbg-package=osmo-gbproxy-dbg
 | 
			
		||||
 | 
			
		||||
# Print test results in case of a failure
 | 
			
		||||
override_dh_auto_test:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
OSMOCONF_FILES = \
 | 
			
		||||
	osmo-gtphub/osmo-gtphub.cfg \
 | 
			
		||||
	osmo-sgsn/osmo-sgsn.cfg \
 | 
			
		||||
	osmo-gbproxy/osmo-gbproxy.cfg
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmoconfdir = $(sysconfdir)/osmocom
 | 
			
		||||
osmoconf_DATA = $(OSMOCONF_FILES)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
!
 | 
			
		||||
! OsmoGbProxy (UNKNOWN) configuration saved from vty
 | 
			
		||||
!!
 | 
			
		||||
!
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging level all debug
 | 
			
		||||
 logging level gprs debug
 | 
			
		||||
 logging level ns info
 | 
			
		||||
 logging level bssgp debug
 | 
			
		||||
 logging level lglobal notice
 | 
			
		||||
 logging level llapd notice
 | 
			
		||||
 logging level linp notice
 | 
			
		||||
 logging level lmux notice
 | 
			
		||||
 logging level lmi notice
 | 
			
		||||
 logging level lmib notice
 | 
			
		||||
 logging level lsms notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
!
 | 
			
		||||
ns
 | 
			
		||||
 nse 666 nsvci 666
 | 
			
		||||
 nse 666 remote-role sgsn
 | 
			
		||||
! nse 666 encapsulation framerelay-gre
 | 
			
		||||
! nse 666 remote-ip 172.16.1.70
 | 
			
		||||
! nse 666 fr-dlci 666
 | 
			
		||||
 timer tns-block 3
 | 
			
		||||
 timer tns-block-retries 3
 | 
			
		||||
 timer tns-reset 3
 | 
			
		||||
 timer tns-reset-retries 3
 | 
			
		||||
 timer tns-test 30
 | 
			
		||||
 timer tns-alive 3
 | 
			
		||||
 timer tns-alive-retries 10
 | 
			
		||||
 encapsulation udp local-port 23000
 | 
			
		||||
! encapsulation framerelay-gre enabled 1
 | 
			
		||||
gbproxy
 | 
			
		||||
 sgsn nsei 666
 | 
			
		||||
 core-mobile-country-code 666
 | 
			
		||||
 core-mobile-network-code 6
 | 
			
		||||
 core-access-point-name none match-imsi ^666066|^66607
 | 
			
		||||
 tlli-list max-length 200
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
!
 | 
			
		||||
! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty
 | 
			
		||||
!!
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
!
 | 
			
		||||
gbproxy
 | 
			
		||||
 sgsn nsei 101
 | 
			
		||||
ns
 | 
			
		||||
 nse 101 nsvci 101
 | 
			
		||||
 nse 101 remote-role sgsn
 | 
			
		||||
 nse 101 encapsulation udp
 | 
			
		||||
 nse 101 remote-ip 192.168.100.239
 | 
			
		||||
 nse 101 remote-port 7777
 | 
			
		||||
 timer tns-block 3
 | 
			
		||||
 timer tns-block-retries 3
 | 
			
		||||
 timer tns-reset 3
 | 
			
		||||
 timer tns-reset-retries 3
 | 
			
		||||
 timer tns-test 30
 | 
			
		||||
 timer tns-alive 3
 | 
			
		||||
 timer tns-alive-retries 10
 | 
			
		||||
 encapsulation framerelay-gre enabled 0
 | 
			
		||||
 encapsulation framerelay-gre local-ip 0.0.0.0
 | 
			
		||||
 encapsulation udp local-ip 127.0.0.100
 | 
			
		||||
 encapsulation udp local-port 23000
 | 
			
		||||
@@ -21,9 +21,9 @@ ns
 | 
			
		||||
 timer tns-test 30
 | 
			
		||||
 timer tns-alive 3
 | 
			
		||||
 timer tns-alive-retries 10
 | 
			
		||||
 encapsulation udp local-ip 127.0.0.1
 | 
			
		||||
 encapsulation udp local-port 23000
 | 
			
		||||
 encapsulation framerelay-gre enabled 0
 | 
			
		||||
 bind udp local
 | 
			
		||||
  listen 127.0.0.1 23000
 | 
			
		||||
  accept-ipaccess
 | 
			
		||||
!
 | 
			
		||||
bssgp
 | 
			
		||||
!
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@ ns
 | 
			
		||||
 timer tns-test 30
 | 
			
		||||
 timer tns-alive 3
 | 
			
		||||
 timer tns-alive-retries 10
 | 
			
		||||
 encapsulation udp local-ip 127.0.0.1
 | 
			
		||||
 encapsulation udp local-port 23000
 | 
			
		||||
 encapsulation framerelay-gre enabled 0
 | 
			
		||||
 bind udp local
 | 
			
		||||
  listen 127.0.0.1 23000
 | 
			
		||||
  accept-ipaccess
 | 
			
		||||
!
 | 
			
		||||
bssgp
 | 
			
		||||
!
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,9 @@ ns
 | 
			
		||||
 timer tns-test 30
 | 
			
		||||
 timer tns-alive 3
 | 
			
		||||
 timer tns-alive-retries 10
 | 
			
		||||
 encapsulation udp local-ip 127.0.0.1
 | 
			
		||||
 encapsulation udp local-port 23000
 | 
			
		||||
 encapsulation framerelay-gre enabled 0
 | 
			
		||||
 bind udp local
 | 
			
		||||
  listen 127.0.0.1 23000
 | 
			
		||||
  accept-ipaccess
 | 
			
		||||
!
 | 
			
		||||
bssgp
 | 
			
		||||
!
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
EXTRA_DIST = osmosgsn-usermanual.adoc \
 | 
			
		||||
    osmosgsn-usermanual-docinfo.xml \
 | 
			
		||||
    osmosgsn-vty-reference.xml \
 | 
			
		||||
    osmogbproxy-usermanual.adoc \
 | 
			
		||||
    osmogbproxy-usermanual-docinfo.xml \
 | 
			
		||||
    regen_doc.sh \
 | 
			
		||||
    chapters \
 | 
			
		||||
    vty \
 | 
			
		||||
    osmogbproxy-vty-reference.xml \
 | 
			
		||||
    vty-osmogbproxy \
 | 
			
		||||
    $(NULL)
 | 
			
		||||
 | 
			
		||||
if BUILD_MANUALS
 | 
			
		||||
  ASCIIDOC = osmosgsn-usermanual.adoc osmogbproxy-usermanual.adoc
 | 
			
		||||
  ASCIIDOC = osmosgsn-usermanual.adoc
 | 
			
		||||
  ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
 | 
			
		||||
 | 
			
		||||
  VTY_REFERENCE = osmosgsn-vty-reference.xml osmogbproxy-vty-reference.xml
 | 
			
		||||
  VTY_REFERENCE = osmosgsn-vty-reference.xml
 | 
			
		||||
 | 
			
		||||
  BUILT_REFERENCE_XML = $(builddir)/vty/sgsn_vty_reference.xml
 | 
			
		||||
  $(builddir)/vty/sgsn_vty_reference.xml: $(top_builddir)/src/sgsn/osmo-sgsn
 | 
			
		||||
	mkdir -p $(builddir)/vty
 | 
			
		||||
	$(top_builddir)/src/sgsn/osmo-sgsn --vty-ref-xml > $@
 | 
			
		||||
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
			
		||||
 | 
			
		||||
  OSMO_REPOSITORY = osmo-sgsn
 | 
			
		||||
 
 | 
			
		||||
@@ -356,3 +356,23 @@ sgsn
 | 
			
		||||
sgsn
 | 
			
		||||
 encryption GEA0
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== Configure SCCP/M3UA to accept _IuPS_ links
 | 
			
		||||
 | 
			
		||||
OsmoSGSN acts as client to contact an STP instance and establish an SCCP/M3UA
 | 
			
		||||
link.
 | 
			
		||||
 | 
			
		||||
An example configuration of OsmoSGSN's SCCP link:
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
cs7 instance 0
 | 
			
		||||
 point-code 0.23.4
 | 
			
		||||
 asp asp-clnt-OsmoSGSN 2905 0 m3ua
 | 
			
		||||
  remote-ip 127.0.0.1
 | 
			
		||||
  sctp-role client
 | 
			
		||||
 as as-clnt-OsmoSGSN m3ua
 | 
			
		||||
  asp asp-clnt-OsmoSGSN
 | 
			
		||||
  routing-key 0 0.23.4
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
This configuration is explained in detail in <<cs7_config>>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
== Configuring OsmoGbPROXY
 | 
			
		||||
 | 
			
		||||
TBD.  Unfortunately this chapter of the manual still needs to be written.
 | 
			
		||||
Osmocom has very limited funding and support resources; Feel free to help
 | 
			
		||||
us completing this documentation by contributing with code, documentation
 | 
			
		||||
or by supporting the developers financially.
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
[[control]]
 | 
			
		||||
== Control interface
 | 
			
		||||
 | 
			
		||||
The actual protocol is described in <<common-control-if>>, the variables
 | 
			
		||||
common to all programs using it are described in <<ctrl_common_vars>>. Here we
 | 
			
		||||
describe variables specific to OsmoGbPROXY.
 | 
			
		||||
 | 
			
		||||
.Variables available over control interface
 | 
			
		||||
[options="header",width="100%",cols="20%,5%,5%,50%,20%"]
 | 
			
		||||
|===
 | 
			
		||||
|Name|Access|Trap|Value|Comment
 | 
			
		||||
|nsvc-state|RO|No|"<nsei>,<nsvci>,<local-alive>,<local-blocked>,<remote-role>,<remote-alive>,<remote-blocked>"|See <<nsvc_state>> for details.
 | 
			
		||||
|gbproxy-state|RO|No|"<nsei>,<bvci>,<mcc>,<mnc>,<lac>,<rac>,<blocked>"|See <<gbproxy_state>> for details.
 | 
			
		||||
|number-of-peers|RO|No|"<num-of-bss>"|Count of concurrent BSS(BTS) peers.
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
[[nsvc_state]]
 | 
			
		||||
=== nsvc-state
 | 
			
		||||
 | 
			
		||||
Return the list of active NS-VCs (NS Virtual Circuits), including information
 | 
			
		||||
on the key parameters, such as NSEI, NSVCI and the local + remote ALIVE
 | 
			
		||||
and BLOCKED state.
 | 
			
		||||
 | 
			
		||||
[[gbproxy_state]]
 | 
			
		||||
=== gbproxy-state
 | 
			
		||||
 | 
			
		||||
Return the list of active Peers, including information on the key
 | 
			
		||||
parameters, such as NSEI, BVCI, and the MCC-MNC-LAC-RAC of the attached
 | 
			
		||||
BSS, as well as the overall state (BLOCKED or UNBLOCKED).
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
[[chapter_overview]]
 | 
			
		||||
== Overview
 | 
			
		||||
 | 
			
		||||
=== About OsmoGbPROXY
 | 
			
		||||
 | 
			
		||||
OsmoGbPROXY is the Osmocom proxy for the 3GPP Gb interface.  The Gb
 | 
			
		||||
interface is defined by 3GPP as the protocol between the BSS and the
 | 
			
		||||
SGSN inside the 2G/2.5G/2.75G packet switched network domain.
 | 
			
		||||
 | 
			
		||||
As Osmocom implements a BTS-colocated PCU, there are potentially many
 | 
			
		||||
Gb interface connections between all those many PCUs in the network
 | 
			
		||||
and the SGSN.  This can be cumbersome to configure/maintain at the
 | 
			
		||||
SGSN sine.
 | 
			
		||||
 | 
			
		||||
OsmoGbPROXY aggregates many PCU-facing Gb connections into one Gb
 | 
			
		||||
connection to the SGSN.  This is achieved by
 | 
			
		||||
 | 
			
		||||
* maintaining sepaate NS-VCs on the PCU side and on the SGSN side
 | 
			
		||||
* more or less transparently routing BSSGP peer-to-peer Virtual Circuits
 | 
			
		||||
  (BVCs) through the proxy
 | 
			
		||||
* having some special handling for the signaling BVC (BVCI=0) which is
 | 
			
		||||
  shared among all the PCUs connected to the proxy
 | 
			
		||||
 | 
			
		||||
=== Data Model
 | 
			
		||||
 | 
			
		||||
==== gbproxy_config
 | 
			
		||||
 | 
			
		||||
This contains the parsed configuration of the OsmoGbPROXY.
 | 
			
		||||
 | 
			
		||||
==== gproxy_peer
 | 
			
		||||
 | 
			
		||||
A "peer" is any remote NS-entity that the proxy interacts with.  A peer
 | 
			
		||||
includes information about:
 | 
			
		||||
 | 
			
		||||
* the [unique] NSEI of the peer
 | 
			
		||||
* the [unique] BVCI of the peer
 | 
			
		||||
* the Routeing Area (RA) of the peer
 | 
			
		||||
 | 
			
		||||
==== gbproxy_tlli_state
 | 
			
		||||
 | 
			
		||||
One of the (unique) TLLI of any of the subscribers/UEs attached to any of
 | 
			
		||||
the BTSs/PCUs served by the proxy.
 | 
			
		||||
 | 
			
		||||
==== gbproxy_link_info
 | 
			
		||||
 | 
			
		||||
One of the [unique] subscribers/connections that are served through this
 | 
			
		||||
proxy.  The information includes
 | 
			
		||||
 | 
			
		||||
* the TLLI on BSS side
 | 
			
		||||
* the TLLI on SGSN side (may be different due to P-TMSI rewriting)
 | 
			
		||||
* the NSEI of the SGSN for this link
 | 
			
		||||
* a timestamp when we last conversed with this subscriber
 | 
			
		||||
* state related to IMSI acquisition
 | 
			
		||||
** a temporary queue of stored messages (until IMSI acquisition succeeds)
 | 
			
		||||
** N(U) rewriting state (inserting IDENTTIY REQ changes LLC sequence numbers)
 | 
			
		||||
 | 
			
		||||
==== gbproxy_match
 | 
			
		||||
 | 
			
		||||
A single matching rule against which IMSIs are matched.  The matching rule
 | 
			
		||||
is expressed as regular expression.  There can be one such matching rule for
 | 
			
		||||
each
 | 
			
		||||
 | 
			
		||||
* routing between two different SGSNs, see below
 | 
			
		||||
* patching of messages (e.g. APN, PLMN)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Advanced Features
 | 
			
		||||
 | 
			
		||||
==== PLMN patching
 | 
			
		||||
 | 
			
		||||
This feature permits to modify the PLMN inside any BSSGP messages
 | 
			
		||||
containing the Routing Area ID (RAID).
 | 
			
		||||
 | 
			
		||||
The configured core-mcc and core-mnc will be used towards the SGSN,
 | 
			
		||||
irrespective of which MCC/MNC the PCU is using/reporting on Gb.
 | 
			
		||||
 | 
			
		||||
==== APN patching
 | 
			
		||||
 | 
			
		||||
This will transparently re-write the APN name inside SM ACTIVATE PDP
 | 
			
		||||
REQUEST messages on the way from the MS to the SGSN.  The patching is
 | 
			
		||||
performed based on matching on the IMSI of the subscriber.
 | 
			
		||||
 | 
			
		||||
The configured core-apn will be used towards the SGSN, irrespective
 | 
			
		||||
of which APN the MS is requesting in its Layer3 signaling.
 | 
			
		||||
 | 
			
		||||
APN patching can only be performed if no GPRS encryption is enabled in
 | 
			
		||||
the network!
 | 
			
		||||
 | 
			
		||||
APN patching is useful in case a valid APN cannot reliably be
 | 
			
		||||
provisioned via other means, such as via the SIM Card, OTA-DM or via
 | 
			
		||||
CAMEL rewriting in the SGSN.
 | 
			
		||||
 | 
			
		||||
==== P-TMSI patching
 | 
			
		||||
 | 
			
		||||
This feature transparently rewrite the P-TMSI between MS and SGSN.  This
 | 
			
		||||
is required when using the Secondary SGSN support, as both SGSNs could
 | 
			
		||||
allocate overlapping TMSIs and we must make sure they're unique across
 | 
			
		||||
both SGSNs.
 | 
			
		||||
 | 
			
		||||
P-TMSI patching is required by (and hence automatically enablede if
 | 
			
		||||
secondary SGSN support is enabled.
 | 
			
		||||
 | 
			
		||||
P-TMSI patching can only be performed if no GPRS encryption is enabled in
 | 
			
		||||
the network!
 | 
			
		||||
 | 
			
		||||
==== IMSI Acquisition
 | 
			
		||||
 | 
			
		||||
This is a special feature where the proxy will by itself inject GMM IDENTITY
 | 
			
		||||
REQUEST messages for the IMSI into the downlink BSSGP traffic in order
 | 
			
		||||
to establish the IMSI of subscribers for which it is not otherwise known
 | 
			
		||||
 | 
			
		||||
IMSI acquisition is automatically enabled if secondary SGSN support is
 | 
			
		||||
enabled.
 | 
			
		||||
 | 
			
		||||
==== Secondary SGSN Support
 | 
			
		||||
 | 
			
		||||
This allows the proxy to connect not only to one SGSN, but to two
 | 
			
		||||
different SGSNs.  IMSI matching rules are applied to determine which of
 | 
			
		||||
the SGSNs is to be used for traffic of this subscriber.
 | 
			
		||||
 | 
			
		||||
One possible use case of this feature is to have a "local break-out" for
 | 
			
		||||
subscribers who are native to this network (and hence save
 | 
			
		||||
latencies/overhead of back-hauling all related traffic via the
 | 
			
		||||
SGSN+GGSN) while at the same time maintaining the classic behavior for
 | 
			
		||||
inbound roaming subscribers, where the roaming agreements mandate that
 | 
			
		||||
data traffic is brought back to the GGSN in the HPLMN via the SGSN of
 | 
			
		||||
the VPLMN.
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
== Running OsmoGbPROXY
 | 
			
		||||
 | 
			
		||||
The OsmoGbPROXY executable (`osmo-gbproxy`) offers the following command-line
 | 
			
		||||
options:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== SYNOPSIS
 | 
			
		||||
 | 
			
		||||
*osmo-gbproxy* [-h|-V] [-d 'DBGMASK'] [-D] [-c 'CONFIGFILE'] [-s] [-e 'LOGLEVEL'] [-T]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== OPTIONS
 | 
			
		||||
 | 
			
		||||
*-h, --help*::
 | 
			
		||||
	Print a short help message about the supported options
 | 
			
		||||
*-V, --version*::
 | 
			
		||||
	Print the compile-time version number of the program
 | 
			
		||||
*-d, --debug 'DBGMASK','DBGLEVELS'*::
 | 
			
		||||
	Set the log subsystems and levels for logging to stderr. This
 | 
			
		||||
	has mostly been superseded by VTY-based logging configuration,
 | 
			
		||||
	see <<logging>> for further information.
 | 
			
		||||
*-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_sgsn.cfg` in the current
 | 
			
		||||
	working directory.
 | 
			
		||||
*-s, --disable-color*::
 | 
			
		||||
	Disable colors for logging to stderr. This has mostly been
 | 
			
		||||
	deprecated by VTY based logging configuration, see <<logging>>
 | 
			
		||||
	for more information.
 | 
			
		||||
*-e, --log-level 'LOGLEVEL'*::
 | 
			
		||||
	Set the global log level for logging to stderr. This has mostly
 | 
			
		||||
	been deprecated by VTY based logging configuration, see
 | 
			
		||||
	<<logging>> for more information.
 | 
			
		||||
*-T, --timestamp*::
 | 
			
		||||
        Enable prefixing each log line on stderr with a timestamp.  This
 | 
			
		||||
        has mostly been deprecated by VTY based logging configuration, see
 | 
			
		||||
        <<logging>> for more information.
 | 
			
		||||
@@ -1,46 +0,0 @@
 | 
			
		||||
<revhistory>
 | 
			
		||||
  <revision>
 | 
			
		||||
    <revnumber>1</revnumber>
 | 
			
		||||
    <date>March 21, 2019</date>
 | 
			
		||||
    <authorinitials>HW</authorinitials>
 | 
			
		||||
    <revremark>
 | 
			
		||||
      Initial version.
 | 
			
		||||
    </revremark>
 | 
			
		||||
  </revision>
 | 
			
		||||
</revhistory>
 | 
			
		||||
 | 
			
		||||
<authorgroup>
 | 
			
		||||
  <author>
 | 
			
		||||
    <firstname>Harald</firstname>
 | 
			
		||||
    <surname>Welte</surname>
 | 
			
		||||
    <email>hwelte@sysmocom.de</email>
 | 
			
		||||
    <authorinitials>HW</authorinitials>
 | 
			
		||||
    <affiliation>
 | 
			
		||||
      <shortaffil>sysmocom</shortaffil>
 | 
			
		||||
      <orgname>sysmocom - s.f.m.c. GmbH</orgname>
 | 
			
		||||
      <jobtitle>Managing Director</jobtitle>
 | 
			
		||||
    </affiliation>
 | 
			
		||||
  </author>
 | 
			
		||||
</authorgroup>
 | 
			
		||||
 | 
			
		||||
<copyright>
 | 
			
		||||
  <year>2013-2019</year>
 | 
			
		||||
  <holder>sysmocom - s.f.m.c. GmbH</holder>
 | 
			
		||||
</copyright>
 | 
			
		||||
 | 
			
		||||
<legalnotice>
 | 
			
		||||
  <para>
 | 
			
		||||
	Permission is granted to copy, distribute and/or modify this
 | 
			
		||||
	document under the terms of the GNU Free Documentation License,
 | 
			
		||||
	Version 1.3 or any later version published by the Free Software
 | 
			
		||||
	Foundation; with no Invariant Sections, no Front-Cover Texts,
 | 
			
		||||
	and no Back-Cover Texts.  A copy of the license is included in
 | 
			
		||||
	the section entitled "GNU Free Documentation License".
 | 
			
		||||
  </para>
 | 
			
		||||
  <para>
 | 
			
		||||
	The Asciidoc source code of this manual can be found at
 | 
			
		||||
	<ulink url="https://git.osmocom.org/osmo-sgsn/doc/">
 | 
			
		||||
		https://git.osmocom.org/osmo-sgsn/doc/
 | 
			
		||||
	</ulink>
 | 
			
		||||
  </para>
 | 
			
		||||
</legalnotice>
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
:gfdl-enabled:
 | 
			
		||||
 | 
			
		||||
OsmoGbPROXY User Manual
 | 
			
		||||
=======================
 | 
			
		||||
Harald Welte <hwelte@sysmocom.de>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/preface.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/gbproxy-overview.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/gbproxy-running.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/gbproxy-control.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/logging.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/gbproxy-configuration.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gb.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/control_if.adoc[]
 | 
			
		||||
 | 
			
		||||
//include::{srcdir}/chapters/counters.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/glossary.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gfdl.adoc[]
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!--
 | 
			
		||||
  ex:ts=2:sw=42sts=2:et
 | 
			
		||||
  -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
-->
 | 
			
		||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
 | 
			
		||||
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
 | 
			
		||||
<!ENTITY chapter-vty      SYSTEM      "./common/chapters/vty.xml" >
 | 
			
		||||
<!ENTITY sections-vty     SYSTEM      "generated/docbook_osmogbproxy-vty-reference.xml"  >
 | 
			
		||||
]>
 | 
			
		||||
 | 
			
		||||
<book>
 | 
			
		||||
  <info>
 | 
			
		||||
    <revhistory>
 | 
			
		||||
        <revision>
 | 
			
		||||
            <revnumber>v1</revnumber>
 | 
			
		||||
            <date>2nd December 2019</date>
 | 
			
		||||
            <authorinitials>hw</authorinitials>
 | 
			
		||||
            <revremark>Initial</revremark>
 | 
			
		||||
        </revision>
 | 
			
		||||
    </revhistory>
 | 
			
		||||
 | 
			
		||||
    <title>OsmoGbProxy VTY Reference</title>
 | 
			
		||||
 | 
			
		||||
    <copyright>
 | 
			
		||||
      <year>2019</year>
 | 
			
		||||
    </copyright>
 | 
			
		||||
 | 
			
		||||
    <legalnotice>
 | 
			
		||||
      <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
 | 
			
		||||
      </para>
 | 
			
		||||
    </legalnotice>
 | 
			
		||||
  </info>
 | 
			
		||||
 | 
			
		||||
  <!-- Main chapters-->
 | 
			
		||||
  &chapter-vty;
 | 
			
		||||
</book>
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +19,8 @@ include::./common/chapters/logging.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/configuration.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/cs7-config.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gb.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/control_if.adoc[]
 | 
			
		||||
 
 | 
			
		||||
@@ -61,12 +61,6 @@ interact_vty \
 | 
			
		||||
	4245 \
 | 
			
		||||
	osmo-sgsn -c "../examples/osmo-sgsn/osmo-sgsn.cfg"
 | 
			
		||||
 | 
			
		||||
interact_vty \
 | 
			
		||||
	"update_vty_reference" \
 | 
			
		||||
	"vty-osmogbproxy/gbproxy_vty_reference.xml" \
 | 
			
		||||
	4246 \
 | 
			
		||||
	osmo-gbproxy -c "../examples/osmo-gbproxy/osmo-gbproxy.cfg"
 | 
			
		||||
 | 
			
		||||
interact_vty \
 | 
			
		||||
	"update_counters" \
 | 
			
		||||
	"chapters/counters_generated.adoc" \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
 | 
			
		||||
    <node id='config-gbproxy'>
 | 
			
		||||
	    <description>Configure the Gb proxy</description>
 | 
			
		||||
    </node>
 | 
			
		||||
</vtydoc>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -2,7 +2,6 @@ noinst_HEADERS = \
 | 
			
		||||
	common.h \
 | 
			
		||||
	crc24.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	gb_proxy.h \
 | 
			
		||||
	gprs_gb.h \
 | 
			
		||||
	gprs_gb_parse.h \
 | 
			
		||||
	gprs_gmm.h \
 | 
			
		||||
@@ -24,6 +23,7 @@ noinst_HEADERS = \
 | 
			
		||||
	gprs_utils.h \
 | 
			
		||||
	gtphub.h \
 | 
			
		||||
	sgsn.h \
 | 
			
		||||
	sgsn_rim.h \
 | 
			
		||||
	signal.h \
 | 
			
		||||
	slhc.h \
 | 
			
		||||
	v42bis.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -8,38 +8,26 @@
 | 
			
		||||
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DRLL,
 | 
			
		||||
	DCC,
 | 
			
		||||
	DMM,
 | 
			
		||||
	DRR,
 | 
			
		||||
	DRSL,
 | 
			
		||||
	DNM,
 | 
			
		||||
	DMNCC,
 | 
			
		||||
	DPAG,
 | 
			
		||||
	DMEAS,
 | 
			
		||||
	DSCCP,
 | 
			
		||||
	DMSC,
 | 
			
		||||
	DHO,
 | 
			
		||||
	DDB,
 | 
			
		||||
	DREF,
 | 
			
		||||
	DGPRS,
 | 
			
		||||
	DNS,
 | 
			
		||||
	DBSSGP,
 | 
			
		||||
	DLLC,
 | 
			
		||||
	DSNDCP,
 | 
			
		||||
	DSLHC,
 | 
			
		||||
	DNAT,
 | 
			
		||||
	DCTRL,
 | 
			
		||||
	DFILTER,
 | 
			
		||||
	DGTPHUB,
 | 
			
		||||
	DRANAP,
 | 
			
		||||
	DSUA,
 | 
			
		||||
	DV42BIS,
 | 
			
		||||
	DPCU,
 | 
			
		||||
	DVLR,
 | 
			
		||||
	DIUCS,
 | 
			
		||||
	DSIGTRAN,
 | 
			
		||||
	DGTP,
 | 
			
		||||
	DOBJ,
 | 
			
		||||
	DRIM,
 | 
			
		||||
	Debug_LastEntry,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,334 +0,0 @@
 | 
			
		||||
#ifndef _GB_PROXY_H
 | 
			
		||||
#define _GB_PROXY_H
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <regex.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_INIT_VU_GEN_TX 256
 | 
			
		||||
 | 
			
		||||
struct rate_ctr_group;
 | 
			
		||||
struct gprs_gb_parse_context;
 | 
			
		||||
struct tlv_parsed;
 | 
			
		||||
 | 
			
		||||
enum gbproxy_global_ctr {
 | 
			
		||||
	GBPROX_GLOB_CTR_INV_BVCI,
 | 
			
		||||
	GBPROX_GLOB_CTR_INV_LAI,
 | 
			
		||||
	GBPROX_GLOB_CTR_INV_RAI,
 | 
			
		||||
	GBPROX_GLOB_CTR_INV_NSEI,
 | 
			
		||||
	GBPROX_GLOB_CTR_PROTO_ERR_BSS,
 | 
			
		||||
	GBPROX_GLOB_CTR_PROTO_ERR_SGSN,
 | 
			
		||||
	GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS,
 | 
			
		||||
	GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN,
 | 
			
		||||
	GBPROX_GLOB_CTR_RESTART_RESET_SGSN,
 | 
			
		||||
	GBPROX_GLOB_CTR_TX_ERR_SGSN,
 | 
			
		||||
	GBPROX_GLOB_CTR_OTHER_ERR,
 | 
			
		||||
	GBPROX_GLOB_CTR_PATCH_PEER_ERR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gbproxy_peer_ctr {
 | 
			
		||||
	GBPROX_PEER_CTR_BLOCKED,
 | 
			
		||||
	GBPROX_PEER_CTR_UNBLOCKED,
 | 
			
		||||
	GBPROX_PEER_CTR_DROPPED,
 | 
			
		||||
	GBPROX_PEER_CTR_INV_NSEI,
 | 
			
		||||
	GBPROX_PEER_CTR_TX_ERR,
 | 
			
		||||
	GBPROX_PEER_CTR_RAID_PATCHED_BSS,
 | 
			
		||||
	GBPROX_PEER_CTR_RAID_PATCHED_SGSN,
 | 
			
		||||
	GBPROX_PEER_CTR_APN_PATCHED,
 | 
			
		||||
	GBPROX_PEER_CTR_TLLI_PATCHED_BSS,
 | 
			
		||||
	GBPROX_PEER_CTR_TLLI_PATCHED_SGSN,
 | 
			
		||||
	GBPROX_PEER_CTR_PTMSI_PATCHED_BSS,
 | 
			
		||||
	GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN,
 | 
			
		||||
	GBPROX_PEER_CTR_PATCH_CRYPT_ERR,
 | 
			
		||||
	GBPROX_PEER_CTR_PATCH_ERR,
 | 
			
		||||
	GBPROX_PEER_CTR_ATTACH_REQS,
 | 
			
		||||
	GBPROX_PEER_CTR_ATTACH_REJS,
 | 
			
		||||
	GBPROX_PEER_CTR_ATTACH_ACKS,
 | 
			
		||||
	GBPROX_PEER_CTR_ATTACH_COMPLS,
 | 
			
		||||
	GBPROX_PEER_CTR_RA_UPD_REQS,
 | 
			
		||||
	GBPROX_PEER_CTR_RA_UPD_REJS,
 | 
			
		||||
	GBPROX_PEER_CTR_RA_UPD_ACKS,
 | 
			
		||||
	GBPROX_PEER_CTR_RA_UPD_COMPLS,
 | 
			
		||||
	GBPROX_PEER_CTR_GMM_STATUS_BSS,
 | 
			
		||||
	GBPROX_PEER_CTR_GMM_STATUS_SGSN,
 | 
			
		||||
	GBPROX_PEER_CTR_DETACH_REQS,
 | 
			
		||||
	GBPROX_PEER_CTR_DETACH_ACKS,
 | 
			
		||||
	GBPROX_PEER_CTR_PDP_ACT_REQS,
 | 
			
		||||
	GBPROX_PEER_CTR_PDP_ACT_REJS,
 | 
			
		||||
	GBPROX_PEER_CTR_PDP_ACT_ACKS,
 | 
			
		||||
	GBPROX_PEER_CTR_PDP_DEACT_REQS,
 | 
			
		||||
	GBPROX_PEER_CTR_PDP_DEACT_ACKS,
 | 
			
		||||
	GBPROX_PEER_CTR_TLLI_UNKNOWN,
 | 
			
		||||
	GBPROX_PEER_CTR_TLLI_CACHE_SIZE,
 | 
			
		||||
	GBPROX_PEER_CTR_LAST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gbproxy_keep_mode {
 | 
			
		||||
	GBPROX_KEEP_NEVER,	/* don't ever keep TLLI/IMSI state of de-registered subscribers */
 | 
			
		||||
	GBPROX_KEEP_REATTACH,	/* keep if re-attach has been requested by SGSN */
 | 
			
		||||
	GBPROX_KEEP_IDENTIFIED,	/* keep if we had resolved an IMSI */
 | 
			
		||||
	GBPROX_KEEP_ALWAYS,	/* always keep */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gbproxy_match_id {
 | 
			
		||||
	GBPROX_MATCH_PATCHING,	/* match rule on whether or not we should patch */
 | 
			
		||||
	GBPROX_MATCH_ROUTING,	/* match rule on whether or not we should route (2-SGSN) */
 | 
			
		||||
	GBPROX_MATCH_LAST
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gbproxy_match {
 | 
			
		||||
	bool  enable;		/* is this match enabled? */
 | 
			
		||||
	char *re_str;		/* regular expression (for IMSI) in string format */
 | 
			
		||||
	regex_t re_comp;	/* compiled regular expression (for IMSI) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* global gb-proxy configuration */
 | 
			
		||||
struct gbproxy_config {
 | 
			
		||||
	/* parsed from config file */
 | 
			
		||||
	uint16_t nsip_sgsn_nsei;
 | 
			
		||||
 | 
			
		||||
	/* NS instance of libosmogb */
 | 
			
		||||
	struct gprs_ns_inst *nsi;
 | 
			
		||||
 | 
			
		||||
	/* Linked list of all Gb peers (except SGSN) */
 | 
			
		||||
	struct llist_head bts_peers;
 | 
			
		||||
 | 
			
		||||
	/* Counter */
 | 
			
		||||
	struct rate_ctr_group *ctrg;
 | 
			
		||||
 | 
			
		||||
	/* MCC/MNC to be patched into RA-ID on the way from BSS to SGSN? */
 | 
			
		||||
	struct osmo_plmn_id core_plmn;
 | 
			
		||||
 | 
			
		||||
	/* APN to be patched into PDP CTX ACT REQ on the way from BSS to SGSN */
 | 
			
		||||
	uint8_t* core_apn;
 | 
			
		||||
	size_t core_apn_size;
 | 
			
		||||
 | 
			
		||||
	/* Frequency (sec) at which timer to clean stale links is fired (0 disabled) */
 | 
			
		||||
	unsigned int clean_stale_timer_freq;
 | 
			
		||||
	/* If !0, Max age to consider a struct gbproxy_link_info as stale */
 | 
			
		||||
	int tlli_max_age;
 | 
			
		||||
	/* If !0, Max len of gbproxy_peer->list (list of struct gbproxy_link_info) */
 | 
			
		||||
	int tlli_max_len;
 | 
			
		||||
	/* If !0, Max len of gbproxy_link_info->stored_msgs (list of msgb) */
 | 
			
		||||
	uint32_t stored_msgs_max_len;
 | 
			
		||||
 | 
			
		||||
	/* Should the P-TMSI be patched on the fly (required for 2-SGSN config) */
 | 
			
		||||
	bool patch_ptmsi;
 | 
			
		||||
	/* Should the IMSI be acquired by the proxy (required for 2-SGSN config) */
 | 
			
		||||
	bool acquire_imsi;
 | 
			
		||||
	/* Should we route subscribers to two different SGSNs? */
 | 
			
		||||
	bool route_to_sgsn2;
 | 
			
		||||
	/* NSEI of the second SGSN */
 | 
			
		||||
	uint16_t nsip_sgsn2_nsei;
 | 
			
		||||
	/* should we keep a cache of per-subscriber state even after de-registration? */
 | 
			
		||||
	enum gbproxy_keep_mode keep_link_infos;
 | 
			
		||||
 | 
			
		||||
	/* IMSI checking/matching for 2-SGSN routing and patching */
 | 
			
		||||
	struct gbproxy_match matches[GBPROX_MATCH_LAST];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gbproxy_patch_state {
 | 
			
		||||
	struct osmo_plmn_id local_plmn;
 | 
			
		||||
 | 
			
		||||
	/* List of TLLIs for which patching is enabled */
 | 
			
		||||
	struct llist_head logical_links;
 | 
			
		||||
	int logical_link_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* one peer at NS level that we interact with (BSS/PCU) */
 | 
			
		||||
struct gbproxy_peer {
 | 
			
		||||
	/* linked to gbproxy_config.bts_peers */
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* point back to the config */
 | 
			
		||||
	struct gbproxy_config *cfg;
 | 
			
		||||
 | 
			
		||||
	/* NSEI of the peer entity */
 | 
			
		||||
	uint16_t nsei;
 | 
			
		||||
 | 
			
		||||
	/* BVCI used for Point-to-Point to this peer */
 | 
			
		||||
	uint16_t bvci;
 | 
			
		||||
	bool blocked;
 | 
			
		||||
 | 
			
		||||
	/* Routeing Area that this peer is part of (raw 04.08 encoding) */
 | 
			
		||||
	uint8_t ra[6];
 | 
			
		||||
 | 
			
		||||
	/* Counter */
 | 
			
		||||
	struct rate_ctr_group *ctrg;
 | 
			
		||||
 | 
			
		||||
	/* State related to on-the-fly patching of certain messages */
 | 
			
		||||
	struct gbproxy_patch_state patch_state;
 | 
			
		||||
 | 
			
		||||
	/* Fired periodically to clean up stale links from list */
 | 
			
		||||
	struct osmo_timer_list clean_stale_timer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gbproxy_tlli_state {
 | 
			
		||||
	/* currently active TLLI */
 | 
			
		||||
	uint32_t current;
 | 
			
		||||
	/* newly-assigned TLLI (e.g. during P-TMSI allocation procedure) */
 | 
			
		||||
	uint32_t assigned;
 | 
			
		||||
	/* has the BSS side validated (confirmed) the new TLLI? */
 | 
			
		||||
	bool bss_validated;
 | 
			
		||||
	/* has the SGSN side validated (confirmed) the new TLLI? */
 | 
			
		||||
	bool net_validated;
 | 
			
		||||
	/* NOTE: once both are validated, we set current = assigned and assigned = 0 */
 | 
			
		||||
 | 
			
		||||
	/* The P-TMSI for this subscriber */
 | 
			
		||||
	uint32_t ptmsi;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* One TLLI (= UE, = Subscriber) served via this proxy */
 | 
			
		||||
struct gbproxy_link_info {
 | 
			
		||||
	/* link to gbproxy_peer.patch_state.logical_links */
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* TLLI on the BSS/PCU side */
 | 
			
		||||
	struct gbproxy_tlli_state tlli;
 | 
			
		||||
	/* TLLI on the SGSN side (can be different in case of P-TMSI patching) */
 | 
			
		||||
	struct gbproxy_tlli_state sgsn_tlli;
 | 
			
		||||
	/* NSEI of the SGSN serving this link */
 | 
			
		||||
	uint32_t sgsn_nsei;
 | 
			
		||||
 | 
			
		||||
	/* timestamp when we last had any contact with this UE */
 | 
			
		||||
	time_t timestamp;
 | 
			
		||||
 | 
			
		||||
	/* IMSI of the subscriber (if/once known) */
 | 
			
		||||
	uint8_t *imsi;
 | 
			
		||||
	size_t imsi_len;
 | 
			
		||||
 | 
			
		||||
	/* is the IMSI acquisition still pending? */
 | 
			
		||||
	bool imsi_acq_pending;
 | 
			
		||||
 | 
			
		||||
	/* queue of stored UL messages (until IMSI acquisition completes and we can
 | 
			
		||||
	 * determine which of the SGSNs we should route this to */
 | 
			
		||||
	struct llist_head stored_msgs;
 | 
			
		||||
	uint32_t stored_msgs_len;
 | 
			
		||||
 | 
			
		||||
	/* generated N(U) we use (required due to IMSI acquisition */
 | 
			
		||||
	unsigned vu_gen_tx_bss;
 | 
			
		||||
 | 
			
		||||
	/* is this subscriber deregistered (TLLI invalidated)? */
 | 
			
		||||
	bool is_deregistered;
 | 
			
		||||
 | 
			
		||||
	/* does this link match either the (2-SGSN) routing or the patching rule? */
 | 
			
		||||
	bool is_matching[GBPROX_MATCH_LAST];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* gb_proxy_vty .c */
 | 
			
		||||
 | 
			
		||||
int gbproxy_vty_init(void);
 | 
			
		||||
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg);
 | 
			
		||||
 | 
			
		||||
/* gb_proxy_ctrl.c */
 | 
			
		||||
int gb_ctrl_cmds_install(void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* gb_proxy.c */
 | 
			
		||||
int gbproxy_init_config(struct gbproxy_config *cfg);
 | 
			
		||||
 | 
			
		||||
/* Main input function for Gb proxy */
 | 
			
		||||
int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci);
 | 
			
		||||
 | 
			
		||||
int gbprox_signal(unsigned int subsys, unsigned int signal,
 | 
			
		||||
		  void *handler_data, void *signal_data);
 | 
			
		||||
 | 
			
		||||
/* Reset all persistent NS-VC's */
 | 
			
		||||
int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi);
 | 
			
		||||
 | 
			
		||||
void gbprox_reset(struct gbproxy_config *cfg);
 | 
			
		||||
 | 
			
		||||
/* TLLI info handling */
 | 
			
		||||
void gbproxy_delete_link_infos(struct gbproxy_peer *peer);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_update_link_state_ul(
 | 
			
		||||
	struct gbproxy_peer *peer, time_t now,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_update_link_state_dl(
 | 
			
		||||
	struct gbproxy_peer *peer, time_t now,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
int gbproxy_update_link_state_after(
 | 
			
		||||
	struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
 | 
			
		||||
	time_t now, struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now);
 | 
			
		||||
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
 | 
			
		||||
			 struct gbproxy_link_info *link_info);
 | 
			
		||||
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info);
 | 
			
		||||
 | 
			
		||||
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
 | 
			
		||||
			      struct gbproxy_link_info *link_info);
 | 
			
		||||
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
 | 
			
		||||
			      const uint8_t *imsi, size_t imsi_len);
 | 
			
		||||
void gbproxy_detach_link_info(struct gbproxy_peer *peer,
 | 
			
		||||
			      struct gbproxy_link_info *link_info);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer);
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer, uint32_t tlli);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
 | 
			
		||||
	struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer, uint32_t tlli);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	uint32_t tlli, uint32_t sgsn_nsei);
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	uint32_t ptmsi);
 | 
			
		||||
 | 
			
		||||
int gbproxy_imsi_matches(
 | 
			
		||||
	struct gbproxy_config *cfg,
 | 
			
		||||
	enum gbproxy_match_id match_id,
 | 
			
		||||
	struct gbproxy_link_info *link_info);
 | 
			
		||||
uint32_t gbproxy_map_tlli(
 | 
			
		||||
	uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss);
 | 
			
		||||
 | 
			
		||||
/* needed by gb_proxy_tlli.h */
 | 
			
		||||
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi);
 | 
			
		||||
uint32_t gbproxy_make_sgsn_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
 | 
			
		||||
	uint32_t bss_tlli);
 | 
			
		||||
void gbproxy_reset_link(struct gbproxy_link_info *link_info);
 | 
			
		||||
int gbproxy_check_imsi(
 | 
			
		||||
	struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len);
 | 
			
		||||
 | 
			
		||||
/* Message patching */
 | 
			
		||||
void gbproxy_patch_bssgp(
 | 
			
		||||
	struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
 | 
			
		||||
	struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
 | 
			
		||||
	int *len_change, struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
 | 
			
		||||
int gbproxy_patch_llc(
 | 
			
		||||
	struct msgb *msg, uint8_t *llc, size_t llc_len,
 | 
			
		||||
	struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
 | 
			
		||||
	int *len_change, struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
 | 
			
		||||
int gbproxy_set_patch_filter(
 | 
			
		||||
	struct gbproxy_match *match, const char *filter, const char **err_msg);
 | 
			
		||||
void gbproxy_clear_patch_filter(struct gbproxy_match *match);
 | 
			
		||||
 | 
			
		||||
/* Peer handling */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_bvci(
 | 
			
		||||
	struct gbproxy_config *cfg, uint16_t bvci);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_nsei(
 | 
			
		||||
	struct gbproxy_config *cfg, uint16_t nsei);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_rai(
 | 
			
		||||
	struct gbproxy_config *cfg, const uint8_t *ra);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_lai(
 | 
			
		||||
	struct gbproxy_config *cfg, const uint8_t *la);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_lac(
 | 
			
		||||
	struct gbproxy_config *cfg, const uint8_t *la);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(
 | 
			
		||||
	struct gbproxy_config *cfg, struct tlv_parsed *tp);
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci);
 | 
			
		||||
void gbproxy_peer_free(struct gbproxy_peer *peer);
 | 
			
		||||
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -8,7 +8,13 @@
 | 
			
		||||
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
 | 
			
		||||
			   bool drop_cipherable);
 | 
			
		||||
/* Has to be called whenever any PDU (signaling, data, ...) has been received */
 | 
			
		||||
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx);
 | 
			
		||||
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx, const struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* page a MS in its routing area */
 | 
			
		||||
int gprs_gb_page_ps_ra(struct sgsn_mm_ctx *mmctx);
 | 
			
		||||
 | 
			
		||||
/* called by the bssgp layer to send NS PDUs */
 | 
			
		||||
int gprs_gb_send_cb(void *ctx, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* called by the ns layer */
 | 
			
		||||
int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,8 @@ int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
 | 
			
		||||
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
 | 
			
		||||
			struct gprs_gb_parse_context *parse_ctx);
 | 
			
		||||
 | 
			
		||||
int gprs_gb_parse_tlli(const uint8_t *bssgp, size_t bssgp_len, uint32_t *tlli);
 | 
			
		||||
 | 
			
		||||
const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
 | 
			
		||||
				 const char *default_msg_name);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_sgsn.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 24.008 § 4.1.3.3 GMM mobility management states on the network side */
 | 
			
		||||
enum gmm_fsm_states {
 | 
			
		||||
@@ -23,6 +25,12 @@ enum gmm_fsm_events {
 | 
			
		||||
	E_GMM_SUSPEND,
 | 
			
		||||
	E_GMM_RESUME,
 | 
			
		||||
	E_GMM_CLEANUP,
 | 
			
		||||
	E_GMM_RAT_CHANGE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gmm_rat_change_data {
 | 
			
		||||
	enum sgsn_ran_type new_ran_type;
 | 
			
		||||
	struct gprs_llc_llme *llme;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline bool gmm_fsm_is_registered(struct osmo_fsm_inst *fi)
 | 
			
		||||
 
 | 
			
		||||
@@ -35,14 +35,14 @@
 | 
			
		||||
 | 
			
		||||
/* TODO move to osmocom/core/socket.c ? */
 | 
			
		||||
#include <netdb.h> /* for IPPROTO_* etc */
 | 
			
		||||
struct osmo_sockaddr {
 | 
			
		||||
struct sgsn_sockaddr {
 | 
			
		||||
	struct sockaddr_storage a;
 | 
			
		||||
	socklen_t l;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* TODO move to osmocom/core/socket.c ? */
 | 
			
		||||
/*! \brief Initialize a sockaddr
 | 
			
		||||
 * \param[out] addr  Valid osmo_sockaddr pointer to write result to
 | 
			
		||||
 * \param[out] addr  Valid sgsn_sockaddr pointer to write result to
 | 
			
		||||
 * \param[in] family  Address Family like AF_INET, AF_INET6, AF_UNSPEC
 | 
			
		||||
 * \param[in] type  Socket type like SOCK_DGRAM, SOCK_STREAM
 | 
			
		||||
 * \param[in] proto  Protocol like IPPROTO_TCP, IPPROTO_UDP
 | 
			
		||||
@@ -53,16 +53,16 @@ struct osmo_sockaddr {
 | 
			
		||||
 * Copy the first result from a getaddrinfo() call with the given parameters to
 | 
			
		||||
 * *addr and *addr_len. On error, do not change *addr and return nonzero.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sockaddr_init(struct osmo_sockaddr *addr,
 | 
			
		||||
int sgsn_sockaddr_init(struct sgsn_sockaddr *addr,
 | 
			
		||||
		       uint16_t family, uint16_t type, uint8_t proto,
 | 
			
		||||
		       const char *host, uint16_t port);
 | 
			
		||||
 | 
			
		||||
/* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to
 | 
			
		||||
 * osmo_sockaddr_init(). */
 | 
			
		||||
static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr,
 | 
			
		||||
 * sgsn_sockaddr_init(). */
 | 
			
		||||
static inline int sgsn_sockaddr_init_udp(struct sgsn_sockaddr *addr,
 | 
			
		||||
					 const char *host, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
	return sgsn_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
				  host, port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -71,25 +71,25 @@ static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr,
 | 
			
		||||
 * \param[in] addr_str_len  Size of buffer addr_str points at.
 | 
			
		||||
 * \param[out] port_str  Valid pointer to a buffer of length port_str_len.
 | 
			
		||||
 * \param[in] port_str_len  Size of buffer port_str points at.
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by osmo_sockaddr_init().
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by sgsn_sockaddr_init().
 | 
			
		||||
 * \param[in] flags  flags as passed to getnameinfo().
 | 
			
		||||
 * \returns  0 on success, an error code on error.
 | 
			
		||||
 *
 | 
			
		||||
 * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string
 | 
			
		||||
 * representations of the given struct osmo_sockaddr in two caller provided
 | 
			
		||||
 * representations of the given struct sgsn_sockaddr in two caller provided
 | 
			
		||||
 * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric
 | 
			
		||||
 * address and port. Either one of addr_str or port_str may be NULL, in which
 | 
			
		||||
 * case nothing is returned there.
 | 
			
		||||
 *
 | 
			
		||||
 * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */
 | 
			
		||||
int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
 * See also sgsn_sockaddr_to_str() (less flexible, but much more convenient). */
 | 
			
		||||
int sgsn_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
			  char *port_str, size_t port_str_len,
 | 
			
		||||
			  const struct osmo_sockaddr *addr,
 | 
			
		||||
			  const struct sgsn_sockaddr *addr,
 | 
			
		||||
			  int flags);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief concatenate the parts returned by osmo_sockaddr_to_strs().
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by osmo_sockaddr_init().
 | 
			
		||||
/*! \brief concatenate the parts returned by sgsn_sockaddr_to_strs().
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by sgsn_sockaddr_init().
 | 
			
		||||
 * \param[in] buf  A buffer to use for string operations.
 | 
			
		||||
 * \param[in] buf_len  Length of the buffer.
 | 
			
		||||
 * \returns  Address string (in buffer).
 | 
			
		||||
@@ -98,33 +98,33 @@ int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
 * the form "<ip-addr> port <port>". The returned string is valid until the
 | 
			
		||||
 * next invocation of this function.
 | 
			
		||||
 */
 | 
			
		||||
const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
 | 
			
		||||
const char *sgsn_sockaddr_to_strb(const struct sgsn_sockaddr *addr,
 | 
			
		||||
				  char *buf, size_t buf_len);
 | 
			
		||||
 | 
			
		||||
/*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer.
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by osmo_sockaddr_init().
 | 
			
		||||
/*! \brief conveniently return sgsn_sockaddr_to_strb() in a static buffer.
 | 
			
		||||
 * \param[in] addr  Binary representation as returned by sgsn_sockaddr_init().
 | 
			
		||||
 * \returns  Address string in static buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * See osmo_sockaddr_to_strb().
 | 
			
		||||
 * See sgsn_sockaddr_to_strb().
 | 
			
		||||
 *
 | 
			
		||||
 * Note: only one osmo_sockaddr_to_str() call will work per print/log
 | 
			
		||||
 * statement. For two or more, use osmo_sockaddr_to_strb() with a separate
 | 
			
		||||
 * Note: only one sgsn_sockaddr_to_str() call will work per print/log
 | 
			
		||||
 * statement. For two or more, use sgsn_sockaddr_to_strb() with a separate
 | 
			
		||||
 * buffer each.
 | 
			
		||||
 */
 | 
			
		||||
const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr);
 | 
			
		||||
const char *sgsn_sockaddr_to_str(const struct sgsn_sockaddr *addr);
 | 
			
		||||
 | 
			
		||||
/*! \brief compare two osmo_sockaddr.
 | 
			
		||||
/*! \brief compare two sgsn_sockaddr.
 | 
			
		||||
 * \param[in] a  The first address to compare.
 | 
			
		||||
 * \param[in] b  The other address to compare.
 | 
			
		||||
 * \returns 0 if equal, otherwise -1 or 1.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
 | 
			
		||||
		      const struct osmo_sockaddr *b);
 | 
			
		||||
int sgsn_sockaddr_cmp(const struct sgsn_sockaddr *a,
 | 
			
		||||
		      const struct sgsn_sockaddr *b);
 | 
			
		||||
 | 
			
		||||
/*! \brief Overwrite *dst with *src.
 | 
			
		||||
 * Like memcpy(), but copy only the valid bytes. */
 | 
			
		||||
void osmo_sockaddr_copy(struct osmo_sockaddr *dst,
 | 
			
		||||
			const struct osmo_sockaddr *src);
 | 
			
		||||
void sgsn_sockaddr_copy(struct sgsn_sockaddr *dst,
 | 
			
		||||
			const struct sgsn_sockaddr *src);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* general */
 | 
			
		||||
@@ -179,7 +179,7 @@ int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b);
 | 
			
		||||
/* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number
 | 
			
		||||
 * from sa is also returned. */
 | 
			
		||||
int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
 | 
			
		||||
			   const struct osmo_sockaddr *sa);
 | 
			
		||||
			   const struct sgsn_sockaddr *sa);
 | 
			
		||||
 | 
			
		||||
/* expiry */
 | 
			
		||||
 | 
			
		||||
@@ -389,7 +389,7 @@ struct gtphub_peer_port {
 | 
			
		||||
	struct gtphub_peer_addr *peer_addr;
 | 
			
		||||
	uint16_t port;
 | 
			
		||||
	unsigned int ref_count; /* references from other peers' seq_maps */
 | 
			
		||||
	struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */
 | 
			
		||||
	struct sgsn_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */
 | 
			
		||||
	int last_restart_count; /* 0..255 = valid, all else means unknown */
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *counters_io;
 | 
			
		||||
@@ -496,13 +496,13 @@ int gtphub_tunnel_complete(struct gtphub_tunnel *tun);
 | 
			
		||||
int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		      unsigned int side_idx,
 | 
			
		||||
		      unsigned int port_idx,
 | 
			
		||||
		      const struct osmo_sockaddr *from_addr,
 | 
			
		||||
		      const struct sgsn_sockaddr *from_addr,
 | 
			
		||||
		      uint8_t *buf,
 | 
			
		||||
		      size_t received,
 | 
			
		||||
		      time_t now,
 | 
			
		||||
		      uint8_t **reply_buf,
 | 
			
		||||
		      struct osmo_fd **to_ofd,
 | 
			
		||||
		      struct osmo_sockaddr *to_addr);
 | 
			
		||||
		      struct sgsn_sockaddr *to_addr);
 | 
			
		||||
 | 
			
		||||
struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
 | 
			
		||||
					  struct gtphub_bind *bind,
 | 
			
		||||
@@ -510,7 +510,7 @@ struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
 | 
			
		||||
					  uint16_t port);
 | 
			
		||||
 | 
			
		||||
struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
 | 
			
		||||
					     const struct osmo_sockaddr *addr);
 | 
			
		||||
					     const struct sgsn_sockaddr *addr);
 | 
			
		||||
 | 
			
		||||
void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
 | 
			
		||||
			  struct gsn_addr *resolved_addr,
 | 
			
		||||
@@ -519,5 +519,5 @@ void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
 | 
			
		||||
const char *gtphub_port_str(struct gtphub_peer_port *port);
 | 
			
		||||
 | 
			
		||||
int gtphub_write(const struct osmo_fd *to,
 | 
			
		||||
		 const struct osmo_sockaddr *to_addr,
 | 
			
		||||
		 const struct sgsn_sockaddr *to_addr,
 | 
			
		||||
		 const uint8_t *buf, size_t buf_len);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/crypt/gprs_cipher.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_sgsn.h>
 | 
			
		||||
#include <osmocom/gsm/oap_client.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_client.h>
 | 
			
		||||
@@ -69,7 +70,7 @@ struct sgsn_config {
 | 
			
		||||
	struct sockaddr_in gtp_listenaddr;
 | 
			
		||||
 | 
			
		||||
	/* misc */
 | 
			
		||||
	struct gprs_ns_inst *nsi;
 | 
			
		||||
	struct gprs_ns2_inst *nsi;
 | 
			
		||||
 | 
			
		||||
	enum sgsn_auth_policy auth_policy;
 | 
			
		||||
	enum gprs_ciph_algo cipher;
 | 
			
		||||
@@ -157,7 +158,7 @@ char *sgsn_gtp_ntoa(struct ul16_t *ul);
 | 
			
		||||
/* sgsn.c */
 | 
			
		||||
 | 
			
		||||
/* Main input function for Gb proxy */
 | 
			
		||||
int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
 | 
			
		||||
int sgsn_rcvmsg(struct msgb *msg, struct gprs_ns2_vc *nsvc, uint16_t ns_bvci);
 | 
			
		||||
 | 
			
		||||
/* sgsn_libgtp.c */
 | 
			
		||||
struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								include/osmocom/sgsn/sgsn_rim.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								include/osmocom/sgsn/sgsn_rim.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
int sgsn_rim_rx(struct osmo_bssgp_prim *bp, struct msgb *msg);
 | 
			
		||||
@@ -16,19 +16,16 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
app_configs = {
 | 
			
		||||
    "gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
 | 
			
		||||
             "doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
 | 
			
		||||
    "sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
 | 
			
		||||
    "gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
apps = [(4246, "src/gbproxy/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
 | 
			
		||||
        (4245, "src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn"),
 | 
			
		||||
apps = [(4245, "src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn"),
 | 
			
		||||
        (4253, "src/gtphub/osmo-gtphub", "OsmoGTPhub", "gtphub")
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
vty_command = ["./src/sgsn/osmo-sgsn", "-c",
 | 
			
		||||
               "doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
 | 
			
		||||
 | 
			
		||||
vty_app = apps[1]
 | 
			
		||||
vty_app = apps[0]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	gprs \
 | 
			
		||||
	sgsn \
 | 
			
		||||
	gbproxy \
 | 
			
		||||
	gtphub \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,46 +0,0 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-fno-strict-aliasing \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGB_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSUPCLIENT_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(LIBGTP_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = \
 | 
			
		||||
	osmo-gbproxy \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_gbproxy_SOURCES = \
 | 
			
		||||
	gb_proxy.c \
 | 
			
		||||
	gb_proxy_main.c \
 | 
			
		||||
	gb_proxy_vty.c \
 | 
			
		||||
	gb_proxy_ctrl.c \
 | 
			
		||||
	gb_proxy_patch.c \
 | 
			
		||||
	gb_proxy_tlli.c \
 | 
			
		||||
	gb_proxy_peer.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
osmo_gbproxy_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_gb_parse.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_llc_parse.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/crc24.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_utils.o \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBGTP_LIBS) \
 | 
			
		||||
	-lrt \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,98 +0,0 @@
 | 
			
		||||
/* Control Interface Implementation for the Gb-proxy */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Daniel Willmann
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/ctrl/control_cmd.h>
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
extern vector ctrl_node_vec;
 | 
			
		||||
 | 
			
		||||
static int get_nsvc_state(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_config *cfg = data;
 | 
			
		||||
	struct gprs_ns_inst *nsi = cfg->nsi;
 | 
			
		||||
	struct gprs_nsvc *nsvc;
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_strdup(cmd, "");
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
 | 
			
		||||
		if (nsvc == nsi->unknown_nsvc)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cmd->reply = gprs_nsvc_state_append(cmd->reply, nsvc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state");
 | 
			
		||||
 | 
			
		||||
static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_config *cfg = data;
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_strdup(cmd, "");
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		struct gprs_ra_id raid;
 | 
			
		||||
		gsm48_parse_ra(&raid, peer->ra);
 | 
			
		||||
 | 
			
		||||
		cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n",
 | 
			
		||||
				peer->nsei, peer->bvci,
 | 
			
		||||
				raid.mcc, raid.mnc,
 | 
			
		||||
				raid.lac, raid.rac,
 | 
			
		||||
				peer->blocked ? "BLOCKED" : "UNBLOCKED");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE_RO(gbproxy_state, "gbproxy-state");
 | 
			
		||||
 | 
			
		||||
static int get_num_peers(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_config *cfg = data;
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_strdup(cmd, "");
 | 
			
		||||
	cmd->reply = talloc_asprintf_append(cmd->reply, "%u", llist_count(&cfg->bts_peers));
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
CTRL_CMD_DEFINE_RO(num_peers, "number-of-peers");
 | 
			
		||||
 | 
			
		||||
int gb_ctrl_cmds_install(void)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_nsvc_state);
 | 
			
		||||
	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_gbproxy_state);
 | 
			
		||||
	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_num_peers);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,398 +0,0 @@
 | 
			
		||||
/* NS-over-IP proxy */
 | 
			
		||||
 | 
			
		||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2010 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <sys/fcntl.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/signal.h>
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
#include <osmocom/sgsn/vty.h>
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/ctrl/ports.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
 | 
			
		||||
void *tall_sgsn_ctx;
 | 
			
		||||
 | 
			
		||||
const char *openbsc_copyright =
 | 
			
		||||
	"Copyright (C) 2010 Harald Welte and On-Waves\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";
 | 
			
		||||
 | 
			
		||||
#define CONFIG_FILE_DEFAULT "osmo-gbproxy.cfg"
 | 
			
		||||
#define CONFIG_FILE_LEGACY "osmo_gbproxy.cfg"
 | 
			
		||||
 | 
			
		||||
static char *config_file = NULL;
 | 
			
		||||
struct gbproxy_config *gbcfg;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
/* Pointer to the SGSN peer */
 | 
			
		||||
extern struct gbprox_peer *gbprox_peer_sgsn;
 | 
			
		||||
 | 
			
		||||
/* call-back function for the NS protocol */
 | 
			
		||||
static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
 | 
			
		||||
		      struct msgb *msg, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case GPRS_NS_EVT_UNIT_DATA:
 | 
			
		||||
		rc = gbprox_rcvmsg(gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
 | 
			
		||||
		if (msg)
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
		rc = -EIO;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void signal_handler(int signal)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signal);
 | 
			
		||||
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
	case SIGINT:
 | 
			
		||||
	case SIGTERM:
 | 
			
		||||
		osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
 | 
			
		||||
		sleep(1);
 | 
			
		||||
		exit(0);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGABRT:
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report
 | 
			
		||||
		 * and then return to the caller, who will abort the process */
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_sgsn_ctx, stderr);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR2:
 | 
			
		||||
		talloc_report_full(tall_vty_ctx, stderr);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_usage()
 | 
			
		||||
{
 | 
			
		||||
	printf("Usage: bsc_hack\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
	printf("  Some useful help...\n");
 | 
			
		||||
	printf("  -h --help this text\n");
 | 
			
		||||
	printf("  -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
 | 
			
		||||
	printf("  -D --daemonize Fork the process into a background daemon\n");
 | 
			
		||||
	printf("  -c --config-file filename The config file to use [%s]\n", CONFIG_FILE_DEFAULT);
 | 
			
		||||
	printf("  -s --disable-color\n");
 | 
			
		||||
	printf("  -T --timestamp Prefix every log line with a timestamp\n");
 | 
			
		||||
	printf("  -V --version. Print the version.\n");
 | 
			
		||||
	printf("  -e --log-level number. Set a global loglevel.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_options(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int option_index = 0, c;
 | 
			
		||||
		static struct option long_options[] = {
 | 
			
		||||
			{ "help", 0, 0, 'h' },
 | 
			
		||||
			{ "debug", 1, 0, 'd' },
 | 
			
		||||
			{ "daemonize", 0, 0, 'D' },
 | 
			
		||||
			{ "config-file", 1, 0, 'c' },
 | 
			
		||||
			{ "disable-color", 0, 0, 's' },
 | 
			
		||||
			{ "timestamp", 0, 0, 'T' },
 | 
			
		||||
			{ "version", 0, 0, 'V' },
 | 
			
		||||
			{ "log-level", 1, 0, 'e' },
 | 
			
		||||
			{ 0, 0, 0, 0 }
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		c = getopt_long(argc, argv, "hd:Dc:sTVe:",
 | 
			
		||||
				long_options, &option_index);
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		switch (c) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			print_usage();
 | 
			
		||||
			print_help();
 | 
			
		||||
			exit(0);
 | 
			
		||||
		case 's':
 | 
			
		||||
			log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'd':
 | 
			
		||||
			log_parse_category_mask(osmo_stderr_target, optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'D':
 | 
			
		||||
			daemonize = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
			config_file = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'T':
 | 
			
		||||
			log_set_print_timestamp(osmo_stderr_target, 1);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'e':
 | 
			
		||||
			log_set_log_level(osmo_stderr_target, atoi(optarg));
 | 
			
		||||
			break;
 | 
			
		||||
		case 'V':
 | 
			
		||||
			print_version(1);
 | 
			
		||||
			exit(0);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc > optind) {
 | 
			
		||||
		fprintf(stderr, "Unsupported positional arguments on command line\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
        switch (node) {
 | 
			
		||||
        /* add items that are not config */
 | 
			
		||||
        case CONFIG_NODE:
 | 
			
		||||
                return 0;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
                return 1;
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
        switch (vty->node) {
 | 
			
		||||
        case GBPROXY_NODE:
 | 
			
		||||
        default:
 | 
			
		||||
                if (gbproxy_vty_is_config_node(vty, vty->node))
 | 
			
		||||
                        vty->node = CONFIG_NODE;
 | 
			
		||||
                else
 | 
			
		||||
                        vty->node = ENABLE_NODE;
 | 
			
		||||
 | 
			
		||||
                vty->index = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return vty->node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OsmoGbProxy",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
	.go_parent_cb	= gbproxy_vty_go_parent,
 | 
			
		||||
	.is_config_node	= gbproxy_vty_is_config_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* default categories */
 | 
			
		||||
static struct log_info_cat gprs_categories[] = {
 | 
			
		||||
	[DGPRS] = {
 | 
			
		||||
		.name = "DGPRS",
 | 
			
		||||
		.description = "GPRS Packet Service",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_DEBUG,
 | 
			
		||||
	},
 | 
			
		||||
	[DNS] = {
 | 
			
		||||
		.name = "DNS",
 | 
			
		||||
		.description = "GPRS Network Service (NS)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_INFO,
 | 
			
		||||
	},
 | 
			
		||||
	[DBSSGP] = {
 | 
			
		||||
		.name = "DBSSGP",
 | 
			
		||||
		.description = "GPRS BSS Gateway Protocol (BSSGP)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_DEBUG,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct log_info gprs_log_info = {
 | 
			
		||||
	.filter_fn = gprs_log_filter_fn,
 | 
			
		||||
	.cat = gprs_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(gprs_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool file_exists(const char *path)
 | 
			
		||||
{
 | 
			
		||||
	struct stat sb;
 | 
			
		||||
	return stat(path, &sb) ? false : true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
 | 
			
		||||
	tall_sgsn_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
 | 
			
		||||
	msgb_talloc_ctx_init(tall_sgsn_ctx, 0);
 | 
			
		||||
	vty_info.tall_ctx = tall_sgsn_ctx;
 | 
			
		||||
 | 
			
		||||
	signal(SIGINT, &signal_handler);
 | 
			
		||||
	signal(SIGTERM, &signal_handler);
 | 
			
		||||
	signal(SIGABRT, &signal_handler);
 | 
			
		||||
	signal(SIGUSR1, &signal_handler);
 | 
			
		||||
	signal(SIGUSR2, &signal_handler);
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(tall_sgsn_ctx, &gprs_log_info);
 | 
			
		||||
 | 
			
		||||
	vty_info.copyright = openbsc_copyright;
 | 
			
		||||
	vty_init(&vty_info);
 | 
			
		||||
	logging_vty_add_cmds();
 | 
			
		||||
	osmo_talloc_vty_add_cmds();
 | 
			
		||||
	osmo_stats_vty_add_cmds();
 | 
			
		||||
	gbproxy_vty_init();
 | 
			
		||||
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	/* Backwards compatibility: for years, the default config file name was
 | 
			
		||||
	 * osmo_gbproxy.cfg. All other Osmocom programs use osmo-*.cfg with a
 | 
			
		||||
	 * dash. To be able to use the new config file name without breaking
 | 
			
		||||
	 * previous setups that might rely on the legacy default config file
 | 
			
		||||
	 * name, we need to look for the old config file if no -c option was
 | 
			
		||||
	 * passed AND no file exists with the new default file name. */
 | 
			
		||||
	if (!config_file) {
 | 
			
		||||
		/* No -c option was passed */
 | 
			
		||||
		if (file_exists(CONFIG_FILE_LEGACY)
 | 
			
		||||
		    && !file_exists(CONFIG_FILE_DEFAULT))
 | 
			
		||||
			config_file = CONFIG_FILE_LEGACY;
 | 
			
		||||
		else
 | 
			
		||||
			config_file = CONFIG_FILE_DEFAULT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctr_init(tall_sgsn_ctx);
 | 
			
		||||
	osmo_stats_init(tall_sgsn_ctx);
 | 
			
		||||
 | 
			
		||||
	bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_sgsn_ctx);
 | 
			
		||||
	if (!bssgp_nsi) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config);
 | 
			
		||||
	if (!gbcfg) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	gbproxy_init_config(gbcfg);
 | 
			
		||||
	gbcfg->nsi = bssgp_nsi;
 | 
			
		||||
	gprs_ns_vty_init(bssgp_nsi);
 | 
			
		||||
	gprs_ns_set_log_ss(DNS);
 | 
			
		||||
	bssgp_set_log_ss(DBSSGP);
 | 
			
		||||
	osmo_signal_register_handler(SS_L_NS, &gbprox_signal, gbcfg);
 | 
			
		||||
 | 
			
		||||
	rc = gbproxy_parse_config(config_file, gbcfg);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file '%s'\n", config_file);
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_sgsn_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		exit(1);
 | 
			
		||||
 | 
			
		||||
	/* Start control interface after getting config for
 | 
			
		||||
	 * ctrl_vty_get_bind_addr() */
 | 
			
		||||
	ctrl = ctrl_interface_setup_dynip(gbcfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_GBPROXY, NULL);
 | 
			
		||||
	if (!ctrl) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Failed to create CTRL interface.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (gb_ctrl_cmds_install() != 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Failed to install CTRL commands.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!gprs_nsvc_by_nsei(gbcfg->nsi, gbcfg->nsip_sgsn_nsei)) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
 | 
			
		||||
			"without creating that NSEI before\n",
 | 
			
		||||
			gbcfg->nsip_sgsn_nsei);
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = gprs_ns_nsip_listen(bssgp_nsi);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = gprs_ns_frgre_listen(bssgp_nsi);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
 | 
			
		||||
			"socket. Do you have CAP_NET_RAW?\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (daemonize) {
 | 
			
		||||
		rc = osmo_daemonize();
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			perror("Error during daemonize");
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset all the persistent NS-VCs that we've read from the config */
 | 
			
		||||
	gbprox_reset_persistent_nsvcs(bssgp_nsi);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		rc = osmo_select_main(0);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			exit(3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,465 +0,0 @@
 | 
			
		||||
/* Gb-proxy message patching */
 | 
			
		||||
 | 
			
		||||
/* (C) 2014 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gprs_utils.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_gb_parse.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/protocol/gsm_08_18.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/gsm/apn.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_sgsn_ctx;
 | 
			
		||||
 | 
			
		||||
/* patch RA identifier in place */
 | 
			
		||||
static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_peer *peer,
 | 
			
		||||
			       int to_bss, const char *log_text)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
	struct osmo_plmn_id old_plmn;
 | 
			
		||||
	struct gprs_ra_id raid;
 | 
			
		||||
	enum gbproxy_peer_ctr counter =
 | 
			
		||||
		to_bss ?
 | 
			
		||||
		GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
 | 
			
		||||
		GBPROX_PEER_CTR_RAID_PATCHED_BSS;
 | 
			
		||||
 | 
			
		||||
	if (!state->local_plmn.mcc || !state->local_plmn.mnc)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	gsm48_parse_ra(&raid, (uint8_t *)raid_enc);
 | 
			
		||||
 | 
			
		||||
	old_plmn = (struct osmo_plmn_id){
 | 
			
		||||
		.mcc = raid.mcc,
 | 
			
		||||
		.mnc = raid.mnc,
 | 
			
		||||
		.mnc_3_digits = raid.mnc_3_digits,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (!to_bss) {
 | 
			
		||||
		/* BSS -> SGSN */
 | 
			
		||||
		if (state->local_plmn.mcc)
 | 
			
		||||
			raid.mcc = peer->cfg->core_plmn.mcc;
 | 
			
		||||
 | 
			
		||||
		if (state->local_plmn.mnc) {
 | 
			
		||||
			raid.mnc = peer->cfg->core_plmn.mnc;
 | 
			
		||||
			raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* SGSN -> BSS */
 | 
			
		||||
		if (state->local_plmn.mcc)
 | 
			
		||||
			raid.mcc = state->local_plmn.mcc;
 | 
			
		||||
 | 
			
		||||
		if (state->local_plmn.mnc) {
 | 
			
		||||
			raid.mnc = state->local_plmn.mnc;
 | 
			
		||||
			raid.mnc_3_digits = state->local_plmn.mnc_3_digits;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
	     "Patching %s to %s: "
 | 
			
		||||
	     "%s-%d-%d -> %s\n",
 | 
			
		||||
	     log_text,
 | 
			
		||||
	     to_bss ? "BSS" : "SGSN",
 | 
			
		||||
	     osmo_plmn_name(&old_plmn), raid.lac, raid.rac,
 | 
			
		||||
	     osmo_rai_name(&raid));
 | 
			
		||||
 | 
			
		||||
	gsm48_encode_ra(raid_enc, &raid);
 | 
			
		||||
	rate_ctr_inc(&peer->ctrg->ctr[counter]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gbproxy_patch_apn_ie(struct msgb *msg,
 | 
			
		||||
				 uint8_t *apn_ie, size_t apn_ie_len,
 | 
			
		||||
				 struct gbproxy_peer *peer,
 | 
			
		||||
				 size_t *new_apn_ie_len, const char *log_text)
 | 
			
		||||
{
 | 
			
		||||
	struct apn_ie_hdr {
 | 
			
		||||
		uint8_t iei;
 | 
			
		||||
		uint8_t apn_len;
 | 
			
		||||
		uint8_t apn[0];
 | 
			
		||||
	} *hdr = (void *)apn_ie;
 | 
			
		||||
 | 
			
		||||
	size_t apn_len = hdr->apn_len;
 | 
			
		||||
	uint8_t *apn = hdr->apn;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
 | 
			
		||||
	OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
 | 
			
		||||
 | 
			
		||||
	if (peer->cfg->core_apn_size == 0) {
 | 
			
		||||
		char str1[110];
 | 
			
		||||
		/* Remove the IE */
 | 
			
		||||
		LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
		     "Patching %s to SGSN: Removing APN '%s'\n",
 | 
			
		||||
		     log_text,
 | 
			
		||||
		     osmo_apn_to_str(str1, apn, apn_len));
 | 
			
		||||
 | 
			
		||||
		*new_apn_ie_len = 0;
 | 
			
		||||
		msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Resize the IE */
 | 
			
		||||
		char str1[110];
 | 
			
		||||
		char str2[110];
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
 | 
			
		||||
 | 
			
		||||
		LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
		     "Patching %s to SGSN: "
 | 
			
		||||
		     "Replacing APN '%s' -> '%s'\n",
 | 
			
		||||
		     log_text,
 | 
			
		||||
		     osmo_apn_to_str(str1, apn, apn_len),
 | 
			
		||||
		     osmo_apn_to_str(str2, peer->cfg->core_apn,
 | 
			
		||||
				       peer->cfg->core_apn_size));
 | 
			
		||||
 | 
			
		||||
		*new_apn_ie_len = peer->cfg->core_apn_size + 2;
 | 
			
		||||
		msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
 | 
			
		||||
		memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
 | 
			
		||||
		hdr->apn_len = peer->cfg->core_apn_size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gbproxy_patch_tlli(uint8_t *tlli_enc,
 | 
			
		||||
			      struct gbproxy_peer *peer,
 | 
			
		||||
			      uint32_t new_tlli,
 | 
			
		||||
			      int to_bss, const char *log_text)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t tlli_be;
 | 
			
		||||
	uint32_t tlli;
 | 
			
		||||
	enum gbproxy_peer_ctr counter =
 | 
			
		||||
		to_bss ?
 | 
			
		||||
		GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
 | 
			
		||||
		GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
 | 
			
		||||
 | 
			
		||||
	memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
 | 
			
		||||
	tlli = ntohl(tlli_be);
 | 
			
		||||
 | 
			
		||||
	if (tlli == new_tlli)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
	     "Patching %ss: "
 | 
			
		||||
	     "Replacing %08x -> %08x\n",
 | 
			
		||||
	     log_text, tlli, new_tlli);
 | 
			
		||||
 | 
			
		||||
	tlli_be = htonl(new_tlli);
 | 
			
		||||
	memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&peer->ctrg->ctr[counter]);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
 | 
			
		||||
			       struct gbproxy_peer *peer,
 | 
			
		||||
			       uint32_t new_ptmsi,
 | 
			
		||||
			       int to_bss, const char *log_text)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t ptmsi_be;
 | 
			
		||||
	uint32_t ptmsi;
 | 
			
		||||
	enum gbproxy_peer_ctr counter =
 | 
			
		||||
		to_bss ?
 | 
			
		||||
		GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
 | 
			
		||||
		GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
 | 
			
		||||
	memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
 | 
			
		||||
	ptmsi = ntohl(ptmsi_be);
 | 
			
		||||
 | 
			
		||||
	if (ptmsi == new_ptmsi)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
	     "Patching %ss: "
 | 
			
		||||
	     "Replacing %08x -> %08x\n",
 | 
			
		||||
	     log_text, ptmsi, new_ptmsi);
 | 
			
		||||
 | 
			
		||||
	ptmsi_be = htonl(new_ptmsi);
 | 
			
		||||
	memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&peer->ctrg->ctr[counter]);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
 | 
			
		||||
		     struct gbproxy_peer *peer,
 | 
			
		||||
		     struct gbproxy_link_info *link_info, int *len_change,
 | 
			
		||||
		     struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
 | 
			
		||||
	int have_patched = 0;
 | 
			
		||||
	int fcs;
 | 
			
		||||
	struct gbproxy_config *cfg = peer->cfg;
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->ptmsi_enc && link_info &&
 | 
			
		||||
	    !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
 | 
			
		||||
		uint32_t ptmsi;
 | 
			
		||||
		if (parse_ctx->to_bss)
 | 
			
		||||
			ptmsi = link_info->tlli.ptmsi;
 | 
			
		||||
		else
 | 
			
		||||
			ptmsi = link_info->sgsn_tlli.ptmsi;
 | 
			
		||||
 | 
			
		||||
		if (ptmsi != GSM_RESERVED_TMSI) {
 | 
			
		||||
			if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
 | 
			
		||||
						ptmsi, parse_ctx->to_bss, "P-TMSI"))
 | 
			
		||||
				have_patched = 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* TODO: invalidate old RAI if present (see below) */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
 | 
			
		||||
		uint32_t ptmsi;
 | 
			
		||||
		if (parse_ctx->to_bss)
 | 
			
		||||
			ptmsi = link_info->tlli.ptmsi;
 | 
			
		||||
		else
 | 
			
		||||
			ptmsi = link_info->sgsn_tlli.ptmsi;
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(ptmsi);
 | 
			
		||||
		if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
 | 
			
		||||
					ptmsi, parse_ctx->to_bss, "new P-TMSI"))
 | 
			
		||||
			have_patched = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->raid_enc) {
 | 
			
		||||
		gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss,
 | 
			
		||||
				   parse_ctx->llc_msg_name);
 | 
			
		||||
		have_patched = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
 | 
			
		||||
		/* TODO: Patch to invalid if P-TMSI unknown. */
 | 
			
		||||
		gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
 | 
			
		||||
				   parse_ctx->llc_msg_name);
 | 
			
		||||
		have_patched = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->apn_ie &&
 | 
			
		||||
	    cfg->core_apn &&
 | 
			
		||||
	    !parse_ctx->to_bss &&
 | 
			
		||||
	    gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
 | 
			
		||||
	    cfg->core_apn) {
 | 
			
		||||
		size_t new_len;
 | 
			
		||||
		gbproxy_patch_apn_ie(msg,
 | 
			
		||||
				     parse_ctx->apn_ie, parse_ctx->apn_ie_len,
 | 
			
		||||
				     peer, &new_len, parse_ctx->llc_msg_name);
 | 
			
		||||
		*len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
 | 
			
		||||
 | 
			
		||||
		have_patched = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (have_patched) {
 | 
			
		||||
		llc_len += *len_change;
 | 
			
		||||
		ghp->crc_length += *len_change;
 | 
			
		||||
 | 
			
		||||
		/* Fix FCS */
 | 
			
		||||
		fcs = gprs_llc_fcs(llc, ghp->crc_length);
 | 
			
		||||
		LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
 | 
			
		||||
		     ghp->fcs, fcs);
 | 
			
		||||
 | 
			
		||||
		llc[llc_len - 3] = fcs & 0xff;
 | 
			
		||||
		llc[llc_len - 2] = (fcs >> 8) & 0xff;
 | 
			
		||||
		llc[llc_len - 1] = (fcs >> 16) & 0xff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return have_patched;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */
 | 
			
		||||
void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
 | 
			
		||||
			 struct gbproxy_peer *peer,
 | 
			
		||||
			 struct gbproxy_link_info *link_info, int *len_change,
 | 
			
		||||
			 struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	const char *err_info = NULL;
 | 
			
		||||
	int err_ctr = -1;
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->bssgp_raid_enc)
 | 
			
		||||
		gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer,
 | 
			
		||||
				   parse_ctx->to_bss, "BSSGP");
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->need_decryption &&
 | 
			
		||||
	    (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
 | 
			
		||||
		/* Patching LLC messages has been requested
 | 
			
		||||
		 * explicitly, but the message (including the
 | 
			
		||||
		 * type) is encrypted, so we possibly fail to
 | 
			
		||||
		 * patch the LLC part of the message. */
 | 
			
		||||
		err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
 | 
			
		||||
		err_info = "GMM message is encrypted";
 | 
			
		||||
		goto patch_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
 | 
			
		||||
		/* Happens with unknown (not cached) TLLI coming from
 | 
			
		||||
		 * the SGSN */
 | 
			
		||||
		/* TODO: What shall be done with the message in this case? */
 | 
			
		||||
		err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
 | 
			
		||||
		err_info = "TLLI sent by the SGSN is unknown";
 | 
			
		||||
		goto patch_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!link_info)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
 | 
			
		||||
		uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
 | 
			
		||||
						 link_info, parse_ctx->to_bss);
 | 
			
		||||
 | 
			
		||||
		if (tlli) {
 | 
			
		||||
			gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
 | 
			
		||||
					   parse_ctx->to_bss, "TLLI");
 | 
			
		||||
			parse_ctx->tlli = tlli;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* Internal error */
 | 
			
		||||
			err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
 | 
			
		||||
			err_info = "Replacement TLLI is 0";
 | 
			
		||||
			goto patch_error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
 | 
			
		||||
		uint32_t ptmsi;
 | 
			
		||||
		if (parse_ctx->to_bss)
 | 
			
		||||
			ptmsi = link_info->tlli.ptmsi;
 | 
			
		||||
		else
 | 
			
		||||
			ptmsi = link_info->sgsn_tlli.ptmsi;
 | 
			
		||||
 | 
			
		||||
		if (ptmsi != GSM_RESERVED_TMSI)
 | 
			
		||||
			gbproxy_patch_ptmsi(
 | 
			
		||||
				parse_ctx->bssgp_ptmsi_enc, peer,
 | 
			
		||||
				ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->llc) {
 | 
			
		||||
		uint8_t *llc = parse_ctx->llc;
 | 
			
		||||
		size_t llc_len = parse_ctx->llc_len;
 | 
			
		||||
		int llc_len_change = 0;
 | 
			
		||||
 | 
			
		||||
		gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
 | 
			
		||||
				  &llc_len_change, parse_ctx);
 | 
			
		||||
		/* Note that the APN might have been resized here, but no
 | 
			
		||||
		 * pointer int the parse_ctx will refer to an adress after the
 | 
			
		||||
		 * APN. So it's possible to patch first and do the TLLI
 | 
			
		||||
		 * handling afterwards. */
 | 
			
		||||
 | 
			
		||||
		if (llc_len_change) {
 | 
			
		||||
			llc_len += llc_len_change;
 | 
			
		||||
 | 
			
		||||
			/* Fix LLC IE len */
 | 
			
		||||
			/* TODO: This is a kludge, but the a pointer to the
 | 
			
		||||
			 * start of the IE is not available here */
 | 
			
		||||
			if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
 | 
			
		||||
				/* most probably a one byte length */
 | 
			
		||||
				if (llc_len > 127) {
 | 
			
		||||
					err_info = "Cannot increase size";
 | 
			
		||||
					err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
 | 
			
		||||
					goto patch_error;
 | 
			
		||||
				}
 | 
			
		||||
				llc[-1] = llc_len | 0x80;
 | 
			
		||||
			} else {
 | 
			
		||||
				llc[-2] = (llc_len >> 8) & 0x7f;
 | 
			
		||||
				llc[-1] = llc_len & 0xff;
 | 
			
		||||
			}
 | 
			
		||||
			*len_change += llc_len_change;
 | 
			
		||||
		}
 | 
			
		||||
		/* Note that the tp struct might contain invalid pointers here
 | 
			
		||||
		 * if the LLC field has changed its size */
 | 
			
		||||
		parse_ctx->llc_len = llc_len;
 | 
			
		||||
	}
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
patch_error:
 | 
			
		||||
	OSMO_ASSERT(err_ctr >= 0);
 | 
			
		||||
	rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
 | 
			
		||||
	LOGP(DGPRS, LOGL_ERROR,
 | 
			
		||||
	     "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
 | 
			
		||||
	     msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
 | 
			
		||||
	     err_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_clear_patch_filter(struct gbproxy_match *match)
 | 
			
		||||
{
 | 
			
		||||
	if (match->enable) {
 | 
			
		||||
		regfree(&match->re_comp);
 | 
			
		||||
		match->enable = false;
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(match->re_str);
 | 
			
		||||
	match->re_str = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
 | 
			
		||||
		const char **err_msg)
 | 
			
		||||
{
 | 
			
		||||
	static char err_buf[300];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	gbproxy_clear_patch_filter(match);
 | 
			
		||||
 | 
			
		||||
	if (!filter)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	rc = regcomp(&match->re_comp, filter,
 | 
			
		||||
		     REG_EXTENDED | REG_NOSUB | REG_ICASE);
 | 
			
		||||
 | 
			
		||||
	if (rc == 0) {
 | 
			
		||||
		match->enable = true;
 | 
			
		||||
		match->re_str = talloc_strdup(tall_sgsn_ctx, filter);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (err_msg) {
 | 
			
		||||
		regerror(rc, &match->re_comp,
 | 
			
		||||
			 err_buf, sizeof(err_buf));
 | 
			
		||||
		*err_msg = err_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_check_imsi(struct gbproxy_match *match,
 | 
			
		||||
		       const uint8_t *imsi, size_t imsi_len)
 | 
			
		||||
{
 | 
			
		||||
	char mi_buf[200];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!match->enable)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	rc = gprs_is_mi_imsi(imsi, imsi_len);
 | 
			
		||||
	if (rc > 0)
 | 
			
		||||
		rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
 | 
			
		||||
	if (rc <= 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
 | 
			
		||||
		     osmo_hexdump(imsi, imsi_len));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
 | 
			
		||||
 | 
			
		||||
	rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
 | 
			
		||||
	if (rc == REG_NOMATCH) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		       "IMSI '%s' doesn't match pattern '%s'\n",
 | 
			
		||||
		       mi_buf, match->re_str);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,240 +0,0 @@
 | 
			
		||||
/* Gb proxy peer handling */
 | 
			
		||||
 | 
			
		||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2010-2013 by On-Waves
 | 
			
		||||
 * (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/protocol/gsm_08_18.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/gsm/tlv.h>
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_sgsn_ctx;
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc peer_ctr_description[] = {
 | 
			
		||||
	{ "blocked",	   "BVC Block                       " },
 | 
			
		||||
	{ "unblocked",	   "BVC Unblock                     " },
 | 
			
		||||
	{ "dropped",	   "BVC blocked, dropped packet     " },
 | 
			
		||||
	{ "inv-nsei",	   "NSEI mismatch                   " },
 | 
			
		||||
	{ "tx-err",	   "NS Transmission error           " },
 | 
			
		||||
	{ "raid-mod:bss",  "RAID patched              (BSS )" },
 | 
			
		||||
	{ "raid-mod:sgsn", "RAID patched              (SGSN)" },
 | 
			
		||||
	{ "apn-mod:sgsn",  "APN patched                     " },
 | 
			
		||||
	{ "tlli-mod:bss",  "TLLI patched              (BSS )" },
 | 
			
		||||
	{ "tlli-mod:sgsn", "TLLI patched              (SGSN)" },
 | 
			
		||||
	{ "ptmsi-mod:bss", "P-TMSI patched            (BSS )" },
 | 
			
		||||
	{ "ptmsi-mod:sgsn","P-TMSI patched            (SGSN)" },
 | 
			
		||||
	{ "mod-crypt-err", "Patch error: encrypted          " },
 | 
			
		||||
	{ "mod-err",	   "Patch error: other              " },
 | 
			
		||||
	{ "attach-reqs",   "Attach Request count            " },
 | 
			
		||||
	{ "attach-rejs",   "Attach Reject count             " },
 | 
			
		||||
	{ "attach-acks",   "Attach Accept count             " },
 | 
			
		||||
	{ "attach-cpls",   "Attach Completed count          " },
 | 
			
		||||
	{ "ra-upd-reqs",   "RoutingArea Update Request count" },
 | 
			
		||||
	{ "ra-upd-rejs",   "RoutingArea Update Reject count " },
 | 
			
		||||
	{ "ra-upd-acks",   "RoutingArea Update Accept count " },
 | 
			
		||||
	{ "ra-upd-cpls",   "RoutingArea Update Compltd count" },
 | 
			
		||||
	{ "gmm-status",    "GMM Status count           (BSS)" },
 | 
			
		||||
	{ "gmm-status",    "GMM Status count          (SGSN)" },
 | 
			
		||||
	{ "detach-reqs",   "Detach Request count            " },
 | 
			
		||||
	{ "detach-acks",   "Detach Accept count             " },
 | 
			
		||||
	{ "pdp-act-reqs",  "PDP Activation Request count    " },
 | 
			
		||||
	{ "pdp-act-rejs",  "PDP Activation Reject count     " },
 | 
			
		||||
	{ "pdp-act-acks",  "PDP Activation Accept count     " },
 | 
			
		||||
	{ "pdp-deact-reqs","PDP Deactivation Request count  " },
 | 
			
		||||
	{ "pdp-deact-acks","PDP Deactivation Accept count   " },
 | 
			
		||||
	{ "tlli-unknown",  "TLLI from SGSN unknown          " },
 | 
			
		||||
	{ "tlli-cache",    "TLLI cache size                 " },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc peer_ctrg_desc = {
 | 
			
		||||
	.group_name_prefix = "gbproxy:peer",
 | 
			
		||||
	.group_description = "GBProxy Peer Statistics",
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(peer_ctr_description),
 | 
			
		||||
	.ctr_desc = peer_ctr_description,
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_PEER,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Find the gbprox_peer by its BVCI */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		if (peer->bvci == bvci)
 | 
			
		||||
			return peer;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the gbprox_peer by its NSEI */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg,
 | 
			
		||||
					  uint16_t nsei)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		if (peer->nsei == nsei)
 | 
			
		||||
			return peer;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look-up a peer by its Routeing Area Identification (RAI) */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg,
 | 
			
		||||
					 const uint8_t *ra)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		if (!memcmp(peer->ra, ra, 6))
 | 
			
		||||
			return peer;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look-up a peer by its Location Area Identification (LAI) */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg,
 | 
			
		||||
					 const uint8_t *la)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		if (!memcmp(peer->ra, la, 5))
 | 
			
		||||
			return peer;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look-up a peer by its Location Area Code (LAC) */
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg,
 | 
			
		||||
					 const uint8_t *la)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	llist_for_each_entry(peer, &cfg->bts_peers, list) {
 | 
			
		||||
		if (!memcmp(peer->ra + 3, la + 3, 2))
 | 
			
		||||
			return peer;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg,
 | 
			
		||||
					       struct tlv_parsed *tp)
 | 
			
		||||
{
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
 | 
			
		||||
		uint16_t bvci;
 | 
			
		||||
 | 
			
		||||
		bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
 | 
			
		||||
		if (bvci >= 2)
 | 
			
		||||
			return gbproxy_peer_by_bvci(cfg, bvci);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
 | 
			
		||||
		uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
 | 
			
		||||
		/* Only compare LAC part, since MCC/MNC are possibly patched.
 | 
			
		||||
		 * Since the LAC of different BSS must be different when
 | 
			
		||||
		 * MCC/MNC are patched, collisions shouldn't happen. */
 | 
			
		||||
		return gbproxy_peer_by_lac(cfg, rai);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
 | 
			
		||||
		uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
 | 
			
		||||
		return gbproxy_peer_by_lac(cfg, lai);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clean_stale_timer_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	time_t now;
 | 
			
		||||
	struct timespec ts = {0,};
 | 
			
		||||
	struct gbproxy_peer *peer = (struct gbproxy_peer *) data;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
 | 
			
		||||
	now = ts.tv_sec;
 | 
			
		||||
	gbproxy_remove_stale_link_infos(peer, now);
 | 
			
		||||
	if (peer->cfg->clean_stale_timer_freq != 0)
 | 
			
		||||
		osmo_timer_schedule(&peer->clean_stale_timer,
 | 
			
		||||
					peer->cfg->clean_stale_timer_freq, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
 | 
			
		||||
	peer = talloc_zero(tall_sgsn_ctx, struct gbproxy_peer);
 | 
			
		||||
	if (!peer)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	peer->bvci = bvci;
 | 
			
		||||
	peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
 | 
			
		||||
	if (!peer->ctrg) {
 | 
			
		||||
		talloc_free(peer);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	peer->cfg = cfg;
 | 
			
		||||
 | 
			
		||||
	llist_add(&peer->list, &cfg->bts_peers);
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&peer->patch_state.logical_links);
 | 
			
		||||
 | 
			
		||||
	osmo_timer_setup(&peer->clean_stale_timer, clean_stale_timer_cb, peer);
 | 
			
		||||
	if (peer->cfg->clean_stale_timer_freq != 0)
 | 
			
		||||
		osmo_timer_schedule(&peer->clean_stale_timer,
 | 
			
		||||
					peer->cfg->clean_stale_timer_freq, 0);
 | 
			
		||||
 | 
			
		||||
	return peer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_peer_free(struct gbproxy_peer *peer)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(&peer->list);
 | 
			
		||||
	osmo_timer_del(&peer->clean_stale_timer);
 | 
			
		||||
	gbproxy_delete_link_infos(peer);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_group_free(peer->ctrg);
 | 
			
		||||
	peer->ctrg = NULL;
 | 
			
		||||
 | 
			
		||||
	talloc_free(peer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	int counter = 0;
 | 
			
		||||
	struct gbproxy_peer *peer, *tmp;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) {
 | 
			
		||||
		if (peer->nsei != nsei)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (bvci && peer->bvci != bvci)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		gbproxy_peer_free(peer);
 | 
			
		||||
		counter += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return counter;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,723 +0,0 @@
 | 
			
		||||
/* Gb-proxy TLLI state handling */
 | 
			
		||||
 | 
			
		||||
/* (C) 2014 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gprs_utils.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_gb_parse.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
 | 
			
		||||
					    uint32_t tlli)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	if (!tlli)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(link_info, &state->logical_links, list)
 | 
			
		||||
		if (link_info->tlli.current == tlli ||
 | 
			
		||||
		    link_info->tlli.assigned == tlli)
 | 
			
		||||
			return link_info;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	uint32_t ptmsi)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	if (ptmsi == GSM_RESERVED_TMSI)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(link_info, &state->logical_links, list)
 | 
			
		||||
		if (link_info->tlli.ptmsi == ptmsi)
 | 
			
		||||
			return link_info;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	uint32_t tlli)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	if (!tlli)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Don't care about the NSEI */
 | 
			
		||||
	llist_for_each_entry(link_info, &state->logical_links, list)
 | 
			
		||||
		if (link_info->sgsn_tlli.current == tlli ||
 | 
			
		||||
		     link_info->sgsn_tlli.assigned == tlli)
 | 
			
		||||
			return link_info;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	uint32_t tlli, uint32_t sgsn_nsei)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	if (!tlli)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(link_info, &state->logical_links, list)
 | 
			
		||||
		if ((link_info->sgsn_tlli.current == tlli ||
 | 
			
		||||
		     link_info->sgsn_tlli.assigned == tlli) &&
 | 
			
		||||
		    link_info->sgsn_nsei == sgsn_nsei)
 | 
			
		||||
			return link_info;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	const uint8_t *imsi,
 | 
			
		||||
	size_t imsi_len)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	if (!gprs_is_mi_imsi(imsi, imsi_len))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(link_info, &state->logical_links, list) {
 | 
			
		||||
		if (link_info->imsi_len != imsi_len)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		return link_info;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg, *nxt;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
 | 
			
		||||
		llist_del(&msg->list);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
 | 
			
		||||
			 struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	gbproxy_link_info_discard_messages(link_info);
 | 
			
		||||
 | 
			
		||||
	llist_del(&link_info->list);
 | 
			
		||||
	talloc_free(link_info);
 | 
			
		||||
	state->logical_link_count -= 1;
 | 
			
		||||
 | 
			
		||||
	peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
 | 
			
		||||
		state->logical_link_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info, *nxt;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
 | 
			
		||||
		gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(state->logical_link_count == 0);
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&state->logical_links));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
 | 
			
		||||
			      struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	link_info->timestamp = now;
 | 
			
		||||
	llist_add(&link_info->list, &state->logical_links);
 | 
			
		||||
	state->logical_link_count += 1;
 | 
			
		||||
 | 
			
		||||
	peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
 | 
			
		||||
		state->logical_link_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
	int exceeded_max_len = 0;
 | 
			
		||||
	int deleted_count = 0;
 | 
			
		||||
	int check_for_age;
 | 
			
		||||
 | 
			
		||||
	if (peer->cfg->tlli_max_len > 0)
 | 
			
		||||
		exceeded_max_len =
 | 
			
		||||
			state->logical_link_count - peer->cfg->tlli_max_len;
 | 
			
		||||
 | 
			
		||||
	check_for_age = peer->cfg->tlli_max_age > 0;
 | 
			
		||||
 | 
			
		||||
	for (; exceeded_max_len > 0; exceeded_max_len--) {
 | 
			
		||||
		struct gbproxy_link_info *link_info;
 | 
			
		||||
		OSMO_ASSERT(!llist_empty(&state->logical_links));
 | 
			
		||||
		link_info = llist_entry(state->logical_links.prev,
 | 
			
		||||
					struct gbproxy_link_info,
 | 
			
		||||
					list);
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Removing TLLI %08x from list "
 | 
			
		||||
		     "(stale, length %d, max_len exceeded)\n",
 | 
			
		||||
		     link_info->tlli.current, state->logical_link_count);
 | 
			
		||||
 | 
			
		||||
		gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
		deleted_count += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (check_for_age && !llist_empty(&state->logical_links)) {
 | 
			
		||||
		time_t age;
 | 
			
		||||
		struct gbproxy_link_info *link_info;
 | 
			
		||||
		link_info = llist_entry(state->logical_links.prev,
 | 
			
		||||
					struct gbproxy_link_info,
 | 
			
		||||
					list);
 | 
			
		||||
		age = now - link_info->timestamp;
 | 
			
		||||
		/* age < 0 only happens after system time jumps, discard entry */
 | 
			
		||||
		if (age <= peer->cfg->tlli_max_age && age >= 0) {
 | 
			
		||||
			check_for_age = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Removing TLLI %08x from list "
 | 
			
		||||
		     "(stale, age %d, max_age exceeded)\n",
 | 
			
		||||
		     link_info->tlli.current, (int)age);
 | 
			
		||||
 | 
			
		||||
		gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
		deleted_count += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return deleted_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
 | 
			
		||||
	link_info = talloc_zero(peer, struct gbproxy_link_info);
 | 
			
		||||
	link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
 | 
			
		||||
	link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
 | 
			
		||||
 | 
			
		||||
	link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&link_info->stored_msgs);
 | 
			
		||||
 | 
			
		||||
	return link_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_detach_link_info(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	llist_del(&link_info->list);
 | 
			
		||||
	OSMO_ASSERT(state->logical_link_count > 0);
 | 
			
		||||
	state->logical_link_count -= 1;
 | 
			
		||||
 | 
			
		||||
	peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
 | 
			
		||||
		state->logical_link_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
 | 
			
		||||
			      const uint8_t *imsi, size_t imsi_len)
 | 
			
		||||
{
 | 
			
		||||
	if (!gprs_is_mi_imsi(imsi, imsi_len))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	link_info->imsi_len = imsi_len;
 | 
			
		||||
	link_info->imsi =
 | 
			
		||||
		talloc_realloc_size(link_info, link_info->imsi, imsi_len);
 | 
			
		||||
	OSMO_ASSERT(link_info->imsi != NULL);
 | 
			
		||||
	memcpy(link_info->imsi, imsi, imsi_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
 | 
			
		||||
			   struct gbproxy_peer *peer, uint32_t new_tlli)
 | 
			
		||||
{
 | 
			
		||||
	if (new_tlli == tlli_state->current)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
	     "The TLLI has been reassigned from %08x to %08x\n",
 | 
			
		||||
	     tlli_state->current, new_tlli);
 | 
			
		||||
 | 
			
		||||
	/* Remember assigned TLLI */
 | 
			
		||||
	tlli_state->assigned = new_tlli;
 | 
			
		||||
	tlli_state->bss_validated = false;
 | 
			
		||||
	tlli_state->net_validated = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t gbproxy_map_tlli(uint32_t other_tlli,
 | 
			
		||||
			  struct gbproxy_link_info *link_info, int to_bss)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t tlli = 0;
 | 
			
		||||
	struct gbproxy_tlli_state *src, *dst;
 | 
			
		||||
	if (to_bss) {
 | 
			
		||||
		src = &link_info->sgsn_tlli;
 | 
			
		||||
		dst = &link_info->tlli;
 | 
			
		||||
	} else {
 | 
			
		||||
		src = &link_info->tlli;
 | 
			
		||||
		dst = &link_info->sgsn_tlli;
 | 
			
		||||
	}
 | 
			
		||||
	if (src->current == other_tlli)
 | 
			
		||||
		tlli = dst->current;
 | 
			
		||||
	else if (src->assigned == other_tlli)
 | 
			
		||||
		tlli = dst->assigned;
 | 
			
		||||
 | 
			
		||||
	return tlli;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
 | 
			
		||||
				  uint32_t tlli, int to_bss)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG,
 | 
			
		||||
	     "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
 | 
			
		||||
	     __func__, tlli_state->current, tlli_state->assigned,
 | 
			
		||||
	     tlli_state->net_validated, tlli_state->bss_validated, tlli);
 | 
			
		||||
 | 
			
		||||
	if (!tlli_state->assigned || tlli_state->assigned != tlli)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* TODO: Is this ok? Check spec */
 | 
			
		||||
	if (gprs_tlli_type(tlli) != TLLI_LOCAL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* See GSM 04.08, 4.7.1.5 */
 | 
			
		||||
	if (to_bss)
 | 
			
		||||
		tlli_state->net_validated = true;
 | 
			
		||||
	else
 | 
			
		||||
		tlli_state->bss_validated = true;
 | 
			
		||||
 | 
			
		||||
	if (!tlli_state->bss_validated || !tlli_state->net_validated)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
	     "The TLLI %08x has been validated (was %08x)\n",
 | 
			
		||||
	     tlli_state->assigned, tlli_state->current);
 | 
			
		||||
 | 
			
		||||
	tlli_state->current = tlli;
 | 
			
		||||
	tlli_state->assigned = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
 | 
			
		||||
				    struct gbproxy_link_info *link_info,
 | 
			
		||||
				    time_t now)
 | 
			
		||||
{
 | 
			
		||||
	gbproxy_detach_link_info(peer, link_info);
 | 
			
		||||
	gbproxy_attach_link_info(peer, now, link_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
 | 
			
		||||
					 struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	if (!link_info)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
 | 
			
		||||
		     link_info->tlli.current);
 | 
			
		||||
		gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	link_info->tlli.current = 0;
 | 
			
		||||
	link_info->tlli.assigned = 0;
 | 
			
		||||
	link_info->sgsn_tlli.current = 0;
 | 
			
		||||
	link_info->sgsn_tlli.assigned = 0;
 | 
			
		||||
 | 
			
		||||
	link_info->is_deregistered = true;
 | 
			
		||||
 | 
			
		||||
	gbproxy_reset_link(link_info);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_imsi_matches(struct gbproxy_config *cfg,
 | 
			
		||||
			 enum gbproxy_match_id match_id,
 | 
			
		||||
			 struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_match *match;
 | 
			
		||||
	OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
 | 
			
		||||
 | 
			
		||||
	match = &cfg->matches[match_id];
 | 
			
		||||
	if (!match->enable)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return link_info != NULL && link_info->is_matching[match_id];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gbproxy_assign_imsi(struct gbproxy_peer *peer,
 | 
			
		||||
			 struct gbproxy_link_info *link_info,
 | 
			
		||||
			 struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	int imsi_matches;
 | 
			
		||||
	struct gbproxy_link_info *other_link_info;
 | 
			
		||||
	enum gbproxy_match_id match_id;
 | 
			
		||||
 | 
			
		||||
	/* Make sure that there is a second entry with the same IMSI */
 | 
			
		||||
	other_link_info = gbproxy_link_info_by_imsi(
 | 
			
		||||
		peer, parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
 | 
			
		||||
	if (other_link_info && other_link_info != link_info) {
 | 
			
		||||
		char mi_buf[200];
 | 
			
		||||
		mi_buf[0] = '\0';
 | 
			
		||||
		gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
 | 
			
		||||
				   parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Removing TLLI %08x from list (IMSI %s re-used)\n",
 | 
			
		||||
		     other_link_info->tlli.current, mi_buf);
 | 
			
		||||
		gbproxy_delete_link_info(peer, other_link_info);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Update the IMSI field */
 | 
			
		||||
	gbproxy_update_link_info(link_info,
 | 
			
		||||
				 parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
 | 
			
		||||
	/* Check, whether the IMSI matches */
 | 
			
		||||
	OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
 | 
			
		||||
		    ARRAY_SIZE(peer->cfg->matches));
 | 
			
		||||
	for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
 | 
			
		||||
	     ++match_id) {
 | 
			
		||||
		imsi_matches = gbproxy_check_imsi(
 | 
			
		||||
			&peer->cfg->matches[match_id],
 | 
			
		||||
			parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
		if (imsi_matches >= 0)
 | 
			
		||||
			link_info->is_matching[match_id] = imsi_matches ? true : false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
 | 
			
		||||
			      const struct gbproxy_tlli_state *b)
 | 
			
		||||
{
 | 
			
		||||
	if (a->current && a->current == b->current)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (a->assigned && a->assigned == b->assigned)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gbproxy_remove_matching_link_infos(
 | 
			
		||||
	struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *info, *nxt;
 | 
			
		||||
	struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	/* Make sure that there is no second entry with the same P-TMSI or TLLI */
 | 
			
		||||
	llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
 | 
			
		||||
		if (info == link_info)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
 | 
			
		||||
		    (link_info->sgsn_nsei != info->sgsn_nsei ||
 | 
			
		||||
		     !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
 | 
			
		||||
		     info->tlli.current);
 | 
			
		||||
		gbproxy_delete_link_info(peer, info);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct gbproxy_link_info *gbproxy_get_link_info_ul(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	int *tlli_is_valid,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info = NULL;
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->tlli_enc) {
 | 
			
		||||
		link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
 | 
			
		||||
 | 
			
		||||
		if (link_info) {
 | 
			
		||||
			*tlli_is_valid = 1;
 | 
			
		||||
			return link_info;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*tlli_is_valid = 0;
 | 
			
		||||
 | 
			
		||||
	if (!link_info && parse_ctx->imsi) {
 | 
			
		||||
		link_info = gbproxy_link_info_by_imsi(
 | 
			
		||||
			peer, parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
 | 
			
		||||
		uint32_t bss_ptmsi;
 | 
			
		||||
		gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
 | 
			
		||||
		link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!link_info)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	link_info->is_deregistered = false;
 | 
			
		||||
 | 
			
		||||
	return link_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_update_link_state_ul(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	time_t now,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info;
 | 
			
		||||
	int tlli_is_valid;
 | 
			
		||||
 | 
			
		||||
	link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->tlli_enc && parse_ctx->llc) {
 | 
			
		||||
		uint32_t sgsn_tlli;
 | 
			
		||||
 | 
			
		||||
		if (!link_info) {
 | 
			
		||||
			LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
 | 
			
		||||
			    parse_ctx->tlli);
 | 
			
		||||
			link_info = gbproxy_link_info_alloc(peer);
 | 
			
		||||
			gbproxy_attach_link_info(peer, now, link_info);
 | 
			
		||||
 | 
			
		||||
			/* Setup TLLIs */
 | 
			
		||||
			sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
 | 
			
		||||
							   parse_ctx->tlli);
 | 
			
		||||
			link_info->sgsn_tlli.current = sgsn_tlli;
 | 
			
		||||
			link_info->tlli.current = parse_ctx->tlli;
 | 
			
		||||
		} else if (!tlli_is_valid) {
 | 
			
		||||
			/* New TLLI (info found by IMSI or P-TMSI) */
 | 
			
		||||
			link_info->tlli.current = parse_ctx->tlli;
 | 
			
		||||
			link_info->tlli.assigned = 0;
 | 
			
		||||
			link_info->sgsn_tlli.current =
 | 
			
		||||
				gbproxy_make_sgsn_tlli(peer, link_info,
 | 
			
		||||
						       parse_ctx->tlli);
 | 
			
		||||
			link_info->sgsn_tlli.assigned = 0;
 | 
			
		||||
			gbproxy_touch_link_info(peer, link_info, now);
 | 
			
		||||
		} else {
 | 
			
		||||
			sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
 | 
			
		||||
			if (!sgsn_tlli)
 | 
			
		||||
				sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
 | 
			
		||||
								   parse_ctx->tlli);
 | 
			
		||||
 | 
			
		||||
			gbproxy_validate_tlli(&link_info->tlli,
 | 
			
		||||
					      parse_ctx->tlli, 0);
 | 
			
		||||
			gbproxy_validate_tlli(&link_info->sgsn_tlli,
 | 
			
		||||
					      sgsn_tlli, 0);
 | 
			
		||||
			gbproxy_touch_link_info(peer, link_info, now);
 | 
			
		||||
		}
 | 
			
		||||
	} else if (link_info) {
 | 
			
		||||
		gbproxy_touch_link_info(peer, link_info, now);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
 | 
			
		||||
		gbproxy_assign_imsi(peer, link_info, parse_ctx);
 | 
			
		||||
 | 
			
		||||
	return link_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct gbproxy_link_info *gbproxy_get_link_info_dl(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Which key to use depends on its availability only, if that fails, do
 | 
			
		||||
	 * not retry it with another key (e.g. IMSI). */
 | 
			
		||||
	if (parse_ctx->tlli_enc)
 | 
			
		||||
		link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
 | 
			
		||||
							   parse_ctx->peer_nsei);
 | 
			
		||||
 | 
			
		||||
	/* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
 | 
			
		||||
	 * GSM 08.18, 7.2) instead of using the IMSI as key. */
 | 
			
		||||
	else if (parse_ctx->imsi)
 | 
			
		||||
		link_info = gbproxy_link_info_by_imsi(
 | 
			
		||||
			peer, parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
 | 
			
		||||
	if (link_info)
 | 
			
		||||
		link_info->is_deregistered = false;
 | 
			
		||||
 | 
			
		||||
	return link_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gbproxy_link_info *gbproxy_update_link_state_dl(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	time_t now,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_link_info *link_info = NULL;
 | 
			
		||||
 | 
			
		||||
	link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
 | 
			
		||||
		/* A new P-TMSI has been signalled in the message,
 | 
			
		||||
		 * register new TLLI */
 | 
			
		||||
		uint32_t new_sgsn_ptmsi;
 | 
			
		||||
		uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
 | 
			
		||||
		gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
 | 
			
		||||
 | 
			
		||||
		if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
 | 
			
		||||
			new_bss_ptmsi = link_info->tlli.ptmsi;
 | 
			
		||||
 | 
			
		||||
		if (new_bss_ptmsi == GSM_RESERVED_TMSI)
 | 
			
		||||
			new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
 | 
			
		||||
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Got new PTMSI %08x from SGSN, using %08x for BSS\n",
 | 
			
		||||
		     new_sgsn_ptmsi, new_bss_ptmsi);
 | 
			
		||||
		/* Setup PTMSIs */
 | 
			
		||||
		link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
 | 
			
		||||
		link_info->tlli.ptmsi = new_bss_ptmsi;
 | 
			
		||||
	} else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
 | 
			
		||||
		   !peer->cfg->patch_ptmsi) {
 | 
			
		||||
		/* A new P-TMSI has been signalled in the message with an unknown
 | 
			
		||||
		 * TLLI, create a new link_info */
 | 
			
		||||
		/* TODO: Add a test case for this branch */
 | 
			
		||||
		uint32_t new_ptmsi;
 | 
			
		||||
		gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
 | 
			
		||||
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
 | 
			
		||||
		     parse_ctx->tlli, new_ptmsi);
 | 
			
		||||
 | 
			
		||||
		link_info = gbproxy_link_info_alloc(peer);
 | 
			
		||||
		link_info->sgsn_tlli.current = parse_ctx->tlli;
 | 
			
		||||
		link_info->tlli.current = parse_ctx->tlli;
 | 
			
		||||
		link_info->sgsn_tlli.ptmsi = new_ptmsi;
 | 
			
		||||
		link_info->tlli.ptmsi = new_ptmsi;
 | 
			
		||||
		gbproxy_attach_link_info(peer, now, link_info);
 | 
			
		||||
	} else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
 | 
			
		||||
		   !peer->cfg->patch_ptmsi) {
 | 
			
		||||
		/* Unknown SGSN TLLI, create a new link_info */
 | 
			
		||||
		uint32_t new_ptmsi;
 | 
			
		||||
		link_info = gbproxy_link_info_alloc(peer);
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
 | 
			
		||||
		     parse_ctx->tlli);
 | 
			
		||||
 | 
			
		||||
		gbproxy_attach_link_info(peer, now, link_info);
 | 
			
		||||
 | 
			
		||||
		/* Setup TLLIs */
 | 
			
		||||
		link_info->sgsn_tlli.current = parse_ctx->tlli;
 | 
			
		||||
		link_info->tlli.current = parse_ctx->tlli;
 | 
			
		||||
 | 
			
		||||
		if (!parse_ctx->new_ptmsi_enc)
 | 
			
		||||
			return link_info;
 | 
			
		||||
		/* A new P-TMSI has been signalled in the message */
 | 
			
		||||
 | 
			
		||||
		gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Assigning new P-TMSI %08x\n", new_ptmsi);
 | 
			
		||||
		/* Setup P-TMSIs */
 | 
			
		||||
		link_info->sgsn_tlli.ptmsi = new_ptmsi;
 | 
			
		||||
		link_info->tlli.ptmsi = new_ptmsi;
 | 
			
		||||
	} else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
 | 
			
		||||
		uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
 | 
			
		||||
						     link_info, 1);
 | 
			
		||||
		gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
 | 
			
		||||
		gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
 | 
			
		||||
		gbproxy_touch_link_info(peer, link_info, now);
 | 
			
		||||
	} else if (link_info) {
 | 
			
		||||
		gbproxy_touch_link_info(peer, link_info, now);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
 | 
			
		||||
		gbproxy_assign_imsi(peer, link_info, parse_ctx);
 | 
			
		||||
 | 
			
		||||
	return link_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_update_link_state_after(
 | 
			
		||||
	struct gbproxy_peer *peer,
 | 
			
		||||
	struct gbproxy_link_info *link_info,
 | 
			
		||||
	time_t now,
 | 
			
		||||
	struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	if (parse_ctx->invalidate_tlli && link_info) {
 | 
			
		||||
		int keep_info =
 | 
			
		||||
			peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
 | 
			
		||||
			(peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
 | 
			
		||||
			 parse_ctx->await_reattach) ||
 | 
			
		||||
			(peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
 | 
			
		||||
			 link_info->imsi_len > 0);
 | 
			
		||||
		if (keep_info) {
 | 
			
		||||
			LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
 | 
			
		||||
			     link_info->tlli.current);
 | 
			
		||||
			rc = gbproxy_unregister_link_info(peer, link_info);
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
 | 
			
		||||
			     link_info->tlli.current);
 | 
			
		||||
			gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
			rc = 1;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
 | 
			
		||||
		   parse_ctx->new_ptmsi_enc && link_info) {
 | 
			
		||||
		/* A new PTMSI has been signaled in the message,
 | 
			
		||||
		 * register new TLLI */
 | 
			
		||||
		uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
 | 
			
		||||
		uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
 | 
			
		||||
		uint32_t new_sgsn_tlli;
 | 
			
		||||
		uint32_t new_bss_tlli = 0;
 | 
			
		||||
 | 
			
		||||
		new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
 | 
			
		||||
		if (new_bss_ptmsi != GSM_RESERVED_TMSI)
 | 
			
		||||
			new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
 | 
			
		||||
		LOGP(DGPRS, LOGL_INFO,
 | 
			
		||||
		     "Assigning new TLLI %08x to SGSN, %08x to BSS\n",
 | 
			
		||||
		     new_sgsn_tlli, new_bss_tlli);
 | 
			
		||||
 | 
			
		||||
		gbproxy_reassign_tlli(&link_info->sgsn_tlli,
 | 
			
		||||
				      peer, new_sgsn_tlli);
 | 
			
		||||
		gbproxy_reassign_tlli(&link_info->tlli,
 | 
			
		||||
				      peer, new_bss_tlli);
 | 
			
		||||
		gbproxy_remove_matching_link_infos(peer, link_info);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gbproxy_remove_stale_link_infos(peer, now);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,926 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2010 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2010 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gsm/apn.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
#include <osmocom/sgsn/gb_proxy.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_utils.h>
 | 
			
		||||
#include <osmocom/sgsn/vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
 | 
			
		||||
static struct gbproxy_config *g_cfg = NULL;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * vty code for gbproxy below
 | 
			
		||||
 */
 | 
			
		||||
static struct cmd_node gbproxy_node = {
 | 
			
		||||
	GBPROXY_NODE,
 | 
			
		||||
	"%s(config-gbproxy)# ",
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct value_string keep_modes[] = {
 | 
			
		||||
	{GBPROX_KEEP_NEVER, "never"},
 | 
			
		||||
	{GBPROX_KEEP_REATTACH, "re-attach"},
 | 
			
		||||
	{GBPROX_KEEP_IDENTIFIED, "identified"},
 | 
			
		||||
	{GBPROX_KEEP_ALWAYS, "always"},
 | 
			
		||||
	{0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct value_string match_ids[] = {
 | 
			
		||||
	{GBPROX_MATCH_PATCHING, "patching"},
 | 
			
		||||
	{GBPROX_MATCH_ROUTING, "routing"},
 | 
			
		||||
	{0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_ra_id raid;
 | 
			
		||||
	gsm48_parse_ra(&raid, peer->ra);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
 | 
			
		||||
		"RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid));
 | 
			
		||||
	if (peer->blocked)
 | 
			
		||||
		vty_out(vty, " [BVC-BLOCKED]");
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_gbproxy(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	enum gbproxy_match_id match_id;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "gbproxy%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->core_plmn.mcc > 0)
 | 
			
		||||
		vty_out(vty, " core-mobile-country-code %s%s",
 | 
			
		||||
			osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->core_plmn.mnc > 0)
 | 
			
		||||
		vty_out(vty, " core-mobile-network-code %s%s",
 | 
			
		||||
			osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
 | 
			
		||||
		struct gbproxy_match *match = &g_cfg->matches[match_id];
 | 
			
		||||
		if (match->re_str)
 | 
			
		||||
			vty_out(vty, " match-imsi %s %s%s",
 | 
			
		||||
				get_value_string(match_ids, match_id),
 | 
			
		||||
				match->re_str, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->core_apn != NULL) {
 | 
			
		||||
	       if (g_cfg->core_apn_size > 0) {
 | 
			
		||||
		       char str[500] = {0};
 | 
			
		||||
		       vty_out(vty, " core-access-point-name %s%s",
 | 
			
		||||
			       osmo_apn_to_str(str, g_cfg->core_apn,
 | 
			
		||||
						 g_cfg->core_apn_size),
 | 
			
		||||
			       VTY_NEWLINE);
 | 
			
		||||
	       } else {
 | 
			
		||||
		       vty_out(vty, " core-access-point-name none%s",
 | 
			
		||||
			       VTY_NEWLINE);
 | 
			
		||||
	       }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->route_to_sgsn2)
 | 
			
		||||
		vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->clean_stale_timer_freq > 0)
 | 
			
		||||
		vty_out(vty, " link-list clean-stale-timer %u%s",
 | 
			
		||||
			g_cfg->clean_stale_timer_freq, VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->tlli_max_age > 0)
 | 
			
		||||
		vty_out(vty, " link-list max-age %d%s",
 | 
			
		||||
			g_cfg->tlli_max_age, VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->tlli_max_len > 0)
 | 
			
		||||
		vty_out(vty, " link-list max-length %d%s",
 | 
			
		||||
			g_cfg->tlli_max_len, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " link-list keep-mode %s%s",
 | 
			
		||||
		get_value_string(keep_modes, g_cfg->keep_link_infos),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->stored_msgs_max_len > 0)
 | 
			
		||||
		vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s",
 | 
			
		||||
			g_cfg->stored_msgs_max_len, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy,
 | 
			
		||||
      cfg_gbproxy_cmd,
 | 
			
		||||
      "gbproxy",
 | 
			
		||||
      "Configure the Gb proxy")
 | 
			
		||||
{
 | 
			
		||||
	vty->node = GBPROXY_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_nsip_sgsn_nsei,
 | 
			
		||||
      cfg_nsip_sgsn_nsei_cmd,
 | 
			
		||||
      "sgsn nsei <0-65534>",
 | 
			
		||||
      "SGSN information\n"
 | 
			
		||||
      "NSEI to be used in the connection with the SGSN\n"
 | 
			
		||||
      "The NSEI\n")
 | 
			
		||||
{
 | 
			
		||||
	unsigned int nsei = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
 | 
			
		||||
		vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
 | 
			
		||||
			nsei, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_cfg->nsip_sgsn_nsei = nsei;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_core_mnc,
 | 
			
		||||
      cfg_gbproxy_core_mnc_cmd,
 | 
			
		||||
      "core-mobile-network-code <1-999>",
 | 
			
		||||
      GBPROXY_CORE_MNC_STR "NCC value\n")
 | 
			
		||||
{
 | 
			
		||||
	uint16_t mnc;
 | 
			
		||||
	bool mnc_3_digits;
 | 
			
		||||
	if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) {
 | 
			
		||||
		vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	g_cfg->core_plmn.mnc = mnc;
 | 
			
		||||
	g_cfg->core_plmn.mnc_3_digits = mnc_3_digits;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_core_mnc,
 | 
			
		||||
      cfg_gbproxy_no_core_mnc_cmd,
 | 
			
		||||
      "no core-mobile-network-code",
 | 
			
		||||
      NO_STR GBPROXY_CORE_MNC_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->core_plmn.mnc = 0;
 | 
			
		||||
	g_cfg->core_plmn.mnc_3_digits = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_core_mcc,
 | 
			
		||||
      cfg_gbproxy_core_mcc_cmd,
 | 
			
		||||
      "core-mobile-country-code <1-999>",
 | 
			
		||||
      GBPROXY_CORE_MCC_STR "MCC value\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->core_plmn.mcc = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_core_mcc,
 | 
			
		||||
      cfg_gbproxy_no_core_mcc_cmd,
 | 
			
		||||
      "no core-mobile-country-code",
 | 
			
		||||
      NO_STR GBPROXY_CORE_MCC_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->core_plmn.mcc = 0;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_match_imsi,
 | 
			
		||||
      cfg_gbproxy_match_imsi_cmd,
 | 
			
		||||
      "match-imsi (patching|routing) .REGEXP",
 | 
			
		||||
      GBPROXY_MATCH_IMSI_STR
 | 
			
		||||
      "Patch MS related information elements on match only\n"
 | 
			
		||||
      "Route to the secondary SGSN on match only\n"
 | 
			
		||||
      "Regular expression for the IMSI match\n")
 | 
			
		||||
{
 | 
			
		||||
	const char *filter = argv[1];
 | 
			
		||||
	const char *err_msg = NULL;
 | 
			
		||||
	struct gbproxy_match *match;
 | 
			
		||||
	enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
 | 
			
		||||
		    match_id < GBPROX_MATCH_LAST);
 | 
			
		||||
	match = &g_cfg->matches[match_id];
 | 
			
		||||
 | 
			
		||||
	if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
 | 
			
		||||
		vty_out(vty, "Match expression invalid: %s%s",
 | 
			
		||||
			err_msg, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_cfg->acquire_imsi = true;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_match_imsi,
 | 
			
		||||
      cfg_gbproxy_no_match_imsi_cmd,
 | 
			
		||||
      "no match-imsi",
 | 
			
		||||
      NO_STR GBPROXY_MATCH_IMSI_STR)
 | 
			
		||||
{
 | 
			
		||||
	enum gbproxy_match_id match_id;
 | 
			
		||||
 | 
			
		||||
	for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
 | 
			
		||||
		gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
 | 
			
		||||
 | 
			
		||||
	g_cfg->acquire_imsi = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
 | 
			
		||||
#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
 | 
			
		||||
 | 
			
		||||
static int set_core_apn(struct vty *vty, const char *apn)
 | 
			
		||||
{
 | 
			
		||||
	int apn_len;
 | 
			
		||||
 | 
			
		||||
	if (!apn) {
 | 
			
		||||
		talloc_free(g_cfg->core_apn);
 | 
			
		||||
		g_cfg->core_apn = NULL;
 | 
			
		||||
		g_cfg->core_apn_size = 0;
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apn_len = strlen(apn);
 | 
			
		||||
 | 
			
		||||
	if (apn_len >= 100) {
 | 
			
		||||
		vty_out(vty, "APN string too long (max 99 chars)%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (apn_len == 0) {
 | 
			
		||||
		talloc_free(g_cfg->core_apn);
 | 
			
		||||
		/* TODO: replace NULL */
 | 
			
		||||
		g_cfg->core_apn = talloc_zero_size(NULL, 2);
 | 
			
		||||
		g_cfg->core_apn_size = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* TODO: replace NULL */
 | 
			
		||||
		g_cfg->core_apn =
 | 
			
		||||
			talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
 | 
			
		||||
		g_cfg->core_apn_size =
 | 
			
		||||
			gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_core_apn,
 | 
			
		||||
      cfg_gbproxy_core_apn_cmd,
 | 
			
		||||
      "core-access-point-name (APN|none)",
 | 
			
		||||
      GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(argv[0], "none") == 0)
 | 
			
		||||
		return set_core_apn(vty, "");
 | 
			
		||||
	else
 | 
			
		||||
		return set_core_apn(vty, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_core_apn,
 | 
			
		||||
      cfg_gbproxy_no_core_apn_cmd,
 | 
			
		||||
      "no core-access-point-name",
 | 
			
		||||
      NO_STR GBPROXY_CORE_APN_STR)
 | 
			
		||||
{
 | 
			
		||||
	return set_core_apn(vty, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
 | 
			
		||||
 * automatically when needed. This command is only left for manual testing
 | 
			
		||||
 * (e.g. doing P-TMSI patching without using a secondary SGSN)
 | 
			
		||||
 */
 | 
			
		||||
#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_patch_ptmsi,
 | 
			
		||||
      cfg_gbproxy_patch_ptmsi_cmd,
 | 
			
		||||
      "patch-ptmsi",
 | 
			
		||||
      GBPROXY_PATCH_PTMSI_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->patch_ptmsi = true;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_patch_ptmsi,
 | 
			
		||||
      cfg_gbproxy_no_patch_ptmsi_cmd,
 | 
			
		||||
      "no patch-ptmsi",
 | 
			
		||||
      NO_STR GBPROXY_PATCH_PTMSI_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->patch_ptmsi = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: Remove the acquire-imsi command, since that feature is enabled
 | 
			
		||||
 * automatically when IMSI matching is enabled. This command is only left for
 | 
			
		||||
 * manual testing (e.g. doing IMSI acquisition without IMSI based patching)
 | 
			
		||||
 */
 | 
			
		||||
#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_acquire_imsi,
 | 
			
		||||
      cfg_gbproxy_acquire_imsi_cmd,
 | 
			
		||||
      "acquire-imsi",
 | 
			
		||||
      GBPROXY_ACQUIRE_IMSI_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->acquire_imsi = true;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_acquire_imsi,
 | 
			
		||||
      cfg_gbproxy_no_acquire_imsi_cmd,
 | 
			
		||||
      "no acquire-imsi",
 | 
			
		||||
      NO_STR GBPROXY_ACQUIRE_IMSI_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->acquire_imsi = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_secondary_sgsn,
 | 
			
		||||
      cfg_gbproxy_secondary_sgsn_cmd,
 | 
			
		||||
      "secondary-sgsn nsei <0-65534>",
 | 
			
		||||
      GBPROXY_SECOND_SGSN_STR
 | 
			
		||||
      "NSEI to be used in the connection with the SGSN\n"
 | 
			
		||||
      "The NSEI\n")
 | 
			
		||||
{
 | 
			
		||||
	unsigned int nsei = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->nsip_sgsn_nsei == nsei) {
 | 
			
		||||
		vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
 | 
			
		||||
			nsei, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_cfg->route_to_sgsn2 = true;
 | 
			
		||||
	g_cfg->nsip_sgsn2_nsei = nsei;
 | 
			
		||||
 | 
			
		||||
	g_cfg->patch_ptmsi = true;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_no_secondary_sgsn,
 | 
			
		||||
      cfg_gbproxy_no_secondary_sgsn_cmd,
 | 
			
		||||
      "no secondary-sgsn",
 | 
			
		||||
      NO_STR GBPROXY_SECOND_SGSN_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->route_to_sgsn2 = false;
 | 
			
		||||
	g_cfg->nsip_sgsn2_nsei = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
	g_cfg->patch_ptmsi = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
 | 
			
		||||
#define GBPROXY_LINK_STR "Set TLLI parameters\n"
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_clean_stale_timer,
 | 
			
		||||
      cfg_gbproxy_link_list_clean_stale_timer_cmd,
 | 
			
		||||
      "link-list clean-stale-timer <1-999999>",
 | 
			
		||||
      GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR
 | 
			
		||||
      "Frequency at which the periodic timer is fired (in seconds)\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	/* Re-schedule running timers soon in case prev frequency was really big
 | 
			
		||||
	   and new frequency is desired to be lower. After initial run, periodic
 | 
			
		||||
	   time is used. Use random() to avoid firing timers for all peers at
 | 
			
		||||
	   the same time */
 | 
			
		||||
	llist_for_each_entry(peer, &g_cfg->bts_peers, list)
 | 
			
		||||
		osmo_timer_schedule(&peer->clean_stale_timer,
 | 
			
		||||
					random() % 5, random() % 1000000);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
 | 
			
		||||
      cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
 | 
			
		||||
      "no link-list clean-stale-timer",
 | 
			
		||||
      NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	g_cfg->clean_stale_timer_freq = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(peer, &g_cfg->bts_peers, list)
 | 
			
		||||
		osmo_timer_del(&peer->clean_stale_timer);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_max_age,
 | 
			
		||||
      cfg_gbproxy_link_list_max_age_cmd,
 | 
			
		||||
      "link-list max-age <1-999999>",
 | 
			
		||||
      GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
 | 
			
		||||
      "Maximum age in seconds\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->tlli_max_age = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_no_max_age,
 | 
			
		||||
      cfg_gbproxy_link_list_no_max_age_cmd,
 | 
			
		||||
      "no link-list max-age",
 | 
			
		||||
      NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->tlli_max_age = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_max_len,
 | 
			
		||||
      cfg_gbproxy_link_list_max_len_cmd,
 | 
			
		||||
      "link-list max-length <1-99999>",
 | 
			
		||||
      GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
 | 
			
		||||
      "Maximum number of logical links in the list\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->tlli_max_len = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_no_max_len,
 | 
			
		||||
      cfg_gbproxy_link_list_no_max_len_cmd,
 | 
			
		||||
      "no link-list max-length",
 | 
			
		||||
      NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->tlli_max_len = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_list_keep_mode,
 | 
			
		||||
      cfg_gbproxy_link_list_keep_mode_cmd,
 | 
			
		||||
      "link-list keep-mode (never|re-attach|identified|always)",
 | 
			
		||||
      GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
 | 
			
		||||
      "Discard entry immediately after detachment\n"
 | 
			
		||||
      "Keep entry if a re-attachment has be requested\n"
 | 
			
		||||
      "Keep entry if it associated with an IMSI\n"
 | 
			
		||||
      "Don't discard entries after detachment\n")
 | 
			
		||||
{
 | 
			
		||||
	int val = get_string_value(keep_modes, argv[0]);
 | 
			
		||||
	OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
 | 
			
		||||
	g_cfg->keep_link_infos = val;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
 | 
			
		||||
      cfg_gbproxy_link_stored_msgs_max_len_cmd,
 | 
			
		||||
      "link stored-msgs-max-length <1-99999>",
 | 
			
		||||
      GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
 | 
			
		||||
      "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
 | 
			
		||||
      cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
 | 
			
		||||
      "no link stored-msgs-max-length",
 | 
			
		||||
      NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->stored_msgs_max_len = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
 | 
			
		||||
       SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	int show_stats = argc >= 1;
 | 
			
		||||
 | 
			
		||||
	if (show_stats)
 | 
			
		||||
		vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
 | 
			
		||||
		gbprox_vty_print_peer(vty, peer);
 | 
			
		||||
 | 
			
		||||
		if (show_stats)
 | 
			
		||||
			vty_out_rate_ctr_group(vty, "  ", peer->ctrg);
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
 | 
			
		||||
       SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gbproxy_peer *peer;
 | 
			
		||||
	char mi_buf[200];
 | 
			
		||||
	time_t now;
 | 
			
		||||
	struct timespec ts = {0,};
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
 | 
			
		||||
	now = ts.tv_sec;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
 | 
			
		||||
		struct gbproxy_link_info *link_info;
 | 
			
		||||
		struct gbproxy_patch_state *state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
		gbprox_vty_print_peer(vty, peer);
 | 
			
		||||
 | 
			
		||||
		llist_for_each_entry(link_info, &state->logical_links, list) {
 | 
			
		||||
			time_t age = now - link_info->timestamp;
 | 
			
		||||
 | 
			
		||||
			if (link_info->imsi > 0) {
 | 
			
		||||
				snprintf(mi_buf, sizeof(mi_buf), "(invalid)");
 | 
			
		||||
				gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
 | 
			
		||||
						   link_info->imsi,
 | 
			
		||||
						   link_info->imsi_len);
 | 
			
		||||
			} else {
 | 
			
		||||
				snprintf(mi_buf, sizeof(mi_buf), "(none)");
 | 
			
		||||
			}
 | 
			
		||||
			vty_out(vty, "  TLLI %08x, IMSI %s, AGE %d",
 | 
			
		||||
				link_info->tlli.current, mi_buf, (int)age);
 | 
			
		||||
 | 
			
		||||
			if (link_info->stored_msgs_len)
 | 
			
		||||
				vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
 | 
			
		||||
					link_info->stored_msgs_len,
 | 
			
		||||
					g_cfg->stored_msgs_max_len);
 | 
			
		||||
 | 
			
		||||
			if (g_cfg->route_to_sgsn2)
 | 
			
		||||
				vty_out(vty, ", SGSN NSEI %d",
 | 
			
		||||
					link_info->sgsn_nsei);
 | 
			
		||||
 | 
			
		||||
			if (link_info->is_deregistered)
 | 
			
		||||
				vty_out(vty, ", DE-REGISTERED");
 | 
			
		||||
 | 
			
		||||
			vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
 | 
			
		||||
	"delete-gbproxy-peer <0-65534> bvci <2-65534>",
 | 
			
		||||
	"Delete a GBProxy peer by NSEI and optionally BVCI\n"
 | 
			
		||||
	"NSEI number\n"
 | 
			
		||||
	"Only delete peer with a matching BVCI\n"
 | 
			
		||||
	"BVCI number\n")
 | 
			
		||||
{
 | 
			
		||||
	const uint16_t nsei = atoi(argv[0]);
 | 
			
		||||
	const uint16_t bvci = atoi(argv[1]);
 | 
			
		||||
	int counter;
 | 
			
		||||
 | 
			
		||||
	counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
 | 
			
		||||
 | 
			
		||||
	if (counter == 0) {
 | 
			
		||||
		vty_out(vty, "BVC not found%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
 | 
			
		||||
	"delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
 | 
			
		||||
	"Delete a GBProxy peer by NSEI and optionally BVCI\n"
 | 
			
		||||
	"NSEI number\n"
 | 
			
		||||
	"Only delete BSSGP connections (BVC)\n"
 | 
			
		||||
	"Only delete dynamic NS connections (NS-VC)\n"
 | 
			
		||||
	"Delete BVC and dynamic NS connections\n"
 | 
			
		||||
	"Show what would be deleted instead of actually deleting\n"
 | 
			
		||||
	)
 | 
			
		||||
{
 | 
			
		||||
	const uint16_t nsei = atoi(argv[0]);
 | 
			
		||||
	const char *mode = argv[1];
 | 
			
		||||
	int dry_run = argc > 2;
 | 
			
		||||
	int delete_bvc = 0;
 | 
			
		||||
	int delete_nsvc = 0;
 | 
			
		||||
	int counter;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(mode, "only-bvc") == 0)
 | 
			
		||||
		delete_bvc = 1;
 | 
			
		||||
	else if (strcmp(mode, "only-nsvc") == 0)
 | 
			
		||||
		delete_nsvc = 1;
 | 
			
		||||
	else
 | 
			
		||||
		delete_bvc = delete_nsvc = 1;
 | 
			
		||||
 | 
			
		||||
	if (delete_bvc) {
 | 
			
		||||
		if (!dry_run)
 | 
			
		||||
			counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
 | 
			
		||||
		else {
 | 
			
		||||
			struct gbproxy_peer *peer;
 | 
			
		||||
			counter = 0;
 | 
			
		||||
			llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
 | 
			
		||||
				if (peer->nsei != nsei)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				vty_out(vty, "BVC: ");
 | 
			
		||||
				gbprox_vty_print_peer(vty, peer);
 | 
			
		||||
				counter += 1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		vty_out(vty, "%sDeleted %d BVC%s",
 | 
			
		||||
			dry_run ? "Not " : "", counter, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (delete_nsvc) {
 | 
			
		||||
		struct gprs_ns_inst *nsi = g_cfg->nsi;
 | 
			
		||||
		struct gprs_nsvc *nsvc, *nsvc2;
 | 
			
		||||
 | 
			
		||||
		counter = 0;
 | 
			
		||||
		llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
 | 
			
		||||
			if (nsvc->nsei != nsei)
 | 
			
		||||
				continue;
 | 
			
		||||
			if (nsvc->persistent)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (!dry_run)
 | 
			
		||||
				gprs_nsvc_delete(nsvc);
 | 
			
		||||
			else
 | 
			
		||||
				vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
 | 
			
		||||
					"remote %s%s",
 | 
			
		||||
					nsvc->nsei, nsvc->nsvci,
 | 
			
		||||
					gprs_ns_ll_str(nsvc), VTY_NEWLINE);
 | 
			
		||||
			counter += 1;
 | 
			
		||||
		}
 | 
			
		||||
		vty_out(vty, "%sDeleted %d NS-VC%s",
 | 
			
		||||
			dry_run ? "Not " : "", counter, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_DELETE_LINK_STR \
 | 
			
		||||
	"Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
 | 
			
		||||
	"delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
 | 
			
		||||
	GBPROXY_DELETE_LINK_STR
 | 
			
		||||
	"Delete entries with a matching TLLI (hex)\n"
 | 
			
		||||
	"Delete entries with a matching IMSI\n"
 | 
			
		||||
	"Delete entries with a matching SGSN NSEI\n"
 | 
			
		||||
	"Identification to match\n")
 | 
			
		||||
{
 | 
			
		||||
	const uint16_t nsei = atoi(argv[0]);
 | 
			
		||||
	enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
 | 
			
		||||
	uint32_t ident = 0;
 | 
			
		||||
	const char *imsi = NULL;
 | 
			
		||||
	struct gbproxy_peer *peer = 0;
 | 
			
		||||
	struct gbproxy_link_info *link_info, *nxt;
 | 
			
		||||
	struct gbproxy_patch_state *state;
 | 
			
		||||
	char mi_buf[200];
 | 
			
		||||
	int found = 0;
 | 
			
		||||
 | 
			
		||||
	match = argv[1][0];
 | 
			
		||||
 | 
			
		||||
	switch (match) {
 | 
			
		||||
	case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
 | 
			
		||||
	case MATCH_IMSI: imsi = argv[2]; break;
 | 
			
		||||
	case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	peer = gbproxy_peer_by_nsei(g_cfg, nsei);
 | 
			
		||||
	if (!peer) {
 | 
			
		||||
		vty_out(vty, "Didn't find peer with NSEI %d%s",
 | 
			
		||||
			nsei, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
 | 
			
		||||
		switch (match) {
 | 
			
		||||
		case MATCH_TLLI:
 | 
			
		||||
			if (link_info->tlli.current != ident)
 | 
			
		||||
				continue;
 | 
			
		||||
			break;
 | 
			
		||||
		case MATCH_SGSN:
 | 
			
		||||
			if (link_info->sgsn_nsei != ident)
 | 
			
		||||
				continue;
 | 
			
		||||
			break;
 | 
			
		||||
		case MATCH_IMSI:
 | 
			
		||||
			if (!link_info->imsi)
 | 
			
		||||
				continue;
 | 
			
		||||
			mi_buf[0] = '\0';
 | 
			
		||||
			gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
 | 
			
		||||
					   link_info->imsi,
 | 
			
		||||
					   link_info->imsi_len);
 | 
			
		||||
 | 
			
		||||
			if (strcmp(mi_buf, imsi) != 0)
 | 
			
		||||
				continue;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
		found += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!found && argc >= 2) {
 | 
			
		||||
		vty_out(vty, "Didn't find link entry with %s %s%s",
 | 
			
		||||
			argv[1], argv[2], VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(delete_gb_link, delete_gb_link_cmd,
 | 
			
		||||
	"delete-gbproxy-link <0-65534> (stale|de-registered)",
 | 
			
		||||
	GBPROXY_DELETE_LINK_STR
 | 
			
		||||
	"Delete stale entries\n"
 | 
			
		||||
	"Delete de-registered entries\n")
 | 
			
		||||
{
 | 
			
		||||
	const uint16_t nsei = atoi(argv[0]);
 | 
			
		||||
	enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
 | 
			
		||||
	struct gbproxy_peer *peer = 0;
 | 
			
		||||
	struct gbproxy_link_info *link_info, *nxt;
 | 
			
		||||
	struct gbproxy_patch_state *state;
 | 
			
		||||
	time_t now;
 | 
			
		||||
	struct timespec ts = {0,};
 | 
			
		||||
 | 
			
		||||
	int found = 0;
 | 
			
		||||
 | 
			
		||||
	match = argv[1][0];
 | 
			
		||||
 | 
			
		||||
	peer = gbproxy_peer_by_nsei(g_cfg, nsei);
 | 
			
		||||
	if (!peer) {
 | 
			
		||||
		vty_out(vty, "Didn't find peer with NSEI %d%s",
 | 
			
		||||
			nsei, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state = &peer->patch_state;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
 | 
			
		||||
	now = ts.tv_sec;
 | 
			
		||||
 | 
			
		||||
	if (match == MATCH_STALE) {
 | 
			
		||||
		found = gbproxy_remove_stale_link_infos(peer, now);
 | 
			
		||||
		if (found)
 | 
			
		||||
			vty_out(vty, "Deleted %d stale logical link%s%s",
 | 
			
		||||
				found, found == 1 ? "" : "s", VTY_NEWLINE);
 | 
			
		||||
	} else {
 | 
			
		||||
		llist_for_each_entry_safe(link_info, nxt,
 | 
			
		||||
					  &state->logical_links, list) {
 | 
			
		||||
			if (!link_info->is_deregistered)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			gbproxy_delete_link_info(peer, link_info);
 | 
			
		||||
			found += 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (found)
 | 
			
		||||
		vty_out(vty, "Deleted %d %s logical link%s%s",
 | 
			
		||||
			found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * legacy commands to provide an upgrade path from "broken" releases
 | 
			
		||||
 * or pre-releases
 | 
			
		||||
 */
 | 
			
		||||
DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
 | 
			
		||||
      cfg_gbproxy_broken_apn_match_cmd,
 | 
			
		||||
      "core-access-point-name none match-imsi .REGEXP",
 | 
			
		||||
      GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
 | 
			
		||||
      "Patch MS related information elements on match only\n"
 | 
			
		||||
      "Route to the secondary SGSN on match only\n"
 | 
			
		||||
      "Regular expression for the IMSI match\n")
 | 
			
		||||
{
 | 
			
		||||
	const char *filter = argv[0];
 | 
			
		||||
	const char *err_msg = NULL;
 | 
			
		||||
	struct gbproxy_match *match;
 | 
			
		||||
	enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
 | 
			
		||||
 | 
			
		||||
	/* apply APN none */
 | 
			
		||||
	set_core_apn(vty, "");
 | 
			
		||||
 | 
			
		||||
	/* do the matching... with copy and paste */
 | 
			
		||||
	OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
 | 
			
		||||
		    match_id < GBPROX_MATCH_LAST);
 | 
			
		||||
	match = &g_cfg->matches[match_id];
 | 
			
		||||
 | 
			
		||||
	if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
 | 
			
		||||
		vty_out(vty, "Match expression invalid: %s%s",
 | 
			
		||||
			err_msg, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_cfg->acquire_imsi = true;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
 | 
			
		||||
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
 | 
			
		||||
DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
 | 
			
		||||
      cfg_gbproxy_depr_tlli_list_max_len_cmd,
 | 
			
		||||
      "tlli-list max-length <1-99999>",
 | 
			
		||||
      GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
 | 
			
		||||
      "Maximum number of TLLIs in the list\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->tlli_max_len = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_vty_init(void)
 | 
			
		||||
{
 | 
			
		||||
	install_element_ve(&show_gbproxy_cmd);
 | 
			
		||||
	install_element_ve(&show_gbproxy_links_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &delete_gb_link_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
 | 
			
		||||
	install_node(&gbproxy_node, config_write_gbproxy);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
 | 
			
		||||
 | 
			
		||||
	/* broken or deprecated to allow an upgrade path */
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
 | 
			
		||||
	install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	g_cfg = cfg;
 | 
			
		||||
	rc = vty_read_config_file(config_file, NULL);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -437,6 +437,47 @@ int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
 | 
			
		||||
	return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Determine the TLLI from the given BSSGP message.
 | 
			
		||||
 *  \param[in] bssgp pointer to start of BSSGP header
 | 
			
		||||
 *  \param[in] bssgp_len length of BSSGP message in octets
 | 
			
		||||
 *  \param[out] tlli TLLI (if any) in host byte order
 | 
			
		||||
 *  \returns 1 if TLLI found; 0 if none found; negative on parse error */
 | 
			
		||||
int gprs_gb_parse_tlli(const uint8_t *bssgp, size_t bssgp_len, uint32_t *tlli)
 | 
			
		||||
{
 | 
			
		||||
	const struct bssgp_normal_hdr *bgph;
 | 
			
		||||
	uint8_t pdu_type;
 | 
			
		||||
 | 
			
		||||
	if (bssgp_len < sizeof(struct bssgp_normal_hdr))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	bgph = (struct bssgp_normal_hdr *)bssgp;
 | 
			
		||||
	pdu_type = bgph->pdu_type;
 | 
			
		||||
 | 
			
		||||
	if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
 | 
			
		||||
	    pdu_type == BSSGP_PDUT_DL_UNITDATA) {
 | 
			
		||||
		const struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *)bssgp;
 | 
			
		||||
		if (bssgp_len < sizeof(struct bssgp_ud_hdr))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		*tlli = osmo_load32be((const uint8_t *)&budh->tlli);
 | 
			
		||||
		return 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		const uint8_t *data = bgph->data;
 | 
			
		||||
		size_t data_len = bssgp_len - sizeof(*bgph);
 | 
			
		||||
		struct tlv_parsed tp;
 | 
			
		||||
 | 
			
		||||
		if (bssgp_tlv_parse(&tp, data, data_len) < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) {
 | 
			
		||||
			*tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* No TLLI present in message */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
 | 
			
		||||
			struct gprs_gb_parse_context *parse_ctx)
 | 
			
		||||
{
 | 
			
		||||
@@ -604,14 +645,13 @@ void gprs_gb_log_parse_context(int log_level,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_ctx->imsi) {
 | 
			
		||||
		char mi_buf[200];
 | 
			
		||||
		mi_buf[0] = '\0';
 | 
			
		||||
		gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
 | 
			
		||||
				   parse_ctx->imsi, parse_ctx->imsi_len);
 | 
			
		||||
		LOGPC(DGPRS, log_level, "%s IMSI %s",
 | 
			
		||||
		     sep, mi_buf);
 | 
			
		||||
		struct osmo_mobile_identity mi;
 | 
			
		||||
		if (osmo_mobile_identity_decode(&mi, parse_ctx->imsi, parse_ctx->imsi_len, false) == 0
 | 
			
		||||
		    && mi.type == GSM_MI_TYPE_IMSI) {
 | 
			
		||||
			LOGPC(DGPRS, log_level, "%s IMSI %s", sep, mi.imsi);
 | 
			
		||||
			sep = ",";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (parse_ctx->invalidate_tlli) {
 | 
			
		||||
		LOGPC(DGPRS, log_level, "%s invalidate", sep);
 | 
			
		||||
		sep = ",";
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#include <osmocom/sgsn/gprs_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,8 @@ static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what);
 | 
			
		||||
 | 
			
		||||
	ares_process_fd(sgsn->ares_channel,
 | 
			
		||||
			(what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD,
 | 
			
		||||
			(what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD);
 | 
			
		||||
			(what & OSMO_FD_READ) ? fd->fd : ARES_SOCKET_BAD,
 | 
			
		||||
			(what & OSMO_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD);
 | 
			
		||||
	osmo_ares_reschedule(sgsn);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -120,14 +120,14 @@ static void setup_ares_osmo_fd(void *data, int fd, int read, int write)
 | 
			
		||||
 | 
			
		||||
update_fd:
 | 
			
		||||
	if (read)
 | 
			
		||||
		ufd->fd.when |= BSC_FD_READ;
 | 
			
		||||
		osmo_fd_read_enable(&ufd->fd);
 | 
			
		||||
	else
 | 
			
		||||
		ufd->fd.when &= ~BSC_FD_READ;
 | 
			
		||||
		osmo_fd_read_disable(&ufd->fd);
 | 
			
		||||
 | 
			
		||||
	if (write)
 | 
			
		||||
		ufd->fd.when |= BSC_FD_WRITE;
 | 
			
		||||
		osmo_fd_write_enable(&ufd->fd);
 | 
			
		||||
	else
 | 
			
		||||
		ufd->fd.when &= ~BSC_FD_WRITE;
 | 
			
		||||
		osmo_fd_write_disable(&ufd->fd);
 | 
			
		||||
 | 
			
		||||
	osmo_ares_reschedule(sgsn);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -59,15 +59,6 @@ void *osmo_gtphub_ctx;
 | 
			
		||||
/* TODO move this to osmocom/core/select.h ? */
 | 
			
		||||
typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what);
 | 
			
		||||
 | 
			
		||||
/* TODO move this to osmocom/core/linuxlist.h ? */
 | 
			
		||||
#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next)
 | 
			
		||||
#define llist_first(head, type, entry) \
 | 
			
		||||
	llist_entry(__llist_first(head), type, entry)
 | 
			
		||||
 | 
			
		||||
#define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev)
 | 
			
		||||
#define llist_last(head, type, entry) \
 | 
			
		||||
	llist_entry(__llist_last(head), type, entry)
 | 
			
		||||
 | 
			
		||||
/* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */
 | 
			
		||||
 | 
			
		||||
enum gtp_rc {
 | 
			
		||||
@@ -167,12 +158,12 @@ void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
 | 
			
		||||
			   const struct osmo_sockaddr *sa)
 | 
			
		||||
			   const struct sgsn_sockaddr *sa)
 | 
			
		||||
{
 | 
			
		||||
	char addr_str[256];
 | 
			
		||||
	char port_str[6];
 | 
			
		||||
 | 
			
		||||
	if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str),
 | 
			
		||||
	if (sgsn_sockaddr_to_strs(addr_str, sizeof(addr_str),
 | 
			
		||||
				  port_str, sizeof(port_str),
 | 
			
		||||
				  sa, (NI_NUMERICHOST | NI_NUMERICSERV))
 | 
			
		||||
	    != 0) {
 | 
			
		||||
@@ -613,7 +604,7 @@ void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now)
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&exq->items)
 | 
			
		||||
		    || (item->expiry
 | 
			
		||||
			>= llist_last(&exq->items, struct expiring_item, entry)->expiry));
 | 
			
		||||
			>= llist_last_entry(&exq->items, struct expiring_item, entry)->expiry));
 | 
			
		||||
 | 
			
		||||
	/* Add/move to the tail to always sort by expiry, ascending. */
 | 
			
		||||
	llist_del(&item->entry);
 | 
			
		||||
@@ -853,10 +844,7 @@ static int gtphub_sock_init(struct osmo_fd *ofd,
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofd->when = BSC_FD_READ;
 | 
			
		||||
	ofd->cb = cb;
 | 
			
		||||
	ofd->data = data;
 | 
			
		||||
	ofd->priv_nr = ofd_id;
 | 
			
		||||
	osmo_fd_setup(ofd, -1, OSMO_FD_READ, cb, data, ofd_id);
 | 
			
		||||
 | 
			
		||||
	int rc;
 | 
			
		||||
	rc = osmo_sock_init_ofd(ofd,
 | 
			
		||||
@@ -925,7 +913,7 @@ static void gtphub_bind_stop(struct gtphub_bind *b) {
 | 
			
		||||
/* Recv datagram from from->fd, write sender's address to *from_addr.
 | 
			
		||||
 * Return the number of bytes read, zero on error. */
 | 
			
		||||
static int gtphub_read(const struct osmo_fd *from,
 | 
			
		||||
		       struct osmo_sockaddr *from_addr,
 | 
			
		||||
		       struct sgsn_sockaddr *from_addr,
 | 
			
		||||
		       uint8_t *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(from_addr);
 | 
			
		||||
@@ -946,7 +934,7 @@ static int gtphub_read(const struct osmo_fd *from,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n",
 | 
			
		||||
	    (int)received, osmo_sockaddr_to_str(from_addr),
 | 
			
		||||
	    (int)received, sgsn_sockaddr_to_str(from_addr),
 | 
			
		||||
	    osmo_hexdump(buf, received > 1000? 1000 : received),
 | 
			
		||||
	    received > 1000 ? "..." : "");
 | 
			
		||||
 | 
			
		||||
@@ -1145,9 +1133,7 @@ static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf,
 | 
			
		||||
	if (llist_empty(&peer->addresses))
 | 
			
		||||
		return "(addressless)";
 | 
			
		||||
 | 
			
		||||
	struct gtphub_peer_addr *a = llist_first(&peer->addresses,
 | 
			
		||||
						 struct gtphub_peer_addr,
 | 
			
		||||
						 entry);
 | 
			
		||||
	struct gtphub_peer_addr *a = llist_first_entry_or_null(&peer->addresses, struct gtphub_peer_addr, entry);
 | 
			
		||||
	return gsn_addr_to_strb(&a->addr, buf, buflen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1948,14 +1934,14 @@ static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what)
 | 
			
		||||
	LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n",
 | 
			
		||||
	    gtphub_plane_idx_names[plane_idx]);
 | 
			
		||||
 | 
			
		||||
	if (!(what & BSC_FD_READ))
 | 
			
		||||
	if (!(what & OSMO_FD_READ))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	struct gtphub *hub = from_sgsns_ofd->data;
 | 
			
		||||
 | 
			
		||||
	static uint8_t buf[4096];
 | 
			
		||||
	struct osmo_sockaddr from_addr;
 | 
			
		||||
	struct osmo_sockaddr to_addr;
 | 
			
		||||
	struct sgsn_sockaddr from_addr;
 | 
			
		||||
	struct sgsn_sockaddr to_addr;
 | 
			
		||||
	struct osmo_fd *to_ofd;
 | 
			
		||||
	int len;
 | 
			
		||||
	uint8_t *reply_buf;
 | 
			
		||||
@@ -1979,14 +1965,14 @@ static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what)
 | 
			
		||||
	OSMO_ASSERT(plane_idx < GTPH_PLANE_N);
 | 
			
		||||
	LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n",
 | 
			
		||||
	    gtphub_plane_idx_names[plane_idx]);
 | 
			
		||||
	if (!(what & BSC_FD_READ))
 | 
			
		||||
	if (!(what & OSMO_FD_READ))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	struct gtphub *hub = from_ggsns_ofd->data;
 | 
			
		||||
 | 
			
		||||
	static uint8_t buf[4096];
 | 
			
		||||
	struct osmo_sockaddr from_addr;
 | 
			
		||||
	struct osmo_sockaddr to_addr;
 | 
			
		||||
	struct sgsn_sockaddr from_addr;
 | 
			
		||||
	struct sgsn_sockaddr to_addr;
 | 
			
		||||
	struct osmo_fd *to_ofd;
 | 
			
		||||
	int len;
 | 
			
		||||
	uint8_t *reply_buf;
 | 
			
		||||
@@ -2071,9 +2057,9 @@ static int gtphub_unmap(struct gtphub *hub,
 | 
			
		||||
 | 
			
		||||
static int gsn_addr_to_sockaddr(struct gsn_addr *src,
 | 
			
		||||
				uint16_t port,
 | 
			
		||||
				struct osmo_sockaddr *dst)
 | 
			
		||||
				struct sgsn_sockaddr *dst)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port);
 | 
			
		||||
	return sgsn_sockaddr_init_udp(dst, gsn_addr_to_str(src), port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* If p is an Echo request, replace p's data with the matching response and
 | 
			
		||||
@@ -2107,7 +2093,7 @@ static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
 | 
			
		||||
						     const struct osmo_sockaddr *addr);
 | 
			
		||||
						     const struct sgsn_sockaddr *addr);
 | 
			
		||||
 | 
			
		||||
/* Parse buffer as GTP packet, replace elements in-place and return the ofd and
 | 
			
		||||
 * address to forward to. Return a pointer to the osmo_fd, but copy the
 | 
			
		||||
@@ -2117,13 +2103,13 @@ struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *b
 | 
			
		||||
int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		      unsigned int side_idx,
 | 
			
		||||
		      unsigned int plane_idx,
 | 
			
		||||
		      const struct osmo_sockaddr *from_addr,
 | 
			
		||||
		      const struct sgsn_sockaddr *from_addr,
 | 
			
		||||
		      uint8_t *buf,
 | 
			
		||||
		      size_t received,
 | 
			
		||||
		      time_t now,
 | 
			
		||||
		      uint8_t **reply_buf,
 | 
			
		||||
		      struct osmo_fd **to_ofd,
 | 
			
		||||
		      struct osmo_sockaddr *to_addr)
 | 
			
		||||
		      struct sgsn_sockaddr *to_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx];
 | 
			
		||||
	struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx];
 | 
			
		||||
@@ -2138,7 +2124,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
	    (side_idx == GTPH_SIDE_GGSN)? "<-" : "->",
 | 
			
		||||
	    gtphub_plane_idx_names[plane_idx],
 | 
			
		||||
	    gtphub_side_idx_names[side_idx],
 | 
			
		||||
	    osmo_sockaddr_to_str(from_addr),
 | 
			
		||||
	    sgsn_sockaddr_to_str(from_addr),
 | 
			
		||||
	    gtp_type_str(p.type));
 | 
			
		||||
 | 
			
		||||
	if (p.rc <= 0) {
 | 
			
		||||
@@ -2146,7 +2132,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		    gtp_type_str(p.type),
 | 
			
		||||
		    gtphub_side_idx_names[side_idx],
 | 
			
		||||
		    gtphub_plane_idx_names[plane_idx],
 | 
			
		||||
		    osmo_sockaddr_to_str(from_addr));
 | 
			
		||||
		    sgsn_sockaddr_to_str(from_addr));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2156,7 +2142,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
	reply_len = gtphub_handle_echo_req(hub, &p, reply_buf);
 | 
			
		||||
	if (reply_len > 0) {
 | 
			
		||||
		/* It was an echo. Nothing left to do. */
 | 
			
		||||
		osmo_sockaddr_copy(to_addr, from_addr);
 | 
			
		||||
		sgsn_sockaddr_copy(to_addr, from_addr);
 | 
			
		||||
		*to_ofd = &from_bind->ofd;
 | 
			
		||||
 | 
			
		||||
		rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
 | 
			
		||||
@@ -2165,7 +2151,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n",
 | 
			
		||||
		    (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--",
 | 
			
		||||
		    gtphub_side_idx_names[side_idx],
 | 
			
		||||
		    (int)reply_len, osmo_sockaddr_to_str(to_addr));
 | 
			
		||||
		    (int)reply_len, sgsn_sockaddr_to_str(to_addr));
 | 
			
		||||
		return reply_len;
 | 
			
		||||
	}
 | 
			
		||||
	if (reply_len < 0)
 | 
			
		||||
@@ -2178,7 +2164,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
	 * so no-one else is allowed to talk to us from that side. */
 | 
			
		||||
	struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx];
 | 
			
		||||
	if (from_peer) {
 | 
			
		||||
		if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) {
 | 
			
		||||
		if (sgsn_sockaddr_cmp(&from_peer->sa, from_addr) != 0) {
 | 
			
		||||
			LOG(LOGL_ERROR,
 | 
			
		||||
			    "Rejecting: %s proxy configured, but GTP packet"
 | 
			
		||||
			    " received on %s bind is from another sender:"
 | 
			
		||||
@@ -2186,7 +2172,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
			    gtphub_side_idx_names[side_idx],
 | 
			
		||||
			    gtphub_side_idx_names[side_idx],
 | 
			
		||||
			    gtphub_port_str(from_peer),
 | 
			
		||||
			    osmo_sockaddr_to_str(from_addr));
 | 
			
		||||
			    sgsn_sockaddr_to_str(from_addr));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -2204,7 +2190,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		if (side_idx == GTPH_SIDE_GGSN) {
 | 
			
		||||
			LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n",
 | 
			
		||||
			    gtp_type_str(p.type),
 | 
			
		||||
			    osmo_sockaddr_to_str(from_addr));
 | 
			
		||||
			    sgsn_sockaddr_to_str(from_addr));
 | 
			
		||||
			return -1;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* SGSN */
 | 
			
		||||
@@ -2216,7 +2202,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
				    "Dropping packet%s: User plane peer was not"
 | 
			
		||||
				    "announced by PDP Context: %s\n",
 | 
			
		||||
				    gtp_type_str(p.type),
 | 
			
		||||
				    osmo_sockaddr_to_str(from_addr));
 | 
			
		||||
				    sgsn_sockaddr_to_str(from_addr));
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -2235,7 +2221,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
		LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n",
 | 
			
		||||
		    gtp_type_str(p.type),
 | 
			
		||||
		    gtphub_side_idx_names[side_idx],
 | 
			
		||||
		    osmo_sockaddr_to_str(from_addr));
 | 
			
		||||
		    sgsn_sockaddr_to_str(from_addr));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2309,7 +2295,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
	if (!to_peer_from_seq)
 | 
			
		||||
		gtphub_map_seq(&p, from_peer, to_peer);
 | 
			
		||||
 | 
			
		||||
	osmo_sockaddr_copy(to_addr, &to_peer->sa);
 | 
			
		||||
	sgsn_sockaddr_copy(to_addr, &to_peer->sa);
 | 
			
		||||
 | 
			
		||||
	*reply_buf = (uint8_t*)p.data;
 | 
			
		||||
 | 
			
		||||
@@ -2335,7 +2321,7 @@ int gtphub_handle_buf(struct gtphub *hub,
 | 
			
		||||
	    (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--",
 | 
			
		||||
	    gtphub_side_idx_names[other_side_idx(side_idx)],
 | 
			
		||||
	    p.header_tei, p.seq,
 | 
			
		||||
	    (int)received, osmo_sockaddr_to_str(to_addr));
 | 
			
		||||
	    (int)received, sgsn_sockaddr_to_str(to_addr));
 | 
			
		||||
	return received;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2645,7 +2631,7 @@ static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
 | 
			
		||||
					     const struct osmo_sockaddr *addr)
 | 
			
		||||
					     const struct sgsn_sockaddr *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct gsn_addr gsna;
 | 
			
		||||
	uint16_t port;
 | 
			
		||||
@@ -2762,7 +2748,7 @@ struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
 | 
			
		||||
/* Find a GGSN peer with a matching address. If the address is known but the
 | 
			
		||||
 * port not, create a new port for that peer address. */
 | 
			
		||||
struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
 | 
			
		||||
						     const struct osmo_sockaddr *addr)
 | 
			
		||||
						     const struct sgsn_sockaddr *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct gtphub_peer_addr *pa;
 | 
			
		||||
	struct gtphub_peer_port *pp;
 | 
			
		||||
@@ -2817,7 +2803,7 @@ static int gtphub_resolve_ggsn(struct gtphub *hub,
 | 
			
		||||
 | 
			
		||||
/* TODO move to osmocom/core/socket.c ? */
 | 
			
		||||
/* use this in osmo_sock_init() to remove dup. */
 | 
			
		||||
/* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required
 | 
			
		||||
/* Internal: call getaddrinfo for sgsn_sockaddr_init(). The caller is required
 | 
			
		||||
   to call freeaddrinfo(*result), iff zero is returned. */
 | 
			
		||||
static int _osmo_getaddrinfo(struct addrinfo **result,
 | 
			
		||||
			     uint16_t family, uint16_t type, uint8_t proto,
 | 
			
		||||
@@ -2844,7 +2830,7 @@ static int _osmo_getaddrinfo(struct addrinfo **result,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO move to osmocom/core/socket.c ? */
 | 
			
		||||
int osmo_sockaddr_init(struct osmo_sockaddr *addr,
 | 
			
		||||
int sgsn_sockaddr_init(struct sgsn_sockaddr *addr,
 | 
			
		||||
		       uint16_t family, uint16_t type, uint8_t proto,
 | 
			
		||||
		       const char *host, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
@@ -2865,9 +2851,9 @@ int osmo_sockaddr_init(struct osmo_sockaddr *addr,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
int sgsn_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
			  char *port_str, size_t port_str_len,
 | 
			
		||||
			  const struct osmo_sockaddr *addr,
 | 
			
		||||
			  const struct sgsn_sockaddr *addr,
 | 
			
		||||
			  int flags)
 | 
			
		||||
{
 | 
			
		||||
       int rc;
 | 
			
		||||
@@ -2896,14 +2882,14 @@ int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
 | 
			
		||||
       return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
 | 
			
		||||
const char *sgsn_sockaddr_to_strb(const struct sgsn_sockaddr *addr,
 | 
			
		||||
				  char *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
	char portbuf[6];
 | 
			
		||||
	const int portbuf_len = 6;
 | 
			
		||||
	OSMO_ASSERT(buf_len > portbuf_len);
 | 
			
		||||
	char *portbuf = buf + buf_len - portbuf_len;
 | 
			
		||||
	buf_len -= portbuf_len;
 | 
			
		||||
	if (osmo_sockaddr_to_strs(buf, buf_len,
 | 
			
		||||
	if (sgsn_sockaddr_to_strs(buf, buf_len,
 | 
			
		||||
				  portbuf, portbuf_len,
 | 
			
		||||
				  addr,
 | 
			
		||||
				  NI_NUMERICHOST | NI_NUMERICSERV))
 | 
			
		||||
@@ -2918,17 +2904,17 @@ const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr)
 | 
			
		||||
const char *sgsn_sockaddr_to_str(const struct sgsn_sockaddr *addr)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[256];
 | 
			
		||||
	const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf));
 | 
			
		||||
	const char *result = sgsn_sockaddr_to_strb(addr, buf, sizeof(buf));
 | 
			
		||||
	if (! result)
 | 
			
		||||
		return "(invalid)";
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
 | 
			
		||||
		      const struct osmo_sockaddr *b)
 | 
			
		||||
int sgsn_sockaddr_cmp(const struct sgsn_sockaddr *a,
 | 
			
		||||
		      const struct sgsn_sockaddr *b)
 | 
			
		||||
{
 | 
			
		||||
	if (a == b)
 | 
			
		||||
		return 0;
 | 
			
		||||
@@ -2938,7 +2924,7 @@ int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
 | 
			
		||||
		return 1;
 | 
			
		||||
	if (a->l != b->l) {
 | 
			
		||||
		/* Lengths are not the same, but determine the order. Will
 | 
			
		||||
		 * anyone ever sort a list by osmo_sockaddr though...? */
 | 
			
		||||
		 * anyone ever sort a list by sgsn_sockaddr though...? */
 | 
			
		||||
		int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l);
 | 
			
		||||
		if (cmp == 0) {
 | 
			
		||||
			if (a->l < b->l)
 | 
			
		||||
@@ -2951,8 +2937,8 @@ int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
 | 
			
		||||
	return memcmp(&a->a, &b->a, a->l);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_sockaddr_copy(struct osmo_sockaddr *dst,
 | 
			
		||||
			const struct osmo_sockaddr *src)
 | 
			
		||||
void sgsn_sockaddr_copy(struct sgsn_sockaddr *dst,
 | 
			
		||||
			const struct sgsn_sockaddr *src)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(src->l <= sizeof(dst->a));
 | 
			
		||||
	memcpy(&dst->a, &src->a, src->l);
 | 
			
		||||
 
 | 
			
		||||
@@ -95,11 +95,11 @@ void log_cfg(struct gtphub_cfg *cfg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void signal_handler(int signal)
 | 
			
		||||
static void signal_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stdout, "signal %d received\n", signal);
 | 
			
		||||
	fprintf(stdout, "signal %d received\n", signum);
 | 
			
		||||
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
	switch (signum) {
 | 
			
		||||
	case SIGINT:
 | 
			
		||||
	case SIGTERM:
 | 
			
		||||
		osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
 | 
			
		||||
@@ -107,8 +107,16 @@ static void signal_handler(int signal)
 | 
			
		||||
		exit(0);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGABRT:
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report
 | 
			
		||||
		 * and then return to the caller, who will abort the process */
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report and
 | 
			
		||||
		 * then run default SIGABRT handler, who will generate coredump
 | 
			
		||||
		 * and abort the process. abort() should do this for us after we
 | 
			
		||||
		 * return, but program wouldn't exit if an external SIGABRT is
 | 
			
		||||
		 * received.
 | 
			
		||||
		 */
 | 
			
		||||
		talloc_report_full(osmo_gtphub_ctx, stderr);
 | 
			
		||||
		signal(SIGABRT, SIG_DFL);
 | 
			
		||||
		raise(SIGABRT);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
	case SIGUSR2:
 | 
			
		||||
		talloc_report_full(osmo_gtphub_ctx, stderr);
 | 
			
		||||
@@ -118,44 +126,12 @@ static void signal_handler(int signal)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
int gtphub_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	switch (vty->node) {
 | 
			
		||||
	default:
 | 
			
		||||
		osmo_ss7_vty_go_parent(vty);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vty->node;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int gtphub_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
	/* Check if libosmo-sccp declares the node in
 | 
			
		||||
	 * question as config node */
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
	if (osmo_ss7_is_config_node(vty, node))
 | 
			
		||||
		return 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	switch (node) {
 | 
			
		||||
	/* add items that are not config */
 | 
			
		||||
	case CONFIG_NODE:
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OsmoGTPhub",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
	.go_parent_cb	= gtphub_vty_go_parent,
 | 
			
		||||
	.go_parent_cb	= osmo_ss7_vty_go_parent,
 | 
			
		||||
#endif
 | 
			
		||||
	.is_config_node	= gtphub_vty_is_config_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cmdline_cfg {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,13 +33,13 @@
 | 
			
		||||
	LOGP(DGTPHUB, level, fmt, ##args)
 | 
			
		||||
 | 
			
		||||
int gtphub_write(const struct osmo_fd *to,
 | 
			
		||||
		 const struct osmo_sockaddr *to_addr,
 | 
			
		||||
		 const struct sgsn_sockaddr *to_addr,
 | 
			
		||||
		 const uint8_t *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	ssize_t sent = sendto(to->fd, buf, buf_len, 0,
 | 
			
		||||
			      (struct sockaddr*)&to_addr->a, to_addr->l);
 | 
			
		||||
	LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr));
 | 
			
		||||
	LOG(LOGL_DEBUG, "to %s\n", sgsn_sockaddr_to_str(to_addr));
 | 
			
		||||
 | 
			
		||||
	if (sent == -1) {
 | 
			
		||||
		LOG(LOGL_ERROR, "error: %s\n", strerror(errno));
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,7 @@ osmo_sgsn_SOURCES = \
 | 
			
		||||
	sgsn_auth.c \
 | 
			
		||||
	gprs_subscriber.c \
 | 
			
		||||
	sgsn_cdr.c \
 | 
			
		||||
	sgsn_rim.c \
 | 
			
		||||
	slhc.c \
 | 
			
		||||
	gprs_llc_xid.c \
 | 
			
		||||
	v42bis.c \
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,9 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_msgb.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_llc.h>
 | 
			
		||||
 | 
			
		||||
#include "bscconfig.h"
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +38,8 @@
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
/* Has to be called whenever any PDU (signaling, data, ...) has been received */
 | 
			
		||||
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx) {
 | 
			
		||||
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx, const struct msgb *msg) {
 | 
			
		||||
	msgid2mmctx(mmctx, msg);
 | 
			
		||||
	if (mmctx->gb.llme)
 | 
			
		||||
		osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_PDU_RECEPTION, NULL);
 | 
			
		||||
}
 | 
			
		||||
@@ -53,10 +57,9 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
 | 
			
		||||
	bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
 | 
			
		||||
	mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id);
 | 
			
		||||
	if (mmctx) {
 | 
			
		||||
		msgid2mmctx(mmctx, msg);
 | 
			
		||||
		rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]);
 | 
			
		||||
		mmctx->gb.llme = llme;
 | 
			
		||||
		gprs_gb_recv_pdu(mmctx);
 | 
			
		||||
		gprs_gb_recv_pdu(mmctx, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* MMCTX can be NULL */
 | 
			
		||||
@@ -103,3 +106,83 @@ int gprs_gb_page_ps_ra(struct sgsn_mm_ctx *mmctx)
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* called by the bssgp layer to send NS PDUs */
 | 
			
		||||
int gprs_gb_send_cb(void *ctx, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_ns2_inst *nsi = (struct gprs_ns2_inst *) ctx;
 | 
			
		||||
	struct osmo_gprs_ns2_prim nsp = {};
 | 
			
		||||
	nsp.nsei = msgb_nsei(msg);
 | 
			
		||||
	nsp.bvci = msgb_bvci(msg);
 | 
			
		||||
	osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
 | 
			
		||||
	return gprs_ns2_recv_prim(nsi, &nsp.oph);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_ns_prim_status_cb(struct osmo_gprs_ns2_prim *nsp)
 | 
			
		||||
{
 | 
			
		||||
	switch (nsp->u.status.cause) {
 | 
			
		||||
	case GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED:
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS-E %d SNS configured.\n", nsp->nsei);
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_NS2_AFF_CAUSE_RECOVERY:
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became available\n", nsp->nsei);
 | 
			
		||||
		/* workaround for broken BSS which doesn't respond correct to BSSGP status message.
 | 
			
		||||
		 * Sent a BSSGP Reset when a persistent NSVC comes up for the first time. */
 | 
			
		||||
		if (nsp->u.status.first && nsp->u.status.persistent) {
 | 
			
		||||
			struct bssgp_bvc_ctx bctx = {
 | 
			
		||||
				.nsei = nsp->nsei,
 | 
			
		||||
			};
 | 
			
		||||
			bssgp_tx_bvc_reset2(&bctx, BVCI_SIGNALLING, BSSGP_CAUSE_EQUIP_FAIL, false);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_NS2_AFF_CAUSE_FAILURE:
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became unavailable\n", nsp->nsei);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
 | 
			
		||||
		     get_value_string(osmo_prim_op_names, nsp->oph.operation), nsp->oph.primitive);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* call-back function for the NS protocol */
 | 
			
		||||
int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gprs_ns2_prim *nsp;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (oph->sap != SAP_NS)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
 | 
			
		||||
 | 
			
		||||
	if (oph->operation != PRIM_OP_INDICATION) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
 | 
			
		||||
		     get_value_string(osmo_prim_op_names, oph->operation),
 | 
			
		||||
		     oph->operation);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (oph->primitive) {
 | 
			
		||||
	case GPRS_NS2_PRIM_UNIT_DATA:
 | 
			
		||||
		/* hand the message into the BSSGP implementation */
 | 
			
		||||
		/* add required msg fields for Gb layer */
 | 
			
		||||
		msgb_bssgph(oph->msg) = oph->msg->l3h;
 | 
			
		||||
		msgb_bvci(oph->msg) = nsp->bvci;
 | 
			
		||||
		msgb_nsei(oph->msg) = nsp->nsei;
 | 
			
		||||
		rc = bssgp_rcvmsg(oph->msg);
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_NS2_PRIM_STATUS:
 | 
			
		||||
		gprs_ns_prim_status_cb(nsp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
 | 
			
		||||
		     get_value_string(osmo_prim_op_names, oph->operation), oph->primitive);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (oph->msg)
 | 
			
		||||
		msgb_free(oph->msg);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -280,8 +280,12 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm)
 | 
			
		||||
	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT ACK");
 | 
			
		||||
	struct gsm48_hdr *gh;
 | 
			
		||||
	struct gsm48_attach_ack *aa;
 | 
			
		||||
	uint8_t *mid;
 | 
			
		||||
	unsigned long t;
 | 
			
		||||
#ifdef PTMSI_ALLOC
 | 
			
		||||
	struct osmo_mobile_identity mi;
 | 
			
		||||
	uint8_t *l;
 | 
			
		||||
	int rc;
 | 
			
		||||
#endif
 | 
			
		||||
#if 0
 | 
			
		||||
	uint8_t *ptsig;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -300,7 +304,7 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm)
 | 
			
		||||
	aa->att_result = 1;	/* GPRS only */
 | 
			
		||||
	t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1);
 | 
			
		||||
	aa->ra_upd_timer = gprs_secs_to_tmr_floor(t);
 | 
			
		||||
	aa->radio_prio = 4;	/* lowest */
 | 
			
		||||
	aa->radio_prio = 0x44;	/* lowest */
 | 
			
		||||
	gsm48_encode_ra(&aa->ra_id, &mm->ra);
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
@@ -321,14 +325,26 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm)
 | 
			
		||||
 | 
			
		||||
#ifdef PTMSI_ALLOC
 | 
			
		||||
	/* Optional: Allocated P-TMSI */
 | 
			
		||||
	mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
 | 
			
		||||
	gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi);
 | 
			
		||||
	mid[0] = GSM48_IE_GMM_ALLOC_PTMSI;
 | 
			
		||||
	mi = (struct osmo_mobile_identity){
 | 
			
		||||
		.type = GSM_MI_TYPE_TMSI,
 | 
			
		||||
		.tmsi = mm->p_tmsi,
 | 
			
		||||
	};
 | 
			
		||||
	l = msgb_tl_put(msg, GSM48_IE_GMM_ALLOC_PTMSI);
 | 
			
		||||
	rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGMMCTXP(LOGL_ERROR, mm, "Cannot encode Mobile Identity\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	*l = rc;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Optional: MS-identity (combined attach) */
 | 
			
		||||
	/* Optional: GMM cause (partial attach result for combined attach) */
 | 
			
		||||
 | 
			
		||||
	/* Optional: Network feature support 10.5.5.23 */
 | 
			
		||||
	/* msgb_v_put(msg, GSM48_IE_GMM_NET_FEAT_SUPPORT | 0x00);*/
 | 
			
		||||
 | 
			
		||||
	return gsm48_gmm_sendmsg(msg, 0, mm, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -911,9 +927,9 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
 | 
			
		||||
		ctx->t3350_mode = GMM_T3350_MODE_ATT;
 | 
			
		||||
#else
 | 
			
		||||
		memset(&sig_data, 0, sizeof(sig_data));
 | 
			
		||||
		sig_data.mm = mmctx;
 | 
			
		||||
		sig_data.mm = ctx;
 | 
			
		||||
		osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data);
 | 
			
		||||
		osmo_fsm_inst_dispatch(mm->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL);
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		return gsm48_tx_gmm_att_ack(ctx);
 | 
			
		||||
@@ -1026,31 +1042,35 @@ void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *ctx, int gmm_cause)
 | 
			
		||||
static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
 | 
			
		||||
	uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
 | 
			
		||||
	long mi_typel = mi_type;
 | 
			
		||||
	char mi_string[GSM48_MI_SIZE];
 | 
			
		||||
	long mi_typel;
 | 
			
		||||
	char mi_log_string[32];
 | 
			
		||||
	struct osmo_mobile_identity mi;
 | 
			
		||||
 | 
			
		||||
	gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]);
 | 
			
		||||
	if (!ctx) {
 | 
			
		||||
		DEBUGP(DMM, "from unknown TLLI 0x%08x?!? This should not happen\n", msgb_tlli(msg));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI(%s)=%s\n",
 | 
			
		||||
		gsm48_mi_type_name(mi_type), mi_string);
 | 
			
		||||
	if (osmo_mobile_identity_decode(&mi, &gh->data[1], gh->data[0], false)) {
 | 
			
		||||
		LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM IDENTITY RESPONSE: cannot decode Mobile Identity\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi);
 | 
			
		||||
 | 
			
		||||
	LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI=%s\n", mi_log_string);
 | 
			
		||||
 | 
			
		||||
	if (ctx->t3370_id_type == GSM_MI_TYPE_NONE) {
 | 
			
		||||
		LOGMMCTXP(LOGL_NOTICE, ctx,
 | 
			
		||||
			  "Got unexpected IDENTITY RESPONSE: MI(%s)=%s, "
 | 
			
		||||
			  "Got unexpected IDENTITY RESPONSE: MI=%s, "
 | 
			
		||||
			  "ignoring message\n",
 | 
			
		||||
			  gsm48_mi_type_name(mi_type), mi_string);
 | 
			
		||||
			  mi_log_string);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mi_type == ctx->t3370_id_type)
 | 
			
		||||
	if (mi.type == ctx->t3370_id_type)
 | 
			
		||||
		mmctx_timer_stop(ctx, 3370);
 | 
			
		||||
 | 
			
		||||
	switch (mi_type) {
 | 
			
		||||
	switch (mi.type) {
 | 
			
		||||
	case GSM_MI_TYPE_IMSI:
 | 
			
		||||
		/* we already have a mm context with current TLLI, but no
 | 
			
		||||
		 * P-TMSI / IMSI yet.  What we now need to do is to fill
 | 
			
		||||
@@ -1058,7 +1078,7 @@ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
		if (strlen(ctx->imsi) == 0) {
 | 
			
		||||
			/* Check if we already have a MM context for this IMSI */
 | 
			
		||||
			struct sgsn_mm_ctx *ictx;
 | 
			
		||||
			ictx = sgsn_mm_ctx_by_imsi(mi_string);
 | 
			
		||||
			ictx = sgsn_mm_ctx_by_imsi(mi.imsi);
 | 
			
		||||
			if (ictx) {
 | 
			
		||||
				/* Handle it like in gsm48_rx_gmm_det_req,
 | 
			
		||||
				 * except that no messages are sent to the BSS */
 | 
			
		||||
@@ -1070,16 +1090,17 @@ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
				mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(ctx->imsi, mi.imsi);
 | 
			
		||||
		break;
 | 
			
		||||
	case GSM_MI_TYPE_IMEI:
 | 
			
		||||
		osmo_strlcpy(ctx->imei, mi_string, sizeof(ctx->imei));
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(ctx->imei, mi.imei);
 | 
			
		||||
		break;
 | 
			
		||||
	case GSM_MI_TYPE_IMEISV:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if we can let the mobile station enter */
 | 
			
		||||
	mi_typel = mi.type;
 | 
			
		||||
	return osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_IDEN_RESP_RECV, (void *)mi_typel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1099,19 +1120,46 @@ static inline void ptmsi_update(struct sgsn_mm_ctx *ctx)
 | 
			
		||||
	osmo_fsm_inst_dispatch(ctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Detect if RAT has changed */
 | 
			
		||||
static bool mmctx_did_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	if (MSG_IU_UE_CTX(msg) && mmctx->ran_type != MM_CTX_T_UTRAN_Iu)
 | 
			
		||||
		return true;
 | 
			
		||||
	if (!MSG_IU_UE_CTX(msg) && mmctx->ran_type != MM_CTX_T_GERAN_Gb)
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Notify the FSM of a RAT change */
 | 
			
		||||
static void mmctx_handle_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme)
 | 
			
		||||
{
 | 
			
		||||
	struct gmm_rat_change_data rat_chg = {
 | 
			
		||||
		.llme = llme
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	rat_chg.new_ran_type = MSG_IU_UE_CTX(msg) ? MM_CTX_T_UTRAN_Iu : MM_CTX_T_GERAN_Gb;
 | 
			
		||||
 | 
			
		||||
	if (rat_chg.new_ran_type != mmctx->ran_type)
 | 
			
		||||
		osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_RAT_CHANGE, (void *) &rat_chg);
 | 
			
		||||
	else
 | 
			
		||||
		LOGMMCTXP(LOGL_ERROR, mmctx, "RAT didn't change or not implemented (ran_type=%u, "
 | 
			
		||||
				"msg_iu_ue_ctx=%p\n", mmctx->ran_type, MSG_IU_UE_CTX(msg));
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 24.008 § 9.4.1 Attach request */
 | 
			
		||||
static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
				struct gprs_llc_llme *llme)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
 | 
			
		||||
	uint8_t *cur = gh->data, *msnc, *mi, *ms_ra_acc_cap;
 | 
			
		||||
	uint8_t msnc_len, att_type, mi_len, mi_type, ms_ra_acc_cap_len;
 | 
			
		||||
	uint8_t *cur = gh->data, *msnc, *mi_data, *ms_ra_acc_cap;
 | 
			
		||||
	uint8_t msnc_len, att_type, mi_len, ms_ra_acc_cap_len;
 | 
			
		||||
	uint16_t drx_par;
 | 
			
		||||
	uint32_t tmsi;
 | 
			
		||||
	char mi_string[GSM48_MI_SIZE];
 | 
			
		||||
	char mi_log_string[32];
 | 
			
		||||
	struct gprs_ra_id ra_id;
 | 
			
		||||
	uint16_t cid = 0;
 | 
			
		||||
	enum gsm48_gmm_cause reject_cause;
 | 
			
		||||
	struct osmo_mobile_identity mi;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST ");
 | 
			
		||||
@@ -1155,15 +1203,15 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
 | 
			
		||||
	/* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */
 | 
			
		||||
	mi_len = *cur++;
 | 
			
		||||
	mi = cur;
 | 
			
		||||
	if (mi_len > 8)
 | 
			
		||||
		goto err_inval;
 | 
			
		||||
	mi_type = *mi & GSM_MI_TYPE_MASK;
 | 
			
		||||
	mi_data = cur;
 | 
			
		||||
	cur += mi_len;
 | 
			
		||||
 | 
			
		||||
	gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
 | 
			
		||||
	rc = osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto err_inval;
 | 
			
		||||
	osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi);
 | 
			
		||||
 | 
			
		||||
	DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string,
 | 
			
		||||
	DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_log_string,
 | 
			
		||||
		get_value_string(gprs_att_t_strs, att_type));
 | 
			
		||||
 | 
			
		||||
	/* Old routing area identification 10.5.5.15. Skip it */
 | 
			
		||||
@@ -1180,11 +1228,11 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
 | 
			
		||||
	/* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */
 | 
			
		||||
 | 
			
		||||
	switch (mi_type) {
 | 
			
		||||
	switch (mi.type) {
 | 
			
		||||
	case GSM_MI_TYPE_IMSI:
 | 
			
		||||
		/* Try to find MM context based on IMSI */
 | 
			
		||||
		if (!ctx)
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_imsi(mi_string);
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_imsi(mi.imsi);
 | 
			
		||||
		if (!ctx) {
 | 
			
		||||
			if (MSG_IU_UE_CTX(msg))
 | 
			
		||||
				ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg));
 | 
			
		||||
@@ -1194,15 +1242,13 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
				reject_cause = GMM_CAUSE_NET_FAIL;
 | 
			
		||||
				goto rejected;
 | 
			
		||||
			}
 | 
			
		||||
			osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
 | 
			
		||||
			OSMO_STRLCPY_ARRAY(ctx->imsi, mi.imsi);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case GSM_MI_TYPE_TMSI:
 | 
			
		||||
		memcpy(&tmsi, mi+1, 4);
 | 
			
		||||
		tmsi = ntohl(tmsi);
 | 
			
		||||
		/* Try to find MM context based on P-TMSI */
 | 
			
		||||
		if (!ctx)
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_ptmsi(tmsi);
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi);
 | 
			
		||||
		if (!ctx) {
 | 
			
		||||
			/* Allocate a context as most of our code expects one.
 | 
			
		||||
			 * Context will not have an IMSI ultil ID RESP is received */
 | 
			
		||||
@@ -1214,16 +1260,19 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
				reject_cause = GMM_CAUSE_NET_FAIL;
 | 
			
		||||
				goto rejected;
 | 
			
		||||
			}
 | 
			
		||||
			ctx->p_tmsi = tmsi;
 | 
			
		||||
			ctx->p_tmsi = mi.tmsi;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with "
 | 
			
		||||
			"MI type %s\n", gsm48_mi_type_name(mi_type));
 | 
			
		||||
			"MI %s\n", mi_log_string);
 | 
			
		||||
		reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED;
 | 
			
		||||
		goto rejected;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mmctx_did_rat_change(ctx, msg))
 | 
			
		||||
		mmctx_handle_rat_change(ctx, msg, llme);
 | 
			
		||||
 | 
			
		||||
	if (ctx->ran_type == MM_CTX_T_GERAN_Gb) {
 | 
			
		||||
		ctx->gb.tlli = msgb_tlli(msg);
 | 
			
		||||
		ctx->gb.llme = llme;
 | 
			
		||||
@@ -1245,8 +1294,8 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 | 
			
		||||
					   ctx->ciph_algo)) {
 | 
			
		||||
		reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
 | 
			
		||||
		LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI "
 | 
			
		||||
			  "type %s because MS do not support required %s "
 | 
			
		||||
			  "encryption\n", gsm48_mi_type_name(mi_type),
 | 
			
		||||
			  "%s because MS do not support required %s "
 | 
			
		||||
			  "encryption\n", mi_log_string,
 | 
			
		||||
			  get_value_string(gprs_cipher_names,ctx->ciph_algo));
 | 
			
		||||
		goto rejected;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1393,8 +1442,12 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
 | 
			
		||||
	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UPD ACK");
 | 
			
		||||
	struct gsm48_hdr *gh;
 | 
			
		||||
	struct gsm48_ra_upd_ack *rua;
 | 
			
		||||
	uint8_t *mid;
 | 
			
		||||
	unsigned long t;
 | 
			
		||||
#ifdef PTMSI_ALLOC
 | 
			
		||||
	uint8_t *l;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_mobile_identity mi;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_ACKED]);
 | 
			
		||||
	LOGMMCTXP(LOGL_INFO, mm, "<- ROUTING AREA UPDATE ACCEPT\n");
 | 
			
		||||
@@ -1424,9 +1477,17 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
 | 
			
		||||
 | 
			
		||||
#ifdef PTMSI_ALLOC
 | 
			
		||||
	/* Optional: Allocated P-TMSI */
 | 
			
		||||
	mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
 | 
			
		||||
	gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi);
 | 
			
		||||
	mid[0] = GSM48_IE_GMM_ALLOC_PTMSI;
 | 
			
		||||
	mi = (struct osmo_mobile_identity){
 | 
			
		||||
		.type = GSM_MI_TYPE_TMSI,
 | 
			
		||||
		.tmsi = mm->p_tmsi,
 | 
			
		||||
	};
 | 
			
		||||
	l = msgb_tl_put(msg, GSM48_IE_GMM_ALLOC_PTMSI);
 | 
			
		||||
	rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	*l = rc;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Optional: Negotiated READY timer value */
 | 
			
		||||
@@ -1470,21 +1531,19 @@ static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
 | 
			
		||||
	 * being in state PDP-INACTIVE. */
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
 | 
			
		||||
		if (pdp->nsapi < 8) {
 | 
			
		||||
			if (!(pdp_status[0] & (1 << pdp->nsapi))) {
 | 
			
		||||
		bool inactive = (pdp->nsapi < 8) ?
 | 
			
		||||
					!(pdp_status[0] & (1 << pdp->nsapi)) :
 | 
			
		||||
					!(pdp_status[1] & (1 << (pdp->nsapi - 8)));
 | 
			
		||||
		if (!inactive)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
 | 
			
		||||
			"due to PDP CTX STATUS IE=0x%02x%02x\n",
 | 
			
		||||
			pdp->nsapi, pdp_status[1], pdp_status[0]);
 | 
			
		||||
		if (pdp->ggsn)
 | 
			
		||||
			sgsn_delete_pdp_ctx(pdp);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) {
 | 
			
		||||
				LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
 | 
			
		||||
					"due to PDP CTX STATUS IE= 0x%02x%02x\n",
 | 
			
		||||
					pdp->nsapi, pdp_status[1], pdp_status[0]);
 | 
			
		||||
				sgsn_delete_pdp_ctx(pdp);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else /* GTP side already detached, freeing */
 | 
			
		||||
			sgsn_pdp_ctx_free(pdp);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1576,19 +1635,14 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
 | 
			
		||||
		} else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) {
 | 
			
		||||
#ifdef BUILD_IU
 | 
			
		||||
			/* In Iu mode search only for ptmsi */
 | 
			
		||||
			char mi_string[GSM48_MI_SIZE];
 | 
			
		||||
			uint8_t mi_len = TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
 | 
			
		||||
			const uint8_t *mi = TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
 | 
			
		||||
			uint8_t mi_type = *mi & GSM_MI_TYPE_MASK;
 | 
			
		||||
			uint32_t tmsi;
 | 
			
		||||
 | 
			
		||||
			gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
 | 
			
		||||
 | 
			
		||||
			if (mi_type == GSM_MI_TYPE_TMSI) {
 | 
			
		||||
				memcpy(&tmsi, mi+1, 4);
 | 
			
		||||
				tmsi = ntohl(tmsi);
 | 
			
		||||
				mmctx = sgsn_mm_ctx_by_ptmsi(tmsi);
 | 
			
		||||
			struct osmo_mobile_identity mi;
 | 
			
		||||
			if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI),
 | 
			
		||||
							TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI), false)
 | 
			
		||||
			    || mi.type != GSM_MI_TYPE_TMSI) {
 | 
			
		||||
				LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR, "Cannot decode P-TMSI\n");
 | 
			
		||||
				goto rejected;
 | 
			
		||||
			}
 | 
			
		||||
			mmctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi);
 | 
			
		||||
#else
 | 
			
		||||
			LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR,
 | 
			
		||||
			       "Rejecting GMM RA Update Request: No Iu support\n");
 | 
			
		||||
@@ -1604,21 +1658,36 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
 | 
			
		||||
				mmctx->p_tmsi, mmctx->p_tmsi_old,
 | 
			
		||||
				mmctx->gb.tlli, mmctx->gb.tlli_new,
 | 
			
		||||
				osmo_rai_name(&mmctx->ra));
 | 
			
		||||
			/* A RAT change will trigger the common procedure
 | 
			
		||||
			 * below after handling the RAT change. Protect it
 | 
			
		||||
			 * here from being called twice */
 | 
			
		||||
			if (!mmctx_did_rat_change(mmctx, msg))
 | 
			
		||||
				osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL);
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	} else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) ||
 | 
			
		||||
		mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
 | 
			
		||||
	{
 | 
			
		||||
		/* We cannot use the mmctx */
 | 
			
		||||
		/* We've received either a RAU for a MS which isn't registered
 | 
			
		||||
		 * or a RAU with an unknown RA ID. As long the SGSN doesn't support
 | 
			
		||||
		 * PS handover we treat this as invalid RAU */
 | 
			
		||||
		struct gprs_ra_id new_ra_id;
 | 
			
		||||
		char new_ra[32];
 | 
			
		||||
 | 
			
		||||
		bssgp_parse_cell_id(&new_ra_id, msgb_bcid(msg));
 | 
			
		||||
		osmo_rai_name_buf(new_ra, sizeof(new_ra), &new_ra_id);
 | 
			
		||||
 | 
			
		||||
		if (mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
 | 
			
		||||
			LOGMMCTXP(LOGL_INFO, mmctx,
 | 
			
		||||
			"The MM context cannot be used, RA: %s\n",
 | 
			
		||||
			osmo_rai_name(&mmctx->ra));
 | 
			
		||||
		/* mmctx is set to NULL and gprs_llgmm_unassign(llme) will be
 | 
			
		||||
		   called below, let's make sure we don't keep dangling llme
 | 
			
		||||
		   pointers in mmctx (OS#3957, OS#4245). */
 | 
			
		||||
		if (mmctx->ran_type == MM_CTX_T_GERAN_Gb)
 | 
			
		||||
			mmctx->gb.llme = NULL;
 | 
			
		||||
		mmctx = NULL;
 | 
			
		||||
				  "Rejecting RAU - GMM state is deregistered. Old RA: %s New RA: %s\n",
 | 
			
		||||
				  osmo_rai_name(&old_ra_id), new_ra);
 | 
			
		||||
		else
 | 
			
		||||
			LOGMMCTXP(LOGL_INFO, mmctx,
 | 
			
		||||
				  "Rejecting RAU - Old RA doesn't match MM. Old RA: %s New RA: %s\n",
 | 
			
		||||
				  osmo_rai_name(&old_ra_id), new_ra);
 | 
			
		||||
 | 
			
		||||
		reject_cause = GMM_CAUSE_IMPL_DETACHED;
 | 
			
		||||
		goto rejected;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!mmctx) {
 | 
			
		||||
@@ -1636,13 +1705,18 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
 | 
			
		||||
		goto rejected;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mmctx_did_rat_change(mmctx, msg)) {
 | 
			
		||||
		mmctx_handle_rat_change(mmctx, msg, llme);
 | 
			
		||||
		osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */
 | 
			
		||||
	msgid2mmctx(mmctx, msg);
 | 
			
		||||
	/* Bump the statistics of received signalling msgs for this MM context */
 | 
			
		||||
	rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]);
 | 
			
		||||
 | 
			
		||||
	/* Update the MM context with the new RA-ID */
 | 
			
		||||
	if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
 | 
			
		||||
	if (mmctx->ran_type == MM_CTX_T_GERAN_Gb && msgb_bcid(msg)) {
 | 
			
		||||
		bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg));
 | 
			
		||||
		/* Update the MM context with the new (i.e. foreign) TLLI */
 | 
			
		||||
		mmctx->gb.tlli = msgb_tlli(msg);
 | 
			
		||||
@@ -1660,7 +1734,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
 | 
			
		||||
	mmctx_timer_start(mmctx, 3350);
 | 
			
		||||
#else
 | 
			
		||||
	/* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */
 | 
			
		||||
	osmo_fsm_inst_dispatch(mm->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL);
 | 
			
		||||
	osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_ATTACH_SUCCESS, NULL);
 | 
			
		||||
 | 
			
		||||
	memset(&sig_data, 0, sizeof(sig_data));
 | 
			
		||||
	sig_data.mm = mmctx;
 | 
			
		||||
@@ -1762,11 +1836,11 @@ static int gsm48_rx_gmm_ptmsi_reall_compl(struct sgsn_mm_ctx *mmctx)
 | 
			
		||||
static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
 | 
			
		||||
	uint8_t *cur = gh->data, *mi;
 | 
			
		||||
	uint8_t service_type, mi_len, mi_type;
 | 
			
		||||
	uint32_t tmsi;
 | 
			
		||||
	uint8_t *cur = gh->data, *mi_data;
 | 
			
		||||
	uint8_t service_type, mi_len;
 | 
			
		||||
	struct tlv_parsed tp;
 | 
			
		||||
	char mi_string[GSM48_MI_SIZE];
 | 
			
		||||
	struct osmo_mobile_identity mi;
 | 
			
		||||
	char mi_log_string[32];
 | 
			
		||||
	enum gsm48_gmm_cause reject_cause;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -1786,15 +1860,14 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	/* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */
 | 
			
		||||
	mi_len = *cur++;
 | 
			
		||||
	mi = cur;
 | 
			
		||||
	if (mi_len > 8)
 | 
			
		||||
		goto err_inval;
 | 
			
		||||
	mi_type = *mi & GSM_MI_TYPE_MASK;
 | 
			
		||||
	mi_data = cur;
 | 
			
		||||
	cur += mi_len;
 | 
			
		||||
	rc = osmo_mobile_identity_decode(&mi, mi_data, mi_len, false);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto err_inval;
 | 
			
		||||
	osmo_mobile_identity_to_str_buf(mi_log_string, sizeof(mi_log_string), &mi);
 | 
			
		||||
 | 
			
		||||
	gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
 | 
			
		||||
 | 
			
		||||
	DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string,
 | 
			
		||||
	DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_log_string,
 | 
			
		||||
		get_value_string(gprs_service_t_strs, service_type));
 | 
			
		||||
 | 
			
		||||
	LOGPC(DMM, LOGL_INFO, "\n");
 | 
			
		||||
@@ -1802,11 +1875,11 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
	/* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */
 | 
			
		||||
	tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
 | 
			
		||||
 | 
			
		||||
	switch (mi_type) {
 | 
			
		||||
	switch (mi.type) {
 | 
			
		||||
	case GSM_MI_TYPE_IMSI:
 | 
			
		||||
		/* Try to find MM context based on IMSI */
 | 
			
		||||
		if (!ctx)
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_imsi(mi_string);
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_imsi(mi.imsi);
 | 
			
		||||
		if (!ctx) {
 | 
			
		||||
			/* FIXME: We need to have a context for service request? */
 | 
			
		||||
			reject_cause = GMM_CAUSE_IMPL_DETACHED;
 | 
			
		||||
@@ -1815,11 +1888,9 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
		msgid2mmctx(ctx, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	case GSM_MI_TYPE_TMSI:
 | 
			
		||||
		memcpy(&tmsi, mi+1, 4);
 | 
			
		||||
		tmsi = ntohl(tmsi);
 | 
			
		||||
		/* Try to find MM context based on P-TMSI */
 | 
			
		||||
		if (!ctx)
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_ptmsi(tmsi);
 | 
			
		||||
			ctx = sgsn_mm_ctx_by_ptmsi(mi.tmsi);
 | 
			
		||||
		if (!ctx) {
 | 
			
		||||
			/* FIXME: We need to have a context for service request? */
 | 
			
		||||
			reject_cause = GMM_CAUSE_IMPL_DETACHED;
 | 
			
		||||
@@ -1829,7 +1900,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with "
 | 
			
		||||
			"MI type %s\n", gsm48_mi_type_name(mi_type));
 | 
			
		||||
			"MI %s\n", mi_log_string);
 | 
			
		||||
		reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED;
 | 
			
		||||
		goto rejected;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1944,6 +2015,23 @@ int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* A RAT change is only expected/allowed for RAU/Attach Req */
 | 
			
		||||
	if (mmctx && mmctx_did_rat_change(mmctx, msg)) {
 | 
			
		||||
		switch (gh->msg_type) {
 | 
			
		||||
		case GSM48_MT_GMM_RA_UPD_REQ:
 | 
			
		||||
		case GSM48_MT_GMM_ATTACH_REQ:
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* This shouldn't happen with other message types and
 | 
			
		||||
			 * we need to error out to prevent a crash */
 | 
			
		||||
			LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping GMM %s which was received on different "
 | 
			
		||||
				       "RAT (mmctx ran_type=%u, msg_iu_ue_ctx=%p\n",
 | 
			
		||||
				       get_value_string(gprs_msgt_gmm_names, gh->msg_type),
 | 
			
		||||
				       mmctx->ran_type, MSG_IU_UE_CTX(msg));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * For a few messages, mmctx may be NULL. For most, we want to ensure a
 | 
			
		||||
	 * non-NULL mmctx. At the same time, we want to keep the message
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,30 @@
 | 
			
		||||
/* GMM mobility management states on the network side, 3GPP TS 24.008 § 4.1.3.3 */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0+
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
#include <osmocom/core/tdef.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/gprs_gmm_fsm.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
#include <osmocom/sgsn/sgsn.h>
 | 
			
		||||
@@ -67,9 +91,12 @@ static void st_gmm_registered_normal(struct osmo_fsm_inst *fi, uint32_t event, v
 | 
			
		||||
static void st_gmm_registered_suspended(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	switch(event) {
 | 
			
		||||
	case E_GMM_RESUME:
 | 
			
		||||
	case E_GMM_RESUME:		/* explicit BSSGP RESUME from BSS */
 | 
			
		||||
		gmm_fsm_state_chg(fi, ST_GMM_REGISTERED_NORMAL);
 | 
			
		||||
		break;
 | 
			
		||||
	case E_GMM_COMMON_PROC_INIT_REQ: /* implicit resume from MS */
 | 
			
		||||
		gmm_fsm_state_chg(fi, ST_GMM_COMMON_PROC_INIT);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -121,10 +148,12 @@ static struct osmo_fsm_state gmm_fsm_states[] = {
 | 
			
		||||
		.action = st_gmm_registered_normal,
 | 
			
		||||
	},
 | 
			
		||||
	[ST_GMM_REGISTERED_SUSPENDED] = {
 | 
			
		||||
		.in_event_mask = X(E_GMM_RESUME),
 | 
			
		||||
		.in_event_mask = X(E_GMM_RESUME) |
 | 
			
		||||
				 X(E_GMM_COMMON_PROC_INIT_REQ),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(ST_GMM_DEREGISTERED) |
 | 
			
		||||
			X(ST_GMM_REGISTERED_NORMAL),
 | 
			
		||||
			X(ST_GMM_REGISTERED_NORMAL) |
 | 
			
		||||
			X(ST_GMM_COMMON_PROC_INIT),
 | 
			
		||||
		.name = "Registered.SUSPENDED",
 | 
			
		||||
		.action = st_gmm_registered_suspended,
 | 
			
		||||
	},
 | 
			
		||||
@@ -149,11 +178,32 @@ const struct value_string gmm_fsm_event_names[] = {
 | 
			
		||||
	/* OSMO_VALUE_STRING(E_GMM_DETACH_ACCEPTED), */
 | 
			
		||||
	OSMO_VALUE_STRING(E_GMM_SUSPEND),
 | 
			
		||||
	OSMO_VALUE_STRING(E_GMM_CLEANUP),
 | 
			
		||||
	OSMO_VALUE_STRING(E_GMM_RAT_CHANGE),
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void gmm_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) {
 | 
			
		||||
	struct sgsn_mm_ctx *mmctx = fi->priv;
 | 
			
		||||
	struct gmm_rat_change_data *rat_chg = (struct gmm_rat_change_data *)data;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case E_GMM_RAT_CHANGE:
 | 
			
		||||
 | 
			
		||||
		switch (fi->state) {
 | 
			
		||||
		case ST_GMM_COMMON_PROC_INIT:
 | 
			
		||||
			gmm_fsm_state_chg(fi, ST_GMM_DEREGISTERED);
 | 
			
		||||
		default:
 | 
			
		||||
			if (mmctx->ran_type == MM_CTX_T_GERAN_Gb)
 | 
			
		||||
				osmo_fsm_inst_dispatch(mmctx->gb.mm_state_fsm, E_MM_IMPLICIT_DETACH, NULL);
 | 
			
		||||
			else if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu) {
 | 
			
		||||
				osmo_fsm_inst_dispatch(mmctx->iu.mm_state_fsm, E_PMM_IMPLICIT_DETACH, NULL);
 | 
			
		||||
				mmctx->gb.llme = rat_chg->llme;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mmctx->ran_type = rat_chg->new_ran_type;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case E_GMM_CLEANUP:
 | 
			
		||||
		switch (fi->state) {
 | 
			
		||||
		case ST_GMM_DEREGISTERED:
 | 
			
		||||
@@ -175,7 +225,7 @@ struct osmo_fsm gmm_fsm = {
 | 
			
		||||
	.states = gmm_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(gmm_fsm_states),
 | 
			
		||||
	.event_names = gmm_fsm_event_names,
 | 
			
		||||
	.allstate_event_mask = X(E_GMM_CLEANUP),
 | 
			
		||||
	.allstate_event_mask = X(E_GMM_CLEANUP) | X(E_GMM_RAT_CHANGE),
 | 
			
		||||
	.allstate_action = gmm_fsm_allstate_action,
 | 
			
		||||
	.log_subsys = DMM,
 | 
			
		||||
	.timer_cb = gmm_fsm_timer_cb,
 | 
			
		||||
 
 | 
			
		||||
@@ -376,7 +376,10 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
 | 
			
		||||
	 * not yet have a MMC context (e.g. XID negotiation of primarly
 | 
			
		||||
	 * LLC connection from GMM sapi). */
 | 
			
		||||
	if (mmctx) {
 | 
			
		||||
		/* In rare cases the LLME is NULL in those cases don't
 | 
			
		||||
		 * use the mm radio capabilities */
 | 
			
		||||
		dup.imsi = mmctx->imsi;
 | 
			
		||||
		if (mmctx->gb.llme) {
 | 
			
		||||
			dup.drx_parms = mmctx->drx_parms;
 | 
			
		||||
			dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len;
 | 
			
		||||
			dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf;
 | 
			
		||||
@@ -392,6 +395,7 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	memcpy(&dup.qos_profile, qos_profile_default,
 | 
			
		||||
		sizeof(qos_profile_default));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,9 @@ static void st_mm_standby(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
	case E_MM_PDU_RECEPTION:
 | 
			
		||||
		mm_state_gb_fsm_state_chg(fi, ST_MM_READY);
 | 
			
		||||
		break;
 | 
			
		||||
	case E_MM_IMPLICIT_DETACH:
 | 
			
		||||
		mm_state_gb_fsm_state_chg(fi, ST_MM_IDLE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -86,7 +89,7 @@ static struct osmo_fsm_state mm_state_gb_fsm_states[] = {
 | 
			
		||||
		.action = st_mm_ready,
 | 
			
		||||
	},
 | 
			
		||||
	[ST_MM_STANDBY] = {
 | 
			
		||||
		.in_event_mask = X(E_MM_PDU_RECEPTION),
 | 
			
		||||
		.in_event_mask = X(E_MM_PDU_RECEPTION) | X(E_MM_IMPLICIT_DETACH),
 | 
			
		||||
		.out_state_mask = X(ST_MM_IDLE) | X(ST_MM_READY),
 | 
			
		||||
		.name = "Standby",
 | 
			
		||||
		.action = st_mm_standby,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/tdef.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_msgb.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ranap/ranap_common.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/backtrace.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 | 
			
		||||
#include <osmocom/gsm/apn.h>
 | 
			
		||||
@@ -511,6 +511,11 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
 | 
			
		||||
	sig_data.pdp = pdp;
 | 
			
		||||
	osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data);
 | 
			
		||||
 | 
			
		||||
	if (osmo_timer_pending(&pdp->timer)) {
 | 
			
		||||
		LOGPDPCTXP(LOGL_ERROR, pdp, "Freeing PDP ctx with timer %u pending\n", pdp->T);
 | 
			
		||||
		osmo_timer_del(&pdp->timer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctr_group_free(pdp->ctrg);
 | 
			
		||||
	if (pdp->mm)
 | 
			
		||||
		llist_del(&pdp->list);
 | 
			
		||||
 
 | 
			
		||||
@@ -639,7 +639,11 @@ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg)
 | 
			
		||||
		return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pdp->ggsn)
 | 
			
		||||
		return sgsn_delete_pdp_ctx(pdp);
 | 
			
		||||
	/* GTP side already detached, freeing */
 | 
			
		||||
	sgsn_pdp_ctx_free(pdp);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 24.008 § 9.5.9: Deactivate PDP Context Accept */
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/endian.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
@@ -164,27 +165,43 @@ static void debug_ip_packet(uint8_t *data, int len, int dir, char *info)
 | 
			
		||||
 | 
			
		||||
/* Chapter 7.2: SN-PDU Formats */
 | 
			
		||||
struct sndcp_common_hdr {
 | 
			
		||||
#if OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	/* octet 1 */
 | 
			
		||||
	uint8_t nsapi:4;
 | 
			
		||||
	uint8_t more:1;
 | 
			
		||||
	uint8_t type:1;
 | 
			
		||||
	uint8_t first:1;
 | 
			
		||||
	uint8_t spare:1;
 | 
			
		||||
#elif OSMO_IS_BIG_ENDIAN
 | 
			
		||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
 | 
			
		||||
	uint8_t spare:1, first:1, type:1, more:1, nsapi:4;
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
/* PCOMP / DCOMP only exist in first fragment */
 | 
			
		||||
struct sndcp_comp_hdr {
 | 
			
		||||
#if OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	/* octet 2 */
 | 
			
		||||
	uint8_t pcomp:4;
 | 
			
		||||
	uint8_t dcomp:4;
 | 
			
		||||
#elif OSMO_IS_BIG_ENDIAN
 | 
			
		||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
 | 
			
		||||
	uint8_t dcomp:4, pcomp:4;
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct sndcp_udata_hdr {
 | 
			
		||||
#if OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	/* octet 3 */
 | 
			
		||||
	uint8_t npdu_high:4;
 | 
			
		||||
	uint8_t seg_nr:4;
 | 
			
		||||
	/* octet 4 */
 | 
			
		||||
	uint8_t npdu_low;
 | 
			
		||||
#elif OSMO_IS_BIG_ENDIAN
 | 
			
		||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
 | 
			
		||||
	uint8_t seg_nr:4, npdu_high:4;
 | 
			
		||||
	uint8_t npdu_low;
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -366,8 +383,12 @@ static int defrag_segments(struct gprs_sndcp_entity *sne)
 | 
			
		||||
	rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
 | 
			
		||||
				  sne->nsapi, msg, npdu_len, expnd);
 | 
			
		||||
 | 
			
		||||
	if (any_pcomp_or_dcomp_active(sgsn))
 | 
			
		||||
		talloc_free(expnd);
 | 
			
		||||
	/* we must free the memory we allocated above; ownership is not transferred
 | 
			
		||||
	 * downwards in the call above */
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
 | 
			
		||||
	/* Note: We do not have to free expnd explicitly, because it is created
 | 
			
		||||
	 * within the talloc context of msg, which we just freed. */
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -787,7 +808,7 @@ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
 | 
			
		||||
				lle, lle->llme->tlli, lle->sapi, sch->nsapi);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	gprs_gb_recv_pdu(mmctx);
 | 
			
		||||
	gprs_gb_recv_pdu(mmctx, msg);
 | 
			
		||||
 | 
			
		||||
	if (scomph) {
 | 
			
		||||
		sne->defrag.pcomp = scomph->pcomp;
 | 
			
		||||
 
 | 
			
		||||
@@ -812,7 +812,7 @@ static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr,
 | 
			
		||||
	return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_subscr_location_update(struct gprs_subscr *subscr)
 | 
			
		||||
int gprs_subscr_location_update(struct gprs_subscr *subscr, enum sgsn_ran_type ran_type)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gsup_message gsup_msg = {0};
 | 
			
		||||
 | 
			
		||||
@@ -820,6 +820,18 @@ int gprs_subscr_location_update(struct gprs_subscr *subscr)
 | 
			
		||||
		"subscriber data is not available\n");
 | 
			
		||||
 | 
			
		||||
	gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
 | 
			
		||||
 | 
			
		||||
	switch (ran_type) {
 | 
			
		||||
	case MM_CTX_T_GERAN_Gb:
 | 
			
		||||
		gsup_msg.current_rat_type = OSMO_RAT_GERAN_A;
 | 
			
		||||
		break;
 | 
			
		||||
	case MM_CTX_T_UTRAN_Iu:
 | 
			
		||||
		gsup_msg.current_rat_type = OSMO_RAT_UTRAN_IU;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -858,6 +870,8 @@ struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx
 | 
			
		||||
 | 
			
		||||
	if (!subscr) {
 | 
			
		||||
		subscr = gprs_subscr_get_or_create(mmctx->imsi);
 | 
			
		||||
		if (!subscr)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT;
 | 
			
		||||
		subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
 | 
			
		||||
	}
 | 
			
		||||
@@ -881,10 +895,12 @@ int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
 | 
			
		||||
	LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
 | 
			
		||||
 | 
			
		||||
	subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
 | 
			
		||||
	if (!subscr)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
 | 
			
		||||
 | 
			
		||||
	rc = gprs_subscr_location_update(subscr);
 | 
			
		||||
	rc = gprs_subscr_location_update(subscr, mmctx->ran_type);
 | 
			
		||||
	gprs_subscr_put(subscr);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -906,6 +922,8 @@ int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
 | 
			
		||||
	LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n");
 | 
			
		||||
 | 
			
		||||
	subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
 | 
			
		||||
	if (!subscr)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -167,6 +167,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
 | 
			
		||||
	pdp->hisaddr0 =	ggsn->remote_addr;
 | 
			
		||||
	pdp->hisaddr1 = ggsn->remote_addr;
 | 
			
		||||
	//pdp->cch_pdp = 512;	/* Charging Flat Rate */
 | 
			
		||||
	pdp->radio_pri = 0x4;
 | 
			
		||||
 | 
			
		||||
	/* MS provided APN, subscription was verified by the caller */
 | 
			
		||||
	pdp->selmode = 0xFC | 0x00;
 | 
			
		||||
@@ -311,11 +312,16 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
 | 
			
		||||
	return pctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* SGSN wants to delete a PDP context */
 | 
			
		||||
/* SGSN wants to delete a PDP context, send first DeleteCtxReq on the GTP side,
 | 
			
		||||
   then upon DeleteCtx ACK it will send DeactPdpAcc to the MS if still
 | 
			
		||||
   connected. */
 | 
			
		||||
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
 | 
			
		||||
{
 | 
			
		||||
	LOGPDPCTXP(LOGL_INFO, pctx, "Delete PDP Context\n");
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(pctx->ggsn);
 | 
			
		||||
	OSMO_ASSERT(pctx->lib);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: decide if we need teardown or not ! */
 | 
			
		||||
	return gtp_delete_context_req2(pctx->ggsn->gsn, pctx->lib, pctx, 1);
 | 
			
		||||
}
 | 
			
		||||
@@ -660,14 +666,18 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
 | 
			
		||||
 | 
			
		||||
	switch (mm->gmm_fsm->state) {
 | 
			
		||||
	case ST_GMM_REGISTERED_SUSPENDED:
 | 
			
		||||
		/* initiate PS PAGING procedure */
 | 
			
		||||
		gprs_gb_page_ps_ra(mm);
 | 
			
		||||
		/* FIXME: queue the packet we received from GTP */
 | 
			
		||||
		break;
 | 
			
		||||
		LOGMMCTXP(LOGL_INFO, mm, "Dropping DL packet for MS in GMM state %s\n",
 | 
			
		||||
			  osmo_fsm_inst_state_name(mm->gmm_fsm));
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	case ST_GMM_REGISTERED_NORMAL:
 | 
			
		||||
		OSMO_ASSERT(mm->gb.mm_state_fsm->state != ST_MM_IDLE);
 | 
			
		||||
		if (mm->gb.mm_state_fsm->state == ST_MM_STANDBY)
 | 
			
		||||
		if (mm->gb.mm_state_fsm->state == ST_MM_STANDBY) {
 | 
			
		||||
			LOGMMCTXP(LOGL_INFO, mm, "Paging MS in GMM state %s, MM state %s\n",
 | 
			
		||||
				  osmo_fsm_inst_state_name(mm->gmm_fsm),
 | 
			
		||||
				  osmo_fsm_inst_state_name(mm->gb.mm_state_fsm));
 | 
			
		||||
			gprs_gb_page_ps_ra(mm);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* FIXME: queue the packet we received from GTP */
 | 
			
		||||
		break;
 | 
			
		||||
@@ -733,7 +743,7 @@ static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	struct sgsn_instance *sgi = fd->data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!(what & BSC_FD_READ))
 | 
			
		||||
	if (!(what & OSMO_FD_READ))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	switch (fd->priv_nr) {
 | 
			
		||||
@@ -771,31 +781,19 @@ int sgsn_gtp_init(struct sgsn_instance *sgi)
 | 
			
		||||
	if (gsn->mode != GTP_MODE_SGSN)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	sgi->gtp_fd0.fd = gsn->fd0;
 | 
			
		||||
	sgi->gtp_fd0.priv_nr = 0;
 | 
			
		||||
	sgi->gtp_fd0.data = sgi;
 | 
			
		||||
	sgi->gtp_fd0.when = BSC_FD_READ;
 | 
			
		||||
	sgi->gtp_fd0.cb = sgsn_gtp_fd_cb;
 | 
			
		||||
	osmo_fd_setup(&sgi->gtp_fd0, gsn->fd0, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 0);
 | 
			
		||||
	rc = osmo_fd_register(&sgi->gtp_fd0);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	sgi->gtp_fd1c.fd = gsn->fd1c;
 | 
			
		||||
	sgi->gtp_fd1c.priv_nr = 1;
 | 
			
		||||
	sgi->gtp_fd1c.data = sgi;
 | 
			
		||||
	sgi->gtp_fd1c.when = BSC_FD_READ;
 | 
			
		||||
	sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb;
 | 
			
		||||
	osmo_fd_setup(&sgi->gtp_fd1c, gsn->fd1c, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 1);
 | 
			
		||||
	rc = osmo_fd_register(&sgi->gtp_fd1c);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		osmo_fd_unregister(&sgi->gtp_fd0);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sgi->gtp_fd1u.fd = gsn->fd1u;
 | 
			
		||||
	sgi->gtp_fd1u.priv_nr = 2;
 | 
			
		||||
	sgi->gtp_fd1u.data = sgi;
 | 
			
		||||
	sgi->gtp_fd1u.when = BSC_FD_READ;
 | 
			
		||||
	sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb;
 | 
			
		||||
	osmo_fd_setup(&sgi->gtp_fd1u, gsn->fd1u, OSMO_FD_READ, sgsn_gtp_fd_cb, sgi, 2);
 | 
			
		||||
	rc = osmo_fd_register(&sgi->gtp_fd1u);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		osmo_fd_unregister(&sgi->gtp_fd0);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,15 +39,18 @@
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/vty/cpu_sched_vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
 | 
			
		||||
@@ -58,11 +61,13 @@
 | 
			
		||||
#include <osmocom/sgsn/gprs_llc.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_gmm.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_ranap.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_gb.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/ctrl/ports.h>
 | 
			
		||||
 | 
			
		||||
#include <gtp.h>
 | 
			
		||||
#include <osmocom/sgsn/sgsn_rim.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
@@ -78,7 +83,7 @@
 | 
			
		||||
void *tall_sgsn_ctx;
 | 
			
		||||
struct ctrl_handle *g_ctrlh;
 | 
			
		||||
 | 
			
		||||
struct gprs_ns_inst *sgsn_nsi;
 | 
			
		||||
struct gprs_ns2_inst *sgsn_nsi;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
const char *openbsc_copyright =
 | 
			
		||||
	"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
 | 
			
		||||
@@ -92,27 +97,6 @@ const char *openbsc_copyright =
 | 
			
		||||
 | 
			
		||||
struct sgsn_instance *sgsn;
 | 
			
		||||
 | 
			
		||||
/* call-back function for the NS protocol */
 | 
			
		||||
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
 | 
			
		||||
		      struct msgb *msg, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case GPRS_NS_EVT_UNIT_DATA:
 | 
			
		||||
		/* hand the message into the BSSGP implementation */
 | 
			
		||||
		rc = bssgp_rcvmsg(msg);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
 | 
			
		||||
		if (msg)
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
		rc = -EIO;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* call-back function for the BSSGP protocol */
 | 
			
		||||
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
@@ -137,15 +121,17 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
		break;
 | 
			
		||||
	case SAP_BSSGP_NM:
 | 
			
		||||
		break;
 | 
			
		||||
	case SAP_BSSGP_RIM:
 | 
			
		||||
		return sgsn_rim_rx(bp, oph->msg);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void signal_handler(int signal)
 | 
			
		||||
static void signal_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signal);
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signum);
 | 
			
		||||
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
	switch (signum) {
 | 
			
		||||
	case SIGINT:
 | 
			
		||||
	case SIGTERM:
 | 
			
		||||
		osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
 | 
			
		||||
@@ -153,8 +139,17 @@ static void signal_handler(int signal)
 | 
			
		||||
		exit(0);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGABRT:
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report
 | 
			
		||||
		 * and then return to the caller, who will abort the process */
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report and
 | 
			
		||||
		 * then run default SIGABRT handler, who will generate coredump
 | 
			
		||||
		 * and abort the process. abort() should do this for us after we
 | 
			
		||||
		 * return, but program wouldn't exit if an external SIGABRT is
 | 
			
		||||
		 * received.
 | 
			
		||||
		 */
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_sgsn_ctx, stderr);
 | 
			
		||||
		signal(SIGABRT, SIG_DFL);
 | 
			
		||||
		raise(SIGABRT);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_sgsn_ctx, stderr);
 | 
			
		||||
@@ -170,40 +165,12 @@ static void signal_handler(int signal)
 | 
			
		||||
/* NSI that BSSGP uses when transmitting on NS */
 | 
			
		||||
extern struct gprs_ns_inst *bssgp_nsi;
 | 
			
		||||
 | 
			
		||||
int sgsn_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
	/* So far the SGSN has no nested nodes that need parent node
 | 
			
		||||
	 * declaration, except for the ss7 vty nodes. */
 | 
			
		||||
	switch (node) {
 | 
			
		||||
	case SGSN_NODE:
 | 
			
		||||
		return 1;
 | 
			
		||||
	default:
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
		return osmo_ss7_is_config_node(vty, node);
 | 
			
		||||
#else
 | 
			
		||||
		return 0;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sgsn_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	/* So far the SGSN has no nested nodes that need parent node
 | 
			
		||||
	 * declaration, except for the ss7 vty nodes. */
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
	return osmo_ss7_vty_go_parent(vty);
 | 
			
		||||
#else
 | 
			
		||||
	vty->node = CONFIG_NODE;
 | 
			
		||||
	vty->index = NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OsmoSGSN",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
	.go_parent_cb	= sgsn_vty_go_parent,
 | 
			
		||||
	.is_config_node	= sgsn_vty_is_config_node,
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
	.go_parent_cb	= osmo_ss7_vty_go_parent,
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void print_help(void)
 | 
			
		||||
@@ -216,12 +183,43 @@ static void print_help(void)
 | 
			
		||||
	printf("  -s --disable-color\n");
 | 
			
		||||
	printf("  -c --config-file\tThe config file to use [%s]\n", CONFIG_FILE_DEFAULT);
 | 
			
		||||
	printf("  -e --log-level number\tSet a global log level\n");
 | 
			
		||||
 | 
			
		||||
	printf("\nVTY reference generation:\n");
 | 
			
		||||
	printf("     --vty-ref-mode MODE        VTY reference generation mode (e.g. 'expert').\n");
 | 
			
		||||
	printf("     --vty-ref-xml              Generate the VTY reference XML output and exit.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_long_options(const char *prog_name, const int long_option)
 | 
			
		||||
{
 | 
			
		||||
	static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	switch (long_option) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
 | 
			
		||||
		if (vty_ref_mode < 0) {
 | 
			
		||||
			fprintf(stderr, "%s: Unknown VTY reference generation "
 | 
			
		||||
				"mode '%s'\n", prog_name, optarg);
 | 
			
		||||
			exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
 | 
			
		||||
			get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
 | 
			
		||||
			get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
 | 
			
		||||
		vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	default:
 | 
			
		||||
		fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void handle_options(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int option_index = 0, c;
 | 
			
		||||
		static int long_option = 0;
 | 
			
		||||
		static struct option long_options[] = {
 | 
			
		||||
			{"help", 0, 0, 'h'},
 | 
			
		||||
			{"debug", 1, 0, 'd'},
 | 
			
		||||
@@ -231,6 +229,8 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			{"timestamp", 0, 0, 'T'},
 | 
			
		||||
			{ "version", 0, 0, 'V' },
 | 
			
		||||
			{"log-level", 1, 0, 'e'},
 | 
			
		||||
			{"vty-ref-mode", 1, &long_option, 1},
 | 
			
		||||
			{"vty-ref-xml", 0, &long_option, 2},
 | 
			
		||||
			{NULL, 0, 0, 0}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@@ -240,6 +240,9 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		switch (c) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			handle_long_options(argv[0], long_option);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'h':
 | 
			
		||||
			//print_usage();
 | 
			
		||||
			print_help();
 | 
			
		||||
@@ -312,11 +315,6 @@ static struct log_info_cat gprs_categories[] = {
 | 
			
		||||
		.description = "GPRS Network Service (NS)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DBSSGP] = {
 | 
			
		||||
		.name = "DBSSGP",
 | 
			
		||||
		.description = "GPRS BSS Gateway Protocol (BSSGP)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DLLC] = {
 | 
			
		||||
		.name = "DLLC",
 | 
			
		||||
		.description = "GPRS Logical Link Control Protocol (LLC)",
 | 
			
		||||
@@ -352,6 +350,11 @@ static struct log_info_cat gprs_categories[] = {
 | 
			
		||||
		.description = "GPRS Tunnelling Protocol (GTP)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DRIM] = {
 | 
			
		||||
		.name = "DRIM",
 | 
			
		||||
		.description = "RAN Information Management (RIM)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct log_info gprs_log_info = {
 | 
			
		||||
@@ -396,6 +399,7 @@ int main(int argc, char **argv)
 | 
			
		||||
	osmo_stats_vty_add_cmds();
 | 
			
		||||
	sgsn_vty_init(&sgsn->cfg);
 | 
			
		||||
	ctrl_vty_init(tall_sgsn_ctx);
 | 
			
		||||
	osmo_cpu_sched_vty_init(tall_sgsn_ctx);
 | 
			
		||||
 | 
			
		||||
#if BUILD_IU
 | 
			
		||||
	OSMO_ASSERT(osmo_ss7_init() == 0);
 | 
			
		||||
@@ -403,6 +407,30 @@ int main(int argc, char **argv)
 | 
			
		||||
	osmo_sccp_vty_init();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	rate_ctr_init(tall_sgsn_ctx);
 | 
			
		||||
 | 
			
		||||
	logging_vty_add_deprecated_subsys(tall_sgsn_ctx, "bssgp");
 | 
			
		||||
 | 
			
		||||
	sgsn_nsi = gprs_ns2_instantiate(tall_sgsn_ctx, &gprs_ns_prim_cb, NULL);
 | 
			
		||||
	if (!sgsn_nsi) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	sgsn->cfg.nsi = sgsn_nsi;
 | 
			
		||||
	bssgp_set_bssgp_callback(gprs_gb_send_cb, sgsn_nsi);
 | 
			
		||||
 | 
			
		||||
	gprs_llc_init("/usr/local/lib/osmocom/crypt/");
 | 
			
		||||
	sgsn_rate_ctr_init();
 | 
			
		||||
	sgsn_inst_init(sgsn);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	gprs_ns2_vty_init(sgsn_nsi);
 | 
			
		||||
	bssgp_vty_init();
 | 
			
		||||
	gprs_llc_vty_init();
 | 
			
		||||
	gprs_sndcp_vty_init();
 | 
			
		||||
	sgsn_auth_init(sgsn);
 | 
			
		||||
	sgsn_cdr_init(sgsn);
 | 
			
		||||
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	/* Backwards compatibility: for years, the default config file name was
 | 
			
		||||
@@ -420,30 +448,6 @@ int main(int argc, char **argv)
 | 
			
		||||
			osmo_talloc_replace_string(sgsn, &sgsn->config_file, CONFIG_FILE_DEFAULT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctr_init(tall_sgsn_ctx);
 | 
			
		||||
 | 
			
		||||
	gprs_ns_set_log_ss(DNS);
 | 
			
		||||
	bssgp_set_log_ss(DBSSGP);
 | 
			
		||||
 | 
			
		||||
	sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_sgsn_ctx);
 | 
			
		||||
	if (!sgsn_nsi) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	bssgp_nsi = sgsn->cfg.nsi = sgsn_nsi;
 | 
			
		||||
 | 
			
		||||
	gprs_llc_init("/usr/local/lib/osmocom/crypt/");
 | 
			
		||||
	sgsn_rate_ctr_init();
 | 
			
		||||
	sgsn_inst_init(sgsn);
 | 
			
		||||
 | 
			
		||||
	gprs_ns_vty_init(bssgp_nsi);
 | 
			
		||||
	bssgp_vty_init();
 | 
			
		||||
	gprs_llc_vty_init();
 | 
			
		||||
	gprs_sndcp_vty_init();
 | 
			
		||||
	sgsn_auth_init(sgsn);
 | 
			
		||||
	sgsn_cdr_init(sgsn);
 | 
			
		||||
	/* FIXME: register signal handler for SS_L_NS */
 | 
			
		||||
 | 
			
		||||
	rc = sgsn_parse_config(sgsn->config_file);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Error in config file\n");
 | 
			
		||||
@@ -484,19 +488,6 @@ int main(int argc, char **argv)
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = gprs_ns_nsip_listen(sgsn_nsi);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = gprs_ns_frgre_listen(sgsn_nsi);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
 | 
			
		||||
			"socket. Do you have CAP_NET_RAW?\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sgsn->cfg.dynamic_lookup) {
 | 
			
		||||
		if (sgsn_ares_init(sgsn) != 0) {
 | 
			
		||||
			LOGP(DGPRS, LOGL_FATAL,
 | 
			
		||||
@@ -512,8 +503,8 @@ int main(int argc, char **argv)
 | 
			
		||||
						 "OsmoSGSN",
 | 
			
		||||
						 (23 << 3) + 4,
 | 
			
		||||
						 OSMO_SS7_ASP_PROT_M3UA,
 | 
			
		||||
						 0, NULL,
 | 
			
		||||
						 0, "127.0.0.1");
 | 
			
		||||
						 0, "localhost",
 | 
			
		||||
						 0, "localhost");
 | 
			
		||||
	if (!sccp) {
 | 
			
		||||
		printf("Setting up SCCP client failed.\n");
 | 
			
		||||
		return 8;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								src/sgsn/sgsn_rim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/sgsn/sgsn_rim.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gsm/tlv.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
#include <osmocom/sgsn/sgsn_rim.h>
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
 | 
			
		||||
/* Find an NSEI for the destination cell, this function works only for GERAN! */
 | 
			
		||||
static int find_dest_nsei_geran(struct bssgp_rim_routing_info *dest_rim_ri, uint16_t nsei)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_bvc_ctx *bvc_ctx;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dest_rim_ri->discr == BSSGP_RIM_ROUTING_INFO_GERAN);
 | 
			
		||||
 | 
			
		||||
	bvc_ctx = btsctx_by_raid_cid(&dest_rim_ri->geran.raid, dest_rim_ri->geran.cid);
 | 
			
		||||
	if (!bvc_ctx) {
 | 
			
		||||
		LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) cannot find NSEI for destination cell\n", nsei);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bvc_ctx->nsei;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sgsn_rim_rx(struct osmo_bssgp_prim *bp, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
 | 
			
		||||
	int d_nsei;
 | 
			
		||||
	uint16_t nsei = msgb_nsei(msg);
 | 
			
		||||
 | 
			
		||||
	/* At the moment we only support GERAN, so we block all other network
 | 
			
		||||
	 * types here. */
 | 
			
		||||
	if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
 | 
			
		||||
		LOGP(DRIM, LOGL_ERROR,
 | 
			
		||||
		     "BSSGP RIM (NSEI=%u) only GERAN supported, destination cell is not a GERAN cell -- rejected.\n",
 | 
			
		||||
		     nsei);
 | 
			
		||||
		/* At the moment we can only handle GERAN addresses, any other
 | 
			
		||||
		 * type of address will be consideres as an invalid address.
 | 
			
		||||
		 * see also: 3GPP TS 48.018, section 8c.3.1.3 */
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
	if (pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
 | 
			
		||||
		LOGP(DRIM, LOGL_ERROR,
 | 
			
		||||
		     "BSSGP RIM (NSEI=%u) only GERAN supported, source cell is not a GERAN cell -- rejected.\n", nsei);
 | 
			
		||||
		/* See comment above */
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d_nsei = find_dest_nsei_geran(&pdu->routing_info_dest, nsei);
 | 
			
		||||
	if (d_nsei < 0) {
 | 
			
		||||
		LOGP(DRIM, LOGL_NOTICE, "BSSGP RIM (NSEI=%u) Cell %s unknown to this sgsn\n",
 | 
			
		||||
		     nsei, bssgp_rim_ri_name(&pdu->routing_info_dest));
 | 
			
		||||
		/* In case of an invalid destination address we respond with
 | 
			
		||||
		 * a BSSGP STATUS PDU, see also: 3GPP TS 48.018, section 8c.3.1.3 */
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Forward PDU as it is to the correct interface */
 | 
			
		||||
	return bssgp_tx_rim(pdu, (uint16_t) d_nsei);
 | 
			
		||||
}
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sgsn/debug.h>
 | 
			
		||||
#include <osmocom/sgsn/sgsn.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_gmm.h>
 | 
			
		||||
#include <osmocom/sgsn/gprs_sgsn.h>
 | 
			
		||||
#include <osmocom/sgsn/vty.h>
 | 
			
		||||
@@ -186,6 +186,8 @@ static int config_write_sgsn(struct vty *vty)
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "sgsn%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " gtp state-dir %s%s",
 | 
			
		||||
		g_cfg->gtp_statedir, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " gtp local-ip %s%s",
 | 
			
		||||
		inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
@@ -315,6 +317,17 @@ DEFUN(cfg_sgsn, cfg_sgsn_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_sgsn_state_dir, cfg_sgsn_state_dir_cmd,
 | 
			
		||||
	"gtp state-dir PATH",
 | 
			
		||||
	"GTP Parameters\n"
 | 
			
		||||
	"Set the directory for the GTP State file\n"
 | 
			
		||||
	"Local Directory\n")
 | 
			
		||||
{
 | 
			
		||||
	osmo_talloc_replace_string(sgsn, &sgsn->cfg.gtp_statedir, argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd,
 | 
			
		||||
	"gtp local-ip A.B.C.D",
 | 
			
		||||
	"GTP Parameters\n"
 | 
			
		||||
@@ -1007,6 +1020,10 @@ DEFUN(update_subscr_create, update_subscr_create_cmd,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subscr = gprs_subscr_get_or_create(imsi);
 | 
			
		||||
	if (!subscr) {
 | 
			
		||||
		vty_out(vty, "Can not create subscriber. Out of memory.%s", imsi);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	subscr->keep_in_ram = 1;
 | 
			
		||||
	gprs_subscr_put(subscr);
 | 
			
		||||
 | 
			
		||||
@@ -1432,6 +1449,7 @@ int sgsn_vty_init(struct sgsn_config *cfg)
 | 
			
		||||
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_sgsn_cmd);
 | 
			
		||||
	install_node(&sgsn_node, config_write_sgsn);
 | 
			
		||||
	install_element(SGSN_NODE, &cfg_sgsn_state_dir_cmd);
 | 
			
		||||
	install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
 | 
			
		||||
	install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
 | 
			
		||||
	//install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	gprs \
 | 
			
		||||
	gbproxy \
 | 
			
		||||
	gtphub \
 | 
			
		||||
	sgsn \
 | 
			
		||||
	xid \
 | 
			
		||||
@@ -33,7 +32,7 @@ EXTRA_DIST = \
 | 
			
		||||
	$(TESTSUITE) \
 | 
			
		||||
	vty_test_runner.py \
 | 
			
		||||
	ctrl_test_runner.py \
 | 
			
		||||
	test_nodes.vty \
 | 
			
		||||
	osmo-sgsn_test-nodes.vty \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
TESTSUITE = $(srcdir)/testsuite
 | 
			
		||||
@@ -64,7 +63,7 @@ vty-transcript-test:
 | 
			
		||||
	osmo_verify_transcript_vty.py -v \
 | 
			
		||||
		-n OsmoSGSN -p 4245 \
 | 
			
		||||
		-r "$(top_builddir)/src/sgsn/osmo-sgsn -c $(top_srcdir)/doc/examples/osmo-sgsn/osmo-sgsn.cfg" \
 | 
			
		||||
		$(U) $${T:-$(srcdir)/*.vty}
 | 
			
		||||
		$(U) $${T:-$(srcdir)/osmo-sgsn*.vty}
 | 
			
		||||
	rm -f $(builddir)/sms.db $(builddir)/gsn_restart
 | 
			
		||||
 | 
			
		||||
# don't run multiple tests concurrently so that the ports don't conflict
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	gbproxy_test.ok \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	gbproxy_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gbproxy_test_SOURCES = \
 | 
			
		||||
	gbproxy_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gbproxy_test_LDFLAGS = \
 | 
			
		||||
	-Wl,--wrap=osmo_get_rand_id \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gbproxy_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/gbproxy/gb_proxy.o \
 | 
			
		||||
	$(top_builddir)/src/gbproxy/gb_proxy_patch.o \
 | 
			
		||||
	$(top_builddir)/src/gbproxy/gb_proxy_peer.o \
 | 
			
		||||
	$(top_builddir)/src/gbproxy/gb_proxy_tlli.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_gb_parse.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_llc_parse.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/crc24.o \
 | 
			
		||||
	$(top_builddir)/src/gprs/gprs_utils.o \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	-lrt \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -54,7 +54,7 @@
 | 
			
		||||
void gtphub_init(struct gtphub *hub);
 | 
			
		||||
void gtphub_free(struct gtphub *hub);
 | 
			
		||||
 | 
			
		||||
void *osmo_gtphub_ctx;
 | 
			
		||||
extern void *osmo_gtphub_ctx;
 | 
			
		||||
 | 
			
		||||
static void nr_mapping_free(struct expiring_item *e)
 | 
			
		||||
{
 | 
			
		||||
@@ -378,37 +378,37 @@ static void test_expiry(void)
 | 
			
		||||
char resolve_ggsn_got_imsi[GSM23003_IMSI_MAX_DIGITS+1];
 | 
			
		||||
char resolve_ggsn_got_ni[GSM_APN_LENGTH];
 | 
			
		||||
 | 
			
		||||
struct osmo_sockaddr resolved_ggsn_addr;
 | 
			
		||||
struct sgsn_sockaddr resolved_ggsn_addr;
 | 
			
		||||
static int resolve_to_ggsn(const char *addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	LVL2_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_addr,
 | 
			
		||||
	LVL2_ASSERT(sgsn_sockaddr_init_udp(&resolved_ggsn_addr,
 | 
			
		||||
					   addr, port)
 | 
			
		||||
		    == 0);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_sockaddr resolved_sgsn_addr;
 | 
			
		||||
struct sgsn_sockaddr resolved_sgsn_addr;
 | 
			
		||||
static int resolve_to_sgsn(const char *addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	LVL2_ASSERT(osmo_sockaddr_init_udp(&resolved_sgsn_addr,
 | 
			
		||||
	LVL2_ASSERT(sgsn_sockaddr_init_udp(&resolved_sgsn_addr,
 | 
			
		||||
					   addr, port)
 | 
			
		||||
		    == 0);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_sockaddr sgsn_sender;
 | 
			
		||||
struct sgsn_sockaddr sgsn_sender;
 | 
			
		||||
static int send_from_sgsn(const char *addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	LVL2_ASSERT(osmo_sockaddr_init_udp(&sgsn_sender,
 | 
			
		||||
	LVL2_ASSERT(sgsn_sockaddr_init_udp(&sgsn_sender,
 | 
			
		||||
					   addr, port)
 | 
			
		||||
		    == 0);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_sockaddr ggsn_sender;
 | 
			
		||||
struct sgsn_sockaddr ggsn_sender;
 | 
			
		||||
static int send_from_ggsn(const char *addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	LVL2_ASSERT(osmo_sockaddr_init_udp(&ggsn_sender,
 | 
			
		||||
	LVL2_ASSERT(sgsn_sockaddr_init_udp(&ggsn_sender,
 | 
			
		||||
					   addr, port)
 | 
			
		||||
		    == 0);
 | 
			
		||||
	return 1;
 | 
			
		||||
@@ -491,18 +491,18 @@ int __wrap_gtphub_ares_init(struct gtphub *hub)
 | 
			
		||||
 | 
			
		||||
/* override, requires '-Wl,--wrap=gtphub_write' */
 | 
			
		||||
int __real_gtphub_write(const struct osmo_fd *to,
 | 
			
		||||
			const struct osmo_sockaddr *to_addr,
 | 
			
		||||
			const struct sgsn_sockaddr *to_addr,
 | 
			
		||||
			const uint8_t *buf, size_t buf_len);
 | 
			
		||||
 | 
			
		||||
int __wrap_gtphub_write(const struct osmo_fd *to,
 | 
			
		||||
			const struct osmo_sockaddr *to_addr,
 | 
			
		||||
			const struct sgsn_sockaddr *to_addr,
 | 
			
		||||
			const uint8_t *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
	printf("Out-of-band gtphub_write(%d):\n"
 | 
			
		||||
	       "to %s\n"
 | 
			
		||||
	       "%s\n",
 | 
			
		||||
	       (int)buf_len,
 | 
			
		||||
	       osmo_sockaddr_to_str(to_addr),
 | 
			
		||||
	       sgsn_sockaddr_to_str(to_addr),
 | 
			
		||||
	       osmo_hexdump(buf, buf_len));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -529,6 +529,8 @@ static int _reply_is(const char *hex, const char *file, int line)
 | 
			
		||||
	const char *dump = osmo_hexdump_nospc(reply_buf, len);
 | 
			
		||||
	int cmp = strcmp(dump, hex);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dump && hex);
 | 
			
		||||
 | 
			
		||||
	if (cmp != 0) {
 | 
			
		||||
		printf("\n%s:%d: reply_is(): MISMATCH\n"
 | 
			
		||||
		       "  expecting:\n'%s'\n"
 | 
			
		||||
@@ -553,11 +555,11 @@ static int _reply_is(const char *hex, const char *file, int line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define same_addr(GOT, EXPECTED) _same_addr((GOT),(EXPECTED), __FILE__, __LINE__)
 | 
			
		||||
static int _same_addr(const struct osmo_sockaddr *got,
 | 
			
		||||
		      const struct osmo_sockaddr *expected,
 | 
			
		||||
static int _same_addr(const struct sgsn_sockaddr *got,
 | 
			
		||||
		      const struct sgsn_sockaddr *expected,
 | 
			
		||||
		      const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	int cmp = osmo_sockaddr_cmp(got, expected);
 | 
			
		||||
	int cmp = sgsn_sockaddr_cmp(got, expected);
 | 
			
		||||
	if (!cmp)
 | 
			
		||||
		return 1;
 | 
			
		||||
	char buf[256];
 | 
			
		||||
@@ -565,8 +567,8 @@ static int _same_addr(const struct osmo_sockaddr *got,
 | 
			
		||||
	       "  expecting: '%s'\n"
 | 
			
		||||
	       "        got: '%s'\n\n",
 | 
			
		||||
	       file, line,
 | 
			
		||||
	       osmo_sockaddr_to_str(expected),
 | 
			
		||||
	       osmo_sockaddr_to_strb(got, buf, sizeof(buf)));
 | 
			
		||||
	       sgsn_sockaddr_to_str(expected),
 | 
			
		||||
	       sgsn_sockaddr_to_strb(got, buf, sizeof(buf)));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -668,7 +670,7 @@ static void test_echo(void)
 | 
			
		||||
	now = 123;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fd *to_ofd;
 | 
			
		||||
	struct osmo_sockaddr to_addr;
 | 
			
		||||
	struct sgsn_sockaddr to_addr;
 | 
			
		||||
	struct gtphub_peer_port *pp;
 | 
			
		||||
	int send;
 | 
			
		||||
 | 
			
		||||
@@ -855,13 +857,13 @@ static void test_echo(void)
 | 
			
		||||
#define msg_from_sgsn_c(A,B,C,D) msg_from_sgsn(GTPH_PLANE_CTRL, A,B,C,D)
 | 
			
		||||
#define msg_from_sgsn_u(A,B,C,D) msg_from_sgsn(GTPH_PLANE_USER, A,B,C,D)
 | 
			
		||||
static int msg_from_sgsn(int plane_idx,
 | 
			
		||||
			 struct osmo_sockaddr *_sgsn_sender,
 | 
			
		||||
			 struct osmo_sockaddr *ggsn_receiver,
 | 
			
		||||
			 struct sgsn_sockaddr *_sgsn_sender,
 | 
			
		||||
			 struct sgsn_sockaddr *ggsn_receiver,
 | 
			
		||||
			 const char *hex_from_sgsn,
 | 
			
		||||
			 const char *hex_to_ggsn)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fd *ggsn_ofd = NULL;
 | 
			
		||||
	struct osmo_sockaddr ggsn_addr;
 | 
			
		||||
	struct sgsn_sockaddr ggsn_addr;
 | 
			
		||||
	int send;
 | 
			
		||||
	send = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, _sgsn_sender,
 | 
			
		||||
				 buf, msg(hex_from_sgsn), now,
 | 
			
		||||
@@ -875,13 +877,13 @@ static int msg_from_sgsn(int plane_idx,
 | 
			
		||||
#define msg_from_ggsn_c(A,B,C,D) msg_from_ggsn(GTPH_PLANE_CTRL, A,B,C,D)
 | 
			
		||||
#define msg_from_ggsn_u(A,B,C,D) msg_from_ggsn(GTPH_PLANE_USER, A,B,C,D)
 | 
			
		||||
static int msg_from_ggsn(int plane_idx,
 | 
			
		||||
			 struct osmo_sockaddr *ggsn_sender,
 | 
			
		||||
			 struct osmo_sockaddr *sgsn_receiver,
 | 
			
		||||
			 struct sgsn_sockaddr *ggsn_sender,
 | 
			
		||||
			 struct sgsn_sockaddr *sgsn_receiver,
 | 
			
		||||
			 const char *msg_from_ggsn,
 | 
			
		||||
			 const char *msg_to_sgsn)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fd *sgsn_ofd;
 | 
			
		||||
	struct osmo_sockaddr sgsn_addr;
 | 
			
		||||
	struct sgsn_sockaddr sgsn_addr;
 | 
			
		||||
	int send;
 | 
			
		||||
	send = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, ggsn_sender,
 | 
			
		||||
				 buf, msg(msg_from_ggsn), now,
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ OsmoSGSN(config)# list
 | 
			
		||||
OsmoSGSN(config)# sgsn
 | 
			
		||||
OsmoSGSN(config-sgsn)# list
 | 
			
		||||
...
 | 
			
		||||
  gtp state-dir PATH
 | 
			
		||||
  gtp local-ip A.B.C.D
 | 
			
		||||
  ggsn <0-255> remote-ip A.B.C.D
 | 
			
		||||
  ggsn <0-255> gtp-version (0|1)
 | 
			
		||||
@@ -1625,11 +1625,6 @@ static struct log_info_cat gprs_categories[] = {
 | 
			
		||||
		.description = "GPRS Network Service (NS)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_INFO,
 | 
			
		||||
	},
 | 
			
		||||
	[DBSSGP] = {
 | 
			
		||||
		.name = "DBSSGP",
 | 
			
		||||
		.description = "GPRS BSS Gateway Protocol (BSSGP)",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_DEBUG,
 | 
			
		||||
	},
 | 
			
		||||
	[DLLC] = {
 | 
			
		||||
		.name = "DLLC",
 | 
			
		||||
		.description = "GPRS Logical Link Control Protocol (LLC)",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,6 @@ cat $abs_srcdir/gprs/gprs_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([gbproxy])
 | 
			
		||||
AT_KEYWORDS([gbproxy])
 | 
			
		||||
cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([sgsn])
 | 
			
		||||
AT_KEYWORDS([sgsn])
 | 
			
		||||
AT_CHECK([test "$enable_sgsn_test" != no || exit 77])
 | 
			
		||||
 
 | 
			
		||||
@@ -67,55 +67,6 @@ class TestVTYBase(unittest.TestCase):
 | 
			
		||||
        self.vty = None
 | 
			
		||||
        osmoutil.end_proc(self.proc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestVTYGbproxy(TestVTYBase):
 | 
			
		||||
 | 
			
		||||
    def vty_command(self):
 | 
			
		||||
        return ["./src/gbproxy/osmo-gbproxy", "-c",
 | 
			
		||||
                "doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"]
 | 
			
		||||
 | 
			
		||||
    def vty_app(self):
 | 
			
		||||
        return (4246, "./src/gbproxy/osmo-gbproxy", "OsmoGbProxy", "gbproxy")
 | 
			
		||||
 | 
			
		||||
    def testVtyTree(self):
 | 
			
		||||
        self.vty.enable()
 | 
			
		||||
        self.assertTrue(self.vty.verify('configure terminal', ['']))
 | 
			
		||||
        self.assertEqual(self.vty.node(), 'config')
 | 
			
		||||
        self.checkForEndAndExit()
 | 
			
		||||
        self.assertTrue(self.vty.verify('ns', ['']))
 | 
			
		||||
        self.assertEqual(self.vty.node(), 'config-ns')
 | 
			
		||||
        self.checkForEndAndExit()
 | 
			
		||||
        self.assertTrue(self.vty.verify('exit', ['']))
 | 
			
		||||
        self.assertEqual(self.vty.node(), 'config')
 | 
			
		||||
        self.assertTrue(self.vty.verify('gbproxy', ['']))
 | 
			
		||||
        self.assertEqual(self.vty.node(), 'config-gbproxy')
 | 
			
		||||
        self.checkForEndAndExit()
 | 
			
		||||
        self.assertTrue(self.vty.verify('exit', ['']))
 | 
			
		||||
        self.assertEqual(self.vty.node(), 'config')
 | 
			
		||||
 | 
			
		||||
    def testVtyShow(self):
 | 
			
		||||
        res = self.vty.command("show ns")
 | 
			
		||||
        self.assertTrue(res.find('Encapsulation NS-UDP-IP') >= 0)
 | 
			
		||||
 | 
			
		||||
        res = self.vty.command("show gbproxy stats")
 | 
			
		||||
        self.assertTrue(res.find('GBProxy Global Statistics') >= 0)
 | 
			
		||||
 | 
			
		||||
    def testVtyDeletePeer(self):
 | 
			
		||||
        self.vty.enable()
 | 
			
		||||
        self.assertTrue(self.vty.verify('delete-gbproxy-peer 9999 bvci 7777', ['BVC not found']))
 | 
			
		||||
        res = self.vty.command("delete-gbproxy-peer 9999 all dry-run")
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 BVC') >= 0)
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 NS-VC') >= 0)
 | 
			
		||||
        res = self.vty.command("delete-gbproxy-peer 9999 only-bvc dry-run")
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 BVC') >= 0)
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 NS-VC') < 0)
 | 
			
		||||
        res = self.vty.command("delete-gbproxy-peer 9999 only-nsvc dry-run")
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 BVC') < 0)
 | 
			
		||||
        self.assertTrue(res.find('Not Deleted 0 NS-VC') >= 0)
 | 
			
		||||
        res = self.vty.command("delete-gbproxy-peer 9999 all")
 | 
			
		||||
        self.assertTrue(res.find('Deleted 0 BVC') >= 0)
 | 
			
		||||
        self.assertTrue(res.find('Deleted 0 NS-VC') >= 0)
 | 
			
		||||
 | 
			
		||||
class TestVTYSGSN(TestVTYBase):
 | 
			
		||||
 | 
			
		||||
    def vty_command(self):
 | 
			
		||||
@@ -143,7 +94,7 @@ class TestVTYSGSN(TestVTYBase):
 | 
			
		||||
 | 
			
		||||
    def testVtyShow(self):
 | 
			
		||||
        res = self.vty.command("show ns")
 | 
			
		||||
        self.assertTrue(res.find('Encapsulation NS-UDP-IP') >= 0)
 | 
			
		||||
        self.assertTrue(res.find('0 NS-VC:') >= 0)
 | 
			
		||||
        self.assertTrue(self.vty.verify('show bssgp', ['']))
 | 
			
		||||
        self.assertTrue(self.vty.verify('show bssgp stats', ['']))
 | 
			
		||||
        self.assertTrue(self.vty.verify('show bssgp nsei 123', ['']))
 | 
			
		||||
@@ -276,13 +227,6 @@ class TestVTYSGSN(TestVTYBase):
 | 
			
		||||
        for t in [3312, 3322, 3350, 3360, 3370, 3313, 3314, 3316, 3385, 3395, 3397]:
 | 
			
		||||
            self.assertTrue(self.vty.verify('timer t%d 10' % t, ['']))
 | 
			
		||||
 | 
			
		||||
def add_gbproxy_test(suite, workdir):
 | 
			
		||||
    if not os.path.isfile(os.path.join(workdir, "src/gbproxy/osmo-gbproxy")):
 | 
			
		||||
        print("Skipping the Gb-Proxy test")
 | 
			
		||||
        return
 | 
			
		||||
    test = unittest.TestLoader().loadTestsFromTestCase(TestVTYGbproxy)
 | 
			
		||||
    suite.addTest(test)
 | 
			
		||||
 | 
			
		||||
def add_sgsn_test(suite, workdir):
 | 
			
		||||
    if not os.path.isfile(os.path.join(workdir, "src/sgsn/osmo-sgsn")):
 | 
			
		||||
        print("Skipping the SGSN test")
 | 
			
		||||
@@ -320,7 +264,6 @@ if __name__ == '__main__':
 | 
			
		||||
    os.chdir(workdir)
 | 
			
		||||
    print("Running tests for specific VTY commands")
 | 
			
		||||
    suite = unittest.TestSuite()
 | 
			
		||||
    add_gbproxy_test(suite, workdir)
 | 
			
		||||
    add_sgsn_test(suite, workdir)
 | 
			
		||||
 | 
			
		||||
    if args.test_name:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user