mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-11-03 21:53:30 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			1.6.2
			...
			osmith/ski
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					80dcbcdeca | ||
| 
						 | 
					c1d2f10cf6 | 
							
								
								
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -26,7 +26,6 @@ m4
 | 
				
			|||||||
*.m4
 | 
					*.m4
 | 
				
			||||||
missing
 | 
					missing
 | 
				
			||||||
.deps
 | 
					.deps
 | 
				
			||||||
*~
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*.pc
 | 
					*.pc
 | 
				
			||||||
.libs
 | 
					.libs
 | 
				
			||||||
@@ -68,21 +67,3 @@ doc/manuals/generated/
 | 
				
			|||||||
doc/manuals/osmomsc-usermanual.xml
 | 
					doc/manuals/osmomsc-usermanual.xml
 | 
				
			||||||
doc/manuals/common
 | 
					doc/manuals/common
 | 
				
			||||||
doc/manuals/build
 | 
					doc/manuals/build
 | 
				
			||||||
 | 
					 | 
				
			||||||
contrib/osmo-hlr.spec
 | 
					 | 
				
			||||||
/debian/.debhelper/
 | 
					 | 
				
			||||||
/debian/libosmo-gsup-client-dev/
 | 
					 | 
				
			||||||
/debian/files
 | 
					 | 
				
			||||||
/debian/autoreconf.after
 | 
					 | 
				
			||||||
/debian/autoreconf.before
 | 
					 | 
				
			||||||
/debian/libosmo-gsup-client0/
 | 
					 | 
				
			||||||
/debian/libosmo-mslookup0/
 | 
					 | 
				
			||||||
/debian/osmo-hlr-dbg/
 | 
					 | 
				
			||||||
/debian/tmp/
 | 
					 | 
				
			||||||
/doc/manuals/vty/hlr_vty_reference.xml
 | 
					 | 
				
			||||||
/debian/libosmo-mslookup-dev/
 | 
					 | 
				
			||||||
/debian/osmo-hlr-doc/
 | 
					 | 
				
			||||||
/debian/osmo-hlr/
 | 
					 | 
				
			||||||
/debian/osmo-mslookup-utils/
 | 
					 | 
				
			||||||
/debian/*.log
 | 
					 | 
				
			||||||
/debian/*.substvars
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
AUTOMAKE_OPTIONS = foreign dist-bzip2
 | 
					AUTOMAKE_OPTIONS = foreign dist-bzip2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SUBDIRS = \
 | 
					SUBDIRS = \
 | 
				
			||||||
 | 
						doc \
 | 
				
			||||||
	src \
 | 
						src \
 | 
				
			||||||
	include \
 | 
						include \
 | 
				
			||||||
	doc \
 | 
					 | 
				
			||||||
	sql \
 | 
						sql \
 | 
				
			||||||
	contrib \
 | 
						contrib \
 | 
				
			||||||
	tests \
 | 
						tests \
 | 
				
			||||||
@@ -11,8 +11,6 @@ SUBDIRS = \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
	.version \
 | 
						.version \
 | 
				
			||||||
	contrib/osmo-hlr.spec.in \
 | 
					 | 
				
			||||||
	debian \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
					AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										65
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								README.md
									
									
									
									
									
								
							@@ -1,65 +0,0 @@
 | 
				
			|||||||
osmo-hlr - Osmocom HLR Implementation
 | 
					 | 
				
			||||||
=====================================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This repository contains a C-language implementation of a GSM Home
 | 
					 | 
				
			||||||
Location Register (HLR). It is part of the
 | 
					 | 
				
			||||||
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
 | 
					 | 
				
			||||||
project.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Warning: While the HLR logical functionality is implemented, OsmoHLR
 | 
					 | 
				
			||||||
does not use the ETSI/3GPP TCAP/MAP protocol stack. Instead, a much
 | 
					 | 
				
			||||||
simpler custom protocol (GSUP) is used.  This means, OsmoHLR is of
 | 
					 | 
				
			||||||
no use outside the context of an Osmocom core network.  You can use
 | 
					 | 
				
			||||||
it with OsmoMSC, OsmoSGSN etc. - but not with third party components.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Homepage
 | 
					 | 
				
			||||||
--------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The official homepage of the project is
 | 
					 | 
				
			||||||
https://osmocom.org/projects/osmo-hlr/wiki
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GIT Repository
 | 
					 | 
				
			||||||
--------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You can clone from the official osmo-hlr.git repository using
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Documentation
 | 
					 | 
				
			||||||
-------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
 | 
					 | 
				
			||||||
as part of the build process.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Pre-rendered PDF version of the current "master" can be found at
 | 
					 | 
				
			||||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf)
 | 
					 | 
				
			||||||
as well as the VTY reference manuals
 | 
					 | 
				
			||||||
* [VTY Reference Manual for osmo-hlr](https://ftp.osmocom.org/docs/latest/osmohlr-vty-reference.pdf)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Mailing List
 | 
					 | 
				
			||||||
------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Discussions related to osmo-hlr are happening on the
 | 
					 | 
				
			||||||
openbsc@lists.osmocom.org mailing list, please see
 | 
					 | 
				
			||||||
https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
 | 
					 | 
				
			||||||
options and the list archive.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Please observe the [Osmocom Mailing List
 | 
					 | 
				
			||||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
					 | 
				
			||||||
when posting.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Contributing
 | 
					 | 
				
			||||||
------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Our coding standards are described at
 | 
					 | 
				
			||||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
We us a gerrit based patch submission/review process for managing
 | 
					 | 
				
			||||||
contributions.  Please see
 | 
					 | 
				
			||||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
 | 
					 | 
				
			||||||
more details
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The current patch queue for osmo-hlr can be seen at
 | 
					 | 
				
			||||||
https://gerrit.osmocom.org/#/q/project:osmo-hlr+status:open
 | 
					 | 
				
			||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
					 | 
				
			||||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
					 | 
				
			||||||
# In short:
 | 
					 | 
				
			||||||
# LIBVERSION=c:r:a
 | 
					 | 
				
			||||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
 | 
					 | 
				
			||||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
 | 
					 | 
				
			||||||
# If any interfaces have been added since the last public release: c:r:a + 1.
 | 
					 | 
				
			||||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
 | 
					 | 
				
			||||||
#library        what            description / commit summary line
 | 
					 | 
				
			||||||
							
								
								
									
										32
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								configure.ac
									
									
									
									
									
								
							@@ -12,8 +12,6 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
AC_CONFIG_TESTDIR(tests)
 | 
					AC_CONFIG_TESTDIR(tests)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CFLAGS="$CFLAGS -std=gnu11"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dnl kernel style compile messages
 | 
					dnl kernel style compile messages
 | 
				
			||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
					m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,11 +25,6 @@ AC_PROG_MKDIR_P
 | 
				
			|||||||
AC_PROG_CC
 | 
					AC_PROG_CC
 | 
				
			||||||
AC_PROG_INSTALL
 | 
					AC_PROG_INSTALL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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)
 | 
					dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
				
			||||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
					AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
				
			||||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
					if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
				
			||||||
@@ -41,11 +34,11 @@ PKG_PROG_PKG_CONFIG([0.20])
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
 | 
					PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.3.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.4.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
 | 
					PKG_CHECK_MODULES(SQLITE3, sqlite3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,7 +86,6 @@ AC_ARG_ENABLE(werror,
 | 
				
			|||||||
if test x"$werror" = x"yes"
 | 
					if test x"$werror" = x"yes"
 | 
				
			||||||
then
 | 
					then
 | 
				
			||||||
	WERROR_FLAGS="-Werror"
 | 
						WERROR_FLAGS="-Werror"
 | 
				
			||||||
	WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
 | 
					 | 
				
			||||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
						WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
				
			||||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
						WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
				
			||||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
						CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
				
			||||||
@@ -108,22 +100,13 @@ if test "x$enable_ext_tests" = "xyes" ; then
 | 
				
			|||||||
	AM_PATH_PYTHON
 | 
						AM_PATH_PYTHON
 | 
				
			||||||
	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
						AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
				
			||||||
	 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
 | 
						 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
 | 
				
			||||||
		AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
 | 
							AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
					AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
				
			||||||
AC_MSG_RESULT([$enable_ext_tests])
 | 
					AC_MSG_RESULT([$enable_ext_tests])
 | 
				
			||||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
 | 
					AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# mslookup_client_mdns_test (OS#4385: does not work everywhere)
 | 
					 | 
				
			||||||
AC_ARG_ENABLE([mslookup_client_mdns_test],
 | 
					 | 
				
			||||||
		AC_HELP_STRING([--enable-mslookup-client-mdns-test],
 | 
					 | 
				
			||||||
				[Include the mslookup_client_mdns_test in make check [default=no]]),
 | 
					 | 
				
			||||||
		[enable_mslookup_client_mdns_test="$enableval"],[enable_mslookup_client_mdns_test="no"])
 | 
					 | 
				
			||||||
AC_MSG_CHECKING([whether to enable mslookup_client_mdns_test])
 | 
					 | 
				
			||||||
AC_MSG_RESULT([$enable_mslookup_client_mdns_test])
 | 
					 | 
				
			||||||
AM_CONDITIONAL(ENABLE_MSLOOKUP_CLIENT_MDNS_TEST, test "x$enable_mslookup_client_mdns_test" = "xyes")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Generate manuals
 | 
					# Generate manuals
 | 
				
			||||||
AC_ARG_ENABLE(manuals,
 | 
					AC_ARG_ENABLE(manuals,
 | 
				
			||||||
	[AS_HELP_STRING(
 | 
						[AS_HELP_STRING(
 | 
				
			||||||
@@ -203,11 +186,10 @@ AC_OUTPUT(
 | 
				
			|||||||
	contrib/Makefile
 | 
						contrib/Makefile
 | 
				
			||||||
	contrib/systemd/Makefile
 | 
						contrib/systemd/Makefile
 | 
				
			||||||
	contrib/dgsm/Makefile
 | 
						contrib/dgsm/Makefile
 | 
				
			||||||
	contrib/osmo-hlr.spec
 | 
					 | 
				
			||||||
	tests/Makefile
 | 
						tests/Makefile
 | 
				
			||||||
	tests/auc/Makefile
 | 
						tests/auc/Makefile
 | 
				
			||||||
	tests/auc/gen_ts_55_205_test_sets/Makefile
 | 
						tests/auc/gen_ts_55_205_test_sets/Makefile
 | 
				
			||||||
	tests/gsup/Makefile
 | 
						tests/gsup_server/Makefile
 | 
				
			||||||
	tests/db/Makefile
 | 
						tests/db/Makefile
 | 
				
			||||||
	tests/db_upgrade/Makefile
 | 
						tests/db_upgrade/Makefile
 | 
				
			||||||
	tests/mslookup/Makefile
 | 
						tests/mslookup/Makefile
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,3 @@ SUBDIRS = \
 | 
				
			|||||||
	systemd \
 | 
						systemd \
 | 
				
			||||||
	dgsm \
 | 
						dgsm \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					 | 
				
			||||||
EXTRA_DIST = osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
install-data-hook:
 | 
					 | 
				
			||||||
	install -Dm755 $(srcdir)/osmo-hlr-post-upgrade.sh \
 | 
					 | 
				
			||||||
		-t $(DESTDIR)$(datadir)/osmocom/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uninstall-hook:
 | 
					 | 
				
			||||||
	@$(PRE_UNINSTALL)
 | 
					 | 
				
			||||||
	$(RM) $(DESTDIR)$(datadir)/osmocom/osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,4 @@
 | 
				
			|||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
	esme_dgsm.py \
 | 
					 | 
				
			||||||
	freeswitch_dialplan_dgsm.py \
 | 
					 | 
				
			||||||
	osmo-mslookup-pipe.py \
 | 
						osmo-mslookup-pipe.py \
 | 
				
			||||||
	osmo-mslookup-socket.py \
 | 
						osmo-mslookup-socket.py \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,184 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
SPDX-License-Identifier: MIT
 | 
					 | 
				
			||||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WARNING: this is just a proof-of-concept implementation, it blocks for every
 | 
					 | 
				
			||||||
received SMPP request and is not suitable for servicing more than one request
 | 
					 | 
				
			||||||
at a time.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Based on esme.py from RCCN (license changed with permission from author):
 | 
					 | 
				
			||||||
https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
 | 
					 | 
				
			||||||
Copyright 2017 keith <keith@rhizomatica.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Forward SMS to the receiver's SMSC, as determined with mslookup.
 | 
					 | 
				
			||||||
Requires smpplip (pip3 install --user smpplib) and osmo-mslookup-client.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example SMPP configuration for osmo-msc.cfg:
 | 
					 | 
				
			||||||
smpp
 | 
					 | 
				
			||||||
 local-tcp-ip 127.0.0.1 2775
 | 
					 | 
				
			||||||
 policy closed
 | 
					 | 
				
			||||||
 smpp-first
 | 
					 | 
				
			||||||
# outgoing to esme_dgsm.py
 | 
					 | 
				
			||||||
 esme OSMPP
 | 
					 | 
				
			||||||
  no alert-notifications
 | 
					 | 
				
			||||||
  password foo
 | 
					 | 
				
			||||||
  default-route
 | 
					 | 
				
			||||||
# incoming from esme_dgsm.py
 | 
					 | 
				
			||||||
 esme ISMPP
 | 
					 | 
				
			||||||
  no alert-notifications
 | 
					 | 
				
			||||||
  password foo
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
import argparse
 | 
					 | 
				
			||||||
import json
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import smpplib
 | 
					 | 
				
			||||||
import subprocess
 | 
					 | 
				
			||||||
import time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def can_handle_pdu(pdu):
 | 
					 | 
				
			||||||
    if not isinstance(pdu, smpplib.command.DeliverSM):
 | 
					 | 
				
			||||||
        logging.info('PDU is not a DeliverSM, ignoring')
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
 | 
					 | 
				
			||||||
        logging.info("Unable to handle SMS for %s: SMPP_TON_INTL" %
 | 
					 | 
				
			||||||
                     (pdu.destination_addr))
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def query_mslookup(service_type, id, id_type='msisdn'):
 | 
					 | 
				
			||||||
    query_str = '%s.%s.%s' % (service_type, id, id_type)
 | 
					 | 
				
			||||||
    logging.info('mslookup: ' + query_str)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result_line = subprocess.check_output(['osmo-mslookup-client', query_str,
 | 
					 | 
				
			||||||
                                           '-f', 'json'])
 | 
					 | 
				
			||||||
    if isinstance(result_line, bytes):
 | 
					 | 
				
			||||||
        result_line = result_line.decode('ascii')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    logging.info('mslookup result: ' + result_line.rstrip())
 | 
					 | 
				
			||||||
    return json.loads(result_line)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
 | 
					 | 
				
			||||||
           unicode_text):
 | 
					 | 
				
			||||||
    smpp_client = smpplib.client.Client(dst_host, dst_port, 90)
 | 
					 | 
				
			||||||
    smpp_client.connect()
 | 
					 | 
				
			||||||
    smpp_client.bind_transceiver(system_id=args.dst_id, password=args.dst_pass)
 | 
					 | 
				
			||||||
    logging.info('Connected to destination SMSC (%s@%s:%s)' % (args.dst_id,
 | 
					 | 
				
			||||||
                 dst_host, dst_port))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pdu = smpp_client.send_message(
 | 
					 | 
				
			||||||
        source_addr_ton=smpplib.consts.SMPP_TON_ALNUM,
 | 
					 | 
				
			||||||
        source_addr_npi=smpplib.consts.SMPP_NPI_UNK,
 | 
					 | 
				
			||||||
        source_addr=source.decode(),
 | 
					 | 
				
			||||||
        dest_addr_ton=smpplib.consts.SMPP_TON_SBSCR,
 | 
					 | 
				
			||||||
        dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
 | 
					 | 
				
			||||||
        destination_addr=destination.decode(),
 | 
					 | 
				
			||||||
        short_message=unicode_text,
 | 
					 | 
				
			||||||
        registered_delivery=registered_delivery,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    smpp_client.unbind()
 | 
					 | 
				
			||||||
    smpp_client.disconnect()
 | 
					 | 
				
			||||||
    del pdu
 | 
					 | 
				
			||||||
    del smpp_client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def rx_deliver_sm(pdu):
 | 
					 | 
				
			||||||
    if not can_handle_pdu(pdu):
 | 
					 | 
				
			||||||
        return smpplib.consts.SMPP_ESME_RSYSERR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msisdn = pdu.destination_addr.decode()
 | 
					 | 
				
			||||||
    logging.info("Incoming SMS for: " + msisdn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.sleep:
 | 
					 | 
				
			||||||
        logging.info("Sleeping for %i seconds" % (args.sleep))
 | 
					 | 
				
			||||||
        time.sleep(args.sleep)
 | 
					 | 
				
			||||||
        logging.info("Sleep done")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.always_fail is not None:
 | 
					 | 
				
			||||||
        return args.always_fail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = query_mslookup("smpp.sms", msisdn)
 | 
					 | 
				
			||||||
    if 'v4' not in result or not result['v4']:
 | 
					 | 
				
			||||||
        logging.info('No IPv4 result from mslookup! This example only'
 | 
					 | 
				
			||||||
                     ' makes use of IPv4, dropping.')
 | 
					 | 
				
			||||||
        return smpplib.consts.SMPP_ESME_RSYSERR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dst_host, dst_port = result['v4']
 | 
					 | 
				
			||||||
    tx_sms(dst_host, dst_port, pdu.source_addr,
 | 
					 | 
				
			||||||
           pdu.destination_addr, int(pdu.registered_delivery),
 | 
					 | 
				
			||||||
           pdu.short_message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return smpplib.consts.SMPP_ESME_ROK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def smpp_bind():
 | 
					 | 
				
			||||||
    client = smpplib.client.Client(args.src_host, args.src_port, 90)
 | 
					 | 
				
			||||||
    client.set_message_received_handler(rx_deliver_sm)
 | 
					 | 
				
			||||||
    client.connect()
 | 
					 | 
				
			||||||
    client.bind_transceiver(system_id=args.src_id, password=args.src_pass)
 | 
					 | 
				
			||||||
    logging.info('Connected to source SMSC (%s@%s:%s)' % (args.src_id,
 | 
					 | 
				
			||||||
                 args.src_host, args.src_port))
 | 
					 | 
				
			||||||
    logging.info('Waiting for SMS...')
 | 
					 | 
				
			||||||
    client.listen()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
    global args
 | 
					 | 
				
			||||||
    parser = argparse.ArgumentParser()
 | 
					 | 
				
			||||||
    parser.add_argument('--src-host', default='127.0.0.1',
 | 
					 | 
				
			||||||
                        help='source SMSC (OsmoMSC) host (default: 127.0.0.1)')
 | 
					 | 
				
			||||||
    parser.add_argument('--src-port', default=2775, type=int,
 | 
					 | 
				
			||||||
                        help='source SMSC (OsmoMSC) port (default: 2775)')
 | 
					 | 
				
			||||||
    parser.add_argument('--src-id', default='OSMPP',
 | 
					 | 
				
			||||||
                        help='source system id, as configured in osmo-msc.cfg'
 | 
					 | 
				
			||||||
                             ' (default: OSMPP)')
 | 
					 | 
				
			||||||
    parser.add_argument('--src-pass', default='foo',
 | 
					 | 
				
			||||||
                        help='source system password, as configured in'
 | 
					 | 
				
			||||||
                             ' osmo-msc.cfg (default: foo)')
 | 
					 | 
				
			||||||
    parser.add_argument('--dst-id', default='ISMPP',
 | 
					 | 
				
			||||||
                        help='destination system id, as configured in'
 | 
					 | 
				
			||||||
                             ' osmo-msc.cfg (default: ISMPP)')
 | 
					 | 
				
			||||||
    parser.add_argument('--dst-pass', default='foo',
 | 
					 | 
				
			||||||
                        help='destination system password, as configured in'
 | 
					 | 
				
			||||||
                             ' osmo-msc.cfg (default: foo)')
 | 
					 | 
				
			||||||
    parser.add_argument('--sleep', default=0, type=float,
 | 
					 | 
				
			||||||
                        help='sleep time in seconds before forwarding an SMS,'
 | 
					 | 
				
			||||||
                             ' to test multithreading (default: 0)')
 | 
					 | 
				
			||||||
    parser.add_argument('--always-fail', default=None, metavar='SMPP_ESME_ERRCODE',
 | 
					 | 
				
			||||||
                        help='test delivery failure: always return an error code on Deliver-SM,'
 | 
					 | 
				
			||||||
                        ' pass an smpplib error code name like RDELIVERYFAILURE (see smpplib/consts.py),'
 | 
					 | 
				
			||||||
                        ' or an SMPP error code in hex digits')
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    logging.basicConfig(level=logging.INFO, format='[%(asctime)s]'
 | 
					 | 
				
			||||||
                        ' (%(threadName)s) %(message)s', datefmt="%H:%M:%S")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.always_fail:
 | 
					 | 
				
			||||||
        resolved = None
 | 
					 | 
				
			||||||
        name = 'SMPP_ESME_' + args.always_fail
 | 
					 | 
				
			||||||
        if hasattr(smpplib.consts, name):
 | 
					 | 
				
			||||||
            resolved = getattr(smpplib.consts, name)
 | 
					 | 
				
			||||||
        if resolved is None:
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                resolved = int(args.always_fail, 16)
 | 
					 | 
				
			||||||
            except ValueError:
 | 
					 | 
				
			||||||
                resolved = None
 | 
					 | 
				
			||||||
        if resolved is None:
 | 
					 | 
				
			||||||
            print('Invalid argument for --always-fail: %r' % args.always_fail)
 | 
					 | 
				
			||||||
            exit(1)
 | 
					 | 
				
			||||||
        args.always_fail = resolved
 | 
					 | 
				
			||||||
        logging.info('--always-fail: returning error code %s to all Deliver-SM' % hex(args.always_fail))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    smpp_bind()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    main()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# vim: expandtab tabstop=4 shiftwidth=4
 | 
					 | 
				
			||||||
@@ -1,77 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
SPDX-License-Identifier: MIT
 | 
					 | 
				
			||||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is a freeswitch dialplan implementation, see:
 | 
					 | 
				
			||||||
https://freeswitch.org/confluence/display/FREESWITCH/mod_python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Find the right SIP server with mslookup (depending on the destination number)
 | 
					 | 
				
			||||||
and bridge calls accordingly.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
import json
 | 
					 | 
				
			||||||
import subprocess
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def query_mslookup(service_type, id, id_type='msisdn'):
 | 
					 | 
				
			||||||
    query_str = '%s.%s.%s' % (service_type, id, id_type)
 | 
					 | 
				
			||||||
    print('[dialplan-dgsm] mslookup: ' + query_str)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result_line = subprocess.check_output([
 | 
					 | 
				
			||||||
        'osmo-mslookup-client', query_str, '-f', 'json'])
 | 
					 | 
				
			||||||
    if isinstance(result_line, bytes):
 | 
					 | 
				
			||||||
        result_line = result_line.decode('ascii')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    print('[dialplan-dgsm] mslookup result: ' + result_line)
 | 
					 | 
				
			||||||
    return json.loads(result_line)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def handler(session, args):
 | 
					 | 
				
			||||||
    """ Handle calls: bridge to the SIP server found with mslookup. """
 | 
					 | 
				
			||||||
    print('[dialplan-dgsm] call handler')
 | 
					 | 
				
			||||||
    msisdn = session.getVariable('destination_number')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Run osmo-mslookup-client binary. We have also tried to directly call the
 | 
					 | 
				
			||||||
    # C functions with ctypes but this has lead to hard-to-debug segfaults.
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        result = query_mslookup("sip.voice", msisdn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # This example only makes use of IPv4
 | 
					 | 
				
			||||||
        if not result['v4']:
 | 
					 | 
				
			||||||
            print('[dialplan-dgsm] no IPv4 result from mslookup')
 | 
					 | 
				
			||||||
            session.hangup('UNALLOCATED_NUMBER')
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sip_ip, sip_port = result['v4']
 | 
					 | 
				
			||||||
        dial_str = 'sofia/internal/sip:{}@{}:{}'.format(
 | 
					 | 
				
			||||||
            msisdn, sip_ip, sip_port)
 | 
					 | 
				
			||||||
        print('[dialplan-dgsm] dial_str: ' + str(dial_str))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session.execute('bridge', dial_str)
 | 
					 | 
				
			||||||
    except:
 | 
					 | 
				
			||||||
        print('[dialplan-dgsm]: exception during call handler')
 | 
					 | 
				
			||||||
        session.hangup('UNALLOCATED_NUMBER')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def fsapi(session, stream, env, args):
 | 
					 | 
				
			||||||
    """ Freeswitch refuses to load the module without this. """
 | 
					 | 
				
			||||||
    stream.write(env.serialize())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
    import argparse
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser = argparse.ArgumentParser()
 | 
					 | 
				
			||||||
    parser.add_argument('id', type=int)
 | 
					 | 
				
			||||||
    parser.add_argument('-i', '--id-type', default='msisdn',
 | 
					 | 
				
			||||||
                        help='default: "msisdn"')
 | 
					 | 
				
			||||||
    parser.add_argument('-s', '--service', default='sip.voice',
 | 
					 | 
				
			||||||
                        help='default: "sip.voice"')
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = query_mslookup(args.service, args.id, args.id_type)
 | 
					 | 
				
			||||||
    print(json.dumps(result))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    main()
 | 
					 | 
				
			||||||
@@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-abis
 | 
				
			|||||||
# Additional configure options and depends
 | 
					# Additional configure options and depends
 | 
				
			||||||
CONFIG=""
 | 
					CONFIG=""
 | 
				
			||||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
					if [ "$WITH_MANUALS" = "1" ]; then
 | 
				
			||||||
 | 
						osmo-build-dep.sh osmo-gsm-manuals
 | 
				
			||||||
	CONFIG="--enable-manuals"
 | 
						CONFIG="--enable-manuals"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,19 +49,14 @@ set -x
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
cd "$base"
 | 
					cd "$base"
 | 
				
			||||||
autoreconf --install --force
 | 
					autoreconf --install --force
 | 
				
			||||||
./configure \
 | 
					./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
 | 
				
			||||||
	--enable-sanitize \
 | 
					 | 
				
			||||||
	--enable-external-tests \
 | 
					 | 
				
			||||||
	--enable-mslookup-client-mdns-test \
 | 
					 | 
				
			||||||
	--enable-werror \
 | 
					 | 
				
			||||||
	$CONFIG
 | 
					 | 
				
			||||||
$MAKE $PARALLEL_MAKE
 | 
					$MAKE $PARALLEL_MAKE
 | 
				
			||||||
$MAKE check || cat-testlogs.sh
 | 
					$MAKE check || cat-testlogs.sh
 | 
				
			||||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck || cat-testlogs.sh
 | 
					DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
					if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
				
			||||||
	make -C "$base/doc/manuals" publish
 | 
						make -C "$base/doc/manuals" publish
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
					$MAKE maintainer-clean
 | 
				
			||||||
osmo-clean-workspace.sh
 | 
					osmo-clean-workspace.sh
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,95 +0,0 @@
 | 
				
			|||||||
#!/bin/sh -e
 | 
					 | 
				
			||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
 | 
					 | 
				
			||||||
# Copyright 2021 sysmocom s.f.m.c GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Packagers are supposed to call this script in post-upgrade, so it can safely
 | 
					 | 
				
			||||||
# upgrade the database scheme if required.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DB="/var/lib/osmocom/hlr.db"
 | 
					 | 
				
			||||||
IS_ACTIVE=0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
msg() {
 | 
					 | 
				
			||||||
	echo "osmo-hlr-post-upgrade: $@"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
err() {
 | 
					 | 
				
			||||||
	msg "ERROR: $@"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
open_db() {
 | 
					 | 
				
			||||||
	# Attempt to open the database with osmo-hlr-db-tool, it will fail if
 | 
					 | 
				
			||||||
	# upgrading the schema is required
 | 
					 | 
				
			||||||
	osmo-hlr-db-tool -s -l "$DB" create
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
check_upgrade_required() {
 | 
					 | 
				
			||||||
	if ! [ -e "$DB" ]; then
 | 
					 | 
				
			||||||
		msg "nothing to do (no existing database)"
 | 
					 | 
				
			||||||
		exit 0
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if open_db 2>/dev/null; then
 | 
					 | 
				
			||||||
		msg "nothing to do (database version is up to date)"
 | 
					 | 
				
			||||||
		exit 0
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg "database upgrade is required"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
stop_service() {
 | 
					 | 
				
			||||||
	if systemctl is-active -q osmo-hlr; then
 | 
					 | 
				
			||||||
		IS_ACTIVE=1
 | 
					 | 
				
			||||||
		msg "stopping osmo-hlr service"
 | 
					 | 
				
			||||||
		systemctl stop osmo-hlr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		# Verify that it stopped
 | 
					 | 
				
			||||||
		for i in $(seq 1 100); do
 | 
					 | 
				
			||||||
			if ! systemctl is-active -q osmo-hlr; then
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			fi
 | 
					 | 
				
			||||||
			sleep 0.1
 | 
					 | 
				
			||||||
		done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err "failed to stop osmo-hlr service"
 | 
					 | 
				
			||||||
		exit 1
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		msg "osmo-hlr service is not running"
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
create_backup() {
 | 
					 | 
				
			||||||
	backup="$DB.$(date +%Y%m%d%H%M%S).bak"
 | 
					 | 
				
			||||||
	msg "creating backup: $backup"
 | 
					 | 
				
			||||||
	if [ -e "$backup" ]; then
 | 
					 | 
				
			||||||
		err "backup already exists: $backup"
 | 
					 | 
				
			||||||
		exit 1
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
	cp "$DB" "$backup"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
upgrade() {
 | 
					 | 
				
			||||||
	msg "performing database upgrade"
 | 
					 | 
				
			||||||
	osmo-hlr-db-tool -s -U -l "$DB" create
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ! open_db 2>/dev/null; then
 | 
					 | 
				
			||||||
		err "failed to open the database after upgrade"
 | 
					 | 
				
			||||||
		err "osmo-hlr-db-tool output:"
 | 
					 | 
				
			||||||
		open_db
 | 
					 | 
				
			||||||
		# exit because of "set -e"
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg "database upgrade successful"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
start_service() {
 | 
					 | 
				
			||||||
	if [ "$IS_ACTIVE" = "1" ]; then
 | 
					 | 
				
			||||||
		msg "starting osmo-hlr service"
 | 
					 | 
				
			||||||
		systemctl start osmo-hlr
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
check_upgrade_required
 | 
					 | 
				
			||||||
stop_service
 | 
					 | 
				
			||||||
create_backup
 | 
					 | 
				
			||||||
upgrade
 | 
					 | 
				
			||||||
start_service
 | 
					 | 
				
			||||||
@@ -1,195 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# spec file for package osmo-hlr
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 | 
					 | 
				
			||||||
# Copyright (c) 2016, 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Name:           osmo-hlr
 | 
					 | 
				
			||||||
Version:        @VERSION@
 | 
					 | 
				
			||||||
Release:        0
 | 
					 | 
				
			||||||
Summary:        Osmocom Home Location Register for GSUP protocol towards OsmoSGSN and OsmoCSCN
 | 
					 | 
				
			||||||
License:        AGPL-3.0-or-later AND GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          Productivity/Telephony/Servers
 | 
					 | 
				
			||||||
URL:            https://osmocom.org/projects/osmo-hlr
 | 
					 | 
				
			||||||
Source:         %{name}-%{version}.tar.xz
 | 
					 | 
				
			||||||
BuildRequires:  autoconf
 | 
					 | 
				
			||||||
BuildRequires:  automake
 | 
					 | 
				
			||||||
BuildRequires:  libtool
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig >= 0.20
 | 
					 | 
				
			||||||
BuildRequires:  python3
 | 
					 | 
				
			||||||
%if 0%{?suse_version}
 | 
					 | 
				
			||||||
BuildRequires:  systemd-rpm-macros
 | 
					 | 
				
			||||||
%endif
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.4.0
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(libosmocore) >= 1.8.0
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.8.0
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.8.0
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(libosmovty) >= 1.8.0
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(sqlite3)
 | 
					 | 
				
			||||||
BuildRequires:  pkgconfig(talloc) >= 2.0.1
 | 
					 | 
				
			||||||
# only needed for populate_hlr_db.pl
 | 
					 | 
				
			||||||
Requires:       libdbi-drivers-dbd-sqlite3
 | 
					 | 
				
			||||||
%{?systemd_requires}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description
 | 
					 | 
				
			||||||
The GSUP HLR is a stand-alone HLR (Home Location Register) for SIM
 | 
					 | 
				
			||||||
and USIM based subscribers which exposes the GSUP protocol towards
 | 
					 | 
				
			||||||
its users. OsmoSGSN supports this protocol.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-gsup-hlr is still very simplistic. It is a single-threaded
 | 
					 | 
				
			||||||
architecture and uses only sqlite3 tables as back-end.  It is suitable
 | 
					 | 
				
			||||||
for installations of the scale that OsmoNITB was able to handle.  It
 | 
					 | 
				
			||||||
also lacks various features like fine-grained control of subscribed
 | 
					 | 
				
			||||||
services (like supplementary services).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%package -n libosmo-gsup-client0
 | 
					 | 
				
			||||||
Summary:        Osmocom GSUP (General Subscriber Update Protocol) client library
 | 
					 | 
				
			||||||
License:        GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          System/Libraries
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description -n libosmo-gsup-client0
 | 
					 | 
				
			||||||
This is a shared library that can be used to implement client programs for
 | 
					 | 
				
			||||||
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
 | 
					 | 
				
			||||||
and External USSD Entities (EUSEs) using this library to implement clients.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%package -n libosmo-gsup-client-devel
 | 
					 | 
				
			||||||
Summary:        Development files for the Osmocom GSUP client library
 | 
					 | 
				
			||||||
License:        GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          Development/Libraries/C and C++
 | 
					 | 
				
			||||||
Requires:       libosmo-gsup-client0 = %{version}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description -n libosmo-gsup-client-devel
 | 
					 | 
				
			||||||
This is a shared library that can be used to implement client programs for
 | 
					 | 
				
			||||||
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
 | 
					 | 
				
			||||||
and External USSD Entities (EUSEs) using this library to implement clients.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This subpackage contains libraries and header files for developing
 | 
					 | 
				
			||||||
applications that want to make use of libosmo-gsup-client.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%package -n libosmo-mslookup1
 | 
					 | 
				
			||||||
Summary:        Osmocom MS lookup library
 | 
					 | 
				
			||||||
License:        GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          System/Libraries
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description -n libosmo-mslookup1
 | 
					 | 
				
			||||||
This shared library contains routines for looking up mobile subscribers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%package -n libosmo-mslookup-devel
 | 
					 | 
				
			||||||
Summary:        Development files for the Osmocom MS lookup library
 | 
					 | 
				
			||||||
License:        GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          Development/Libraries/C and C++
 | 
					 | 
				
			||||||
Requires:       libosmo-mslookup1 = %{version}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description -n libosmo-mslookup-devel
 | 
					 | 
				
			||||||
This shared library contains routines for looking up mobile subscribers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This subpackage contains libraries and header files for developing
 | 
					 | 
				
			||||||
applications that want to make use of libosmo-mslookup.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%package -n osmo-mslookup-client
 | 
					 | 
				
			||||||
Summary:        Standalone program using libosmo-mslookup
 | 
					 | 
				
			||||||
License:        GPL-2.0-or-later
 | 
					 | 
				
			||||||
Group:          Development/Libraries/C and C++
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%description -n osmo-mslookup-client
 | 
					 | 
				
			||||||
Standalone program using libosmo-mslookup to easily integrate with programs
 | 
					 | 
				
			||||||
that want to connect services (SIP, SMS,...) to the current location of a
 | 
					 | 
				
			||||||
subscriber.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%prep
 | 
					 | 
				
			||||||
%setup -q
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%build
 | 
					 | 
				
			||||||
echo "%{version}" >.tarball-version
 | 
					 | 
				
			||||||
autoreconf -fi
 | 
					 | 
				
			||||||
%configure \
 | 
					 | 
				
			||||||
  --docdir="%{_docdir}/%{name}" \
 | 
					 | 
				
			||||||
  --with-systemdsystemunitdir=%{_unitdir} \
 | 
					 | 
				
			||||||
  --enable-shared \
 | 
					 | 
				
			||||||
  --disable-static
 | 
					 | 
				
			||||||
make V=1 %{?_smp_mflags}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%install
 | 
					 | 
				
			||||||
%make_install
 | 
					 | 
				
			||||||
install -d "%{buildroot}/%{_localstatedir}/lib/osmocom"
 | 
					 | 
				
			||||||
find %{buildroot} -type f -name "*.la" -delete -print
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%check
 | 
					 | 
				
			||||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%if 0%{?suse_version}
 | 
					 | 
				
			||||||
%preun
 | 
					 | 
				
			||||||
%service_del_preun %{name}.service
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%postun
 | 
					 | 
				
			||||||
%service_del_postun %{name}.service
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%pre
 | 
					 | 
				
			||||||
%service_add_pre %{name}.service
 | 
					 | 
				
			||||||
%endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%post
 | 
					 | 
				
			||||||
%if 0%{?suse_version}
 | 
					 | 
				
			||||||
%service_add_post %{name}.service
 | 
					 | 
				
			||||||
%endif
 | 
					 | 
				
			||||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%post   -n libosmo-gsup-client0 -p /sbin/ldconfig
 | 
					 | 
				
			||||||
%postun -n libosmo-gsup-client0 -p /sbin/ldconfig
 | 
					 | 
				
			||||||
%post   -n libosmo-mslookup1 -p /sbin/ldconfig
 | 
					 | 
				
			||||||
%postun -n libosmo-mslookup1 -p /sbin/ldconfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files
 | 
					 | 
				
			||||||
%license COPYING
 | 
					 | 
				
			||||||
%dir %{_docdir}/%{name}
 | 
					 | 
				
			||||||
%dir %{_docdir}/%{name}/examples
 | 
					 | 
				
			||||||
%{_docdir}/%{name}/examples/osmo-hlr.cfg
 | 
					 | 
				
			||||||
%{_docdir}/%{name}/examples/osmo-hlr-dgsm.cfg
 | 
					 | 
				
			||||||
%dir %{_docdir}/%{name}/sql
 | 
					 | 
				
			||||||
%{_docdir}/%{name}/sql/hlr.sql
 | 
					 | 
				
			||||||
%{_docdir}/%{name}/sql//hlr_data.sql
 | 
					 | 
				
			||||||
%dir %{_sysconfdir}/osmocom
 | 
					 | 
				
			||||||
%dir %{_localstatedir}/lib/osmocom
 | 
					 | 
				
			||||||
%{_bindir}/osmo-hlr
 | 
					 | 
				
			||||||
%{_bindir}/osmo-hlr-db-tool
 | 
					 | 
				
			||||||
%dir %{_sysconfdir}/osmocom
 | 
					 | 
				
			||||||
%config %{_sysconfdir}/osmocom/osmo-hlr.cfg
 | 
					 | 
				
			||||||
%{_unitdir}/osmo-hlr.service
 | 
					 | 
				
			||||||
%dir %{_datadir}/osmocom
 | 
					 | 
				
			||||||
%{_datadir}/osmocom/osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files -n libosmo-gsup-client0
 | 
					 | 
				
			||||||
%{_libdir}/libosmo-gsup-client.so.0*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files -n libosmo-gsup-client-devel
 | 
					 | 
				
			||||||
%{_bindir}/osmo-euse-demo
 | 
					 | 
				
			||||||
%dir %{_includedir}/osmocom
 | 
					 | 
				
			||||||
%dir %{_includedir}/osmocom/gsupclient
 | 
					 | 
				
			||||||
%{_includedir}/osmocom/gsupclient/*.h
 | 
					 | 
				
			||||||
%{_libdir}/libosmo-gsup-client.so
 | 
					 | 
				
			||||||
%{_libdir}/pkgconfig/libosmo-gsup-client.pc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files -n libosmo-mslookup1
 | 
					 | 
				
			||||||
%{_libdir}/libosmo-mslookup.so.1*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files -n libosmo-mslookup-devel
 | 
					 | 
				
			||||||
%dir %{_includedir}/osmocom
 | 
					 | 
				
			||||||
%dir %{_includedir}/osmocom/mslookup
 | 
					 | 
				
			||||||
%{_includedir}/osmocom/mslookup/*.h
 | 
					 | 
				
			||||||
%{_libdir}/libosmo-mslookup.so
 | 
					 | 
				
			||||||
%{_libdir}/pkgconfig/libosmo-mslookup.pc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%files -n osmo-mslookup-client
 | 
					 | 
				
			||||||
%{_bindir}/osmo-mslookup-client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%changelog
 | 
					 | 
				
			||||||
@@ -1,17 +1,12 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom Home Location Register (OsmoHLR)
 | 
					Description=Osmocom Home Location Register (OsmoHLR)
 | 
				
			||||||
Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
 | 
					Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
 | 
				
			||||||
After=network-online.target
 | 
					 | 
				
			||||||
Wants=network-online.target
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
StateDirectory=osmocom
 | 
					 | 
				
			||||||
WorkingDirectory=%S/osmocom
 | 
					 | 
				
			||||||
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
					ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
ProtectHome=true
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										201
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,204 +1,3 @@
 | 
				
			|||||||
osmo-hlr (1.6.2) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  * systemd: depend on networking-online.target
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Oliver Smith <osmith@sysmocom.de>  Fri, 26 May 2023 14:54:12 +0200
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.6.1) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  * Run struct_endianness.py
 | 
					 | 
				
			||||||
  * tests: adjust to XOR-3G rename in libosmocore
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Oliver Smith <osmith@sysmocom.de>  Thu, 23 Feb 2023 10:23:04 +0100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.6.0) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Vadim Yanitskiy ]
 | 
					 | 
				
			||||||
  * db_auc: hexparse_stmt(): check value returned by osmo_hexparse()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Max ]
 | 
					 | 
				
			||||||
  * Set working directory in systemd service file
 | 
					 | 
				
			||||||
  * Ignore .deb build byproducts
 | 
					 | 
				
			||||||
  * Debian: bump copyright year to match current
 | 
					 | 
				
			||||||
  * Debian: reformat package description
 | 
					 | 
				
			||||||
  * systemd: enable basic hardening
 | 
					 | 
				
			||||||
  * Debian: install osmo-hlr-dgsm.cfg as example config
 | 
					 | 
				
			||||||
  * hlr_vty.c: fix typo
 | 
					 | 
				
			||||||
  * ctrl: take both address and port from vty config
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Harald Welte ]
 | 
					 | 
				
			||||||
  * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
 | 
					 | 
				
			||||||
  * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ arehbein ]
 | 
					 | 
				
			||||||
  * osmo-hlr: Transition to use of 'telnet_init_default'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Oliver Smith ]
 | 
					 | 
				
			||||||
  * osmo_mdns_rfc_record_decode: check ret of talloc
 | 
					 | 
				
			||||||
  * osmo_mdns_rfc_record_decode: proper free on err
 | 
					 | 
				
			||||||
  * mslookup: use apn functions from libosmocore
 | 
					 | 
				
			||||||
  * osmo_mdns_rfc_record/question_encode: remove ctx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Keith ]
 | 
					 | 
				
			||||||
  * Vty: Fixup config shown/written from vty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Neels Hofmeyr ]
 | 
					 | 
				
			||||||
  * fix memleak of proxy_subscr_listentry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Alexander Couzens ]
 | 
					 | 
				
			||||||
  * Add vty `reject-cause` to set the reject cause
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 07 Feb 2023 16:49:14 +0100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.5.0) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Oliver Smith ]
 | 
					 | 
				
			||||||
  * treewide: remove FSF address
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Vadim Yanitskiy ]
 | 
					 | 
				
			||||||
  * fixup: debian: remove unneeded dependency libdbd-sqlite3
 | 
					 | 
				
			||||||
  * debian: add new 'osmo-mslookup-utils' package
 | 
					 | 
				
			||||||
  * tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Pau Espin Pedrol ]
 | 
					 | 
				
			||||||
  * ctrl: Mark function as static
 | 
					 | 
				
			||||||
  * tests: Allow specyfing specific ctrl test to run
 | 
					 | 
				
			||||||
  * tests/ctrl: Move ERROR test scenario to proper file
 | 
					 | 
				
			||||||
  * Fix db_subscr_create() not returning -EEXIST expected by VTY subscriber create cmd
 | 
					 | 
				
			||||||
  * ctrl: Introduce cmd SET subscriber.create <imsi>
 | 
					 | 
				
			||||||
  * ctrl: Introduce CTRL command subscriber.by-*.msisdn
 | 
					 | 
				
			||||||
  * cosmetic: hlr_vty_subscr.c: Fix trailing whitespace
 | 
					 | 
				
			||||||
  * ctrl: Introduce cmd SET subscriber.delete <imsi>
 | 
					 | 
				
			||||||
  * ctrl: Introduce CTRL command subscriber.by-*.aud2g <algo[,ki]>
 | 
					 | 
				
			||||||
  * ctrl: Introduce CTRL command subscriber.by-*.aud3g <algo[,KI,(op|opc),OP_C[,ind_bitlen]]>
 | 
					 | 
				
			||||||
  * doc: Document new subscriber CTRL commands
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Harald Welte ]
 | 
					 | 
				
			||||||
  * update git URLs (git -> https; gitea)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 28 Jun 2022 18:38:31 +0200
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.4.0) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Keith ]
 | 
					 | 
				
			||||||
  * Correct configuration written from vty
 | 
					 | 
				
			||||||
  * vty: enable show subscribers filtered by IMEI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Harald Welte ]
 | 
					 | 
				
			||||||
  * add README.md file as customary for cgit, github, gitlab, etc.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Oliver Smith ]
 | 
					 | 
				
			||||||
  * Add post-upgrade script for automatic db upgrade
 | 
					 | 
				
			||||||
  * debian/control: remove dh-systemd build-depend
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Pau Espin Pedrol ]
 | 
					 | 
				
			||||||
  * db: Avoid use uninitialized rc if running 0 statements
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Neels Hofmeyr ]
 | 
					 | 
				
			||||||
  * db v6: determine 3G AUC IND from VLR name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 16 Nov 2021 14:56:41 +0100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.3.0) unstable; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Alexander Couzens ]
 | 
					 | 
				
			||||||
  * hlr: respect the num_auth_vectors requested
 | 
					 | 
				
			||||||
  * hlr: remove unused internal USSD list
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Oliver Smith ]
 | 
					 | 
				
			||||||
  * add libosmo-mslookup abstract client
 | 
					 | 
				
			||||||
  * add mDNS lookup method to libosmo-mslookup
 | 
					 | 
				
			||||||
  * Makefile.am: fix pkgconfig_DATA
 | 
					 | 
				
			||||||
  * add mDNS lookup method to libosmo-mslookup (#2)
 | 
					 | 
				
			||||||
  * contrib/dgsm/ add example esme and dialplan
 | 
					 | 
				
			||||||
  * mslookup_client.c: fix dereferencing null pointer
 | 
					 | 
				
			||||||
  * mdns_msg.c: always call va_end
 | 
					 | 
				
			||||||
  * mslookup_client_mdns.c: fix dereferencing null
 | 
					 | 
				
			||||||
  * osmo-mslookup-client.c: fix dereferencing null
 | 
					 | 
				
			||||||
  * osmo-mslookup-client: fix dereferencing null
 | 
					 | 
				
			||||||
  * mdns_sock.c: fix resource leak of sock
 | 
					 | 
				
			||||||
  * mdns_rfc.c: fix possible access of uninit. mem
 | 
					 | 
				
			||||||
  * mslookup_client_mdns_test: disable by default
 | 
					 | 
				
			||||||
  * mslookup_client_mdns_test: no automatic skip
 | 
					 | 
				
			||||||
  * Cosmetic: mention OS#4491 in location cancel code
 | 
					 | 
				
			||||||
  * hlr_vty_subscr: prettier output for last LU seen
 | 
					 | 
				
			||||||
  * contrib: import RPM spec
 | 
					 | 
				
			||||||
  * contrib: integrate RPM spec
 | 
					 | 
				
			||||||
  * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
 | 
					 | 
				
			||||||
  * contrib/jenkins: don't build osmo-gsm-manuals
 | 
					 | 
				
			||||||
  * configure.ac: set -std=gnu11
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Neels Hofmeyr ]
 | 
					 | 
				
			||||||
  * add osmo-mslookup-client program
 | 
					 | 
				
			||||||
  * add osmo-mslookup-client program (#2)
 | 
					 | 
				
			||||||
  * fix missing braces in LOGP_GSUP_FWD
 | 
					 | 
				
			||||||
  * gsup_client.c: fix deprecation for client create func
 | 
					 | 
				
			||||||
  * 1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name
 | 
					 | 
				
			||||||
  * 2/2: wrap ipa_name in osmo_cni_peer_id with type enum and union
 | 
					 | 
				
			||||||
  * gsup client: add up_down_cb(), add osmo_gsup_client_create3()
 | 
					 | 
				
			||||||
  * db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
 | 
					 | 
				
			||||||
  * enlarge the GSUP message headroom
 | 
					 | 
				
			||||||
  * test_nodes.vty: remove cruft
 | 
					 | 
				
			||||||
  * D-GSM 1/n: add mslookup server in osmo-hlr
 | 
					 | 
				
			||||||
  * D-GSM 2/n: implement mDNS method of mslookup server
 | 
					 | 
				
			||||||
  * D-GSM 3/n: implement roaming by mslookup in osmo-hlr
 | 
					 | 
				
			||||||
  * gsup_server: send routing error back to the correct peer
 | 
					 | 
				
			||||||
  * adoc: add D-GSM chapter to osmohlr-usermanual
 | 
					 | 
				
			||||||
  * drop error log for when a subscriber does not exist
 | 
					 | 
				
			||||||
  * vty: show subscriber: change format of 'last LU seen'
 | 
					 | 
				
			||||||
  * vty: show subscriber: show lu d,h,m,s ago, not just seconds
 | 
					 | 
				
			||||||
  * auc3g: officially wrap IND around IND_bitlen space
 | 
					 | 
				
			||||||
  * make osmo_cni_peer_id_cmp() NULL safe
 | 
					 | 
				
			||||||
  * osmo_gsup_req_new(): require from_peer != NULL
 | 
					 | 
				
			||||||
  * gsup_server.c: properly handle negative rc from osmo_gsup_conn_ccm_get()
 | 
					 | 
				
			||||||
  * osmo_mslookup_server_mdns_rx(): handle read() rc == 0
 | 
					 | 
				
			||||||
  * hlr_subscr_nam(): fix condition to fix nam=false notifications
 | 
					 | 
				
			||||||
  * esme_dgsm.py: add --always-fail option for debugging SMPP
 | 
					 | 
				
			||||||
  * osmo-mslookup-client: fix segfault for respond_error() caller
 | 
					 | 
				
			||||||
  * manual: describe subscriber import by SQL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Harald Welte ]
 | 
					 | 
				
			||||||
  * Revert "add osmo-mslookup-client program"
 | 
					 | 
				
			||||||
  * Revert "add mDNS lookup method to libosmo-mslookup"
 | 
					 | 
				
			||||||
  * Use OSMO_FD_* instead of deprecated BSC_FD_*
 | 
					 | 
				
			||||||
  * support the XOR algorithm for UMTS AKA
 | 
					 | 
				
			||||||
  * auc_test.c: Add some comments on what the test cases actually do
 | 
					 | 
				
			||||||
  * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
 | 
					 | 
				
			||||||
  * manuals: generate vty reference xml at build time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Vadim Yanitskiy ]
 | 
					 | 
				
			||||||
  * db: fix possible SQLite3 allocated memory leak in db_open()
 | 
					 | 
				
			||||||
  * gsup_server: fix typo: s/omso_gsup_message/osmo_gsup_message/
 | 
					 | 
				
			||||||
  * debian/control: change maintainer to the Osmocom team / mailing list
 | 
					 | 
				
			||||||
  * cosmetic: fix spelling in logging message: existAnt -> existEnt
 | 
					 | 
				
			||||||
  * doc/manuals: fix s/There/The/ in 'USSD Configuration'
 | 
					 | 
				
			||||||
  * doc/manuals: re-organize description of internal USSD handlers
 | 
					 | 
				
			||||||
  * USSD: fix handle_ussd(): do not free() unconditionally
 | 
					 | 
				
			||||||
  * USSD: add special 'idle' handler to IUSE for testing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Eric ]
 | 
					 | 
				
			||||||
  * configure.ac: fix libtool issue  with clang and sanitizer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Philipp Maier ]
 | 
					 | 
				
			||||||
  * doc: do not use loglevel info for log category ss
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Pau Espin Pedrol ]
 | 
					 | 
				
			||||||
  * configure.ac: Fix trailing whitespace
 | 
					 | 
				
			||||||
  * doc: Update VTY reference xml file
 | 
					 | 
				
			||||||
  * Support setting rt-prio and cpu-affinity mask through VTY
 | 
					 | 
				
			||||||
  * Set TCP NODELAY sockopt to GSUP cli and srv connections
 | 
					 | 
				
			||||||
  * contrib/jenkins: Enable parallel make in make distcheck
 | 
					 | 
				
			||||||
  * .gitignore: Ignore new autofoo tmp files
 | 
					 | 
				
			||||||
  * tests: Replace deprecated API log_set_print_filename
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Keith ]
 | 
					 | 
				
			||||||
  * osmo-hlr-db-tool: Make import from osmo-nitb less "lossy"
 | 
					 | 
				
			||||||
  * Correct vty inline help for show subscriber
 | 
					 | 
				
			||||||
  * Add vty command to show summary of all or filtered subscribers
 | 
					 | 
				
			||||||
  * Fix Coverity Warnings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 23 Feb 2021 18:13:53 +0100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo-hlr (1.2.0) unstable; urgency=medium
 | 
					osmo-hlr (1.2.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [ Ruben Undheim ]
 | 
					  [ Ruben Undheim ]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -1,30 +1,30 @@
 | 
				
			|||||||
Source: osmo-hlr
 | 
					Source: osmo-hlr
 | 
				
			||||||
Section: net
 | 
					Section: net
 | 
				
			||||||
Priority: optional
 | 
					Priority: optional
 | 
				
			||||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
					Maintainer: Max Suraev <msuraev@sysmocom.de>
 | 
				
			||||||
Build-Depends: debhelper (>= 9),
 | 
					Build-Depends: debhelper (>= 9),
 | 
				
			||||||
               pkg-config,
 | 
					               pkg-config,
 | 
				
			||||||
               dh-autoreconf,
 | 
					               dh-autoreconf,
 | 
				
			||||||
 | 
					               dh-systemd (>= 1.5),
 | 
				
			||||||
               autotools-dev,
 | 
					               autotools-dev,
 | 
				
			||||||
               python3-minimal,
 | 
					               python3-minimal,
 | 
				
			||||||
               libosmocore-dev (>= 1.8.0),
 | 
					               libosmocore-dev,
 | 
				
			||||||
               libosmo-abis-dev (>= 1.4.0),
 | 
					               libosmo-abis-dev,
 | 
				
			||||||
               libosmo-netif-dev (>= 1.3.0),
 | 
					               libosmo-netif-dev,
 | 
				
			||||||
               libsqlite3-dev,
 | 
					               libsqlite3-dev,
 | 
				
			||||||
               sqlite3,
 | 
					               sqlite3,
 | 
				
			||||||
               osmo-gsm-manuals-dev (>= 1.4.0)
 | 
					               osmo-gsm-manuals-dev
 | 
				
			||||||
Standards-Version: 3.9.6
 | 
					Standards-Version: 3.9.6
 | 
				
			||||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
 | 
					Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
 | 
				
			||||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
 | 
					Vcs-Git: git://git.osmocom.org/osmo-hlr
 | 
				
			||||||
Homepage: https://projects.osmocom.org/projects/osmo-hlr
 | 
					Homepage: https://projects.osmocom.org/projects/osmo-hlr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package: osmo-hlr
 | 
					Package: osmo-hlr
 | 
				
			||||||
Architecture: any
 | 
					Architecture: any
 | 
				
			||||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
					Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
 | 
				
			||||||
Description: Osmocom Home Location Register
 | 
					Description: Osmocom Home Location Register
 | 
				
			||||||
 OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which
 | 
					 OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which works over GSUP
 | 
				
			||||||
 works over GSUP protocol. The subscribers are store in sqlite DB.
 | 
					 protocol. The subscribers are store in sqlite DB. It supports both 2G and 3G authentication.
 | 
				
			||||||
 It supports both 2G and 3G authentication.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package: osmo-hlr-dbg
 | 
					Package: osmo-hlr-dbg
 | 
				
			||||||
Architecture: any
 | 
					Architecture: any
 | 
				
			||||||
@@ -59,7 +59,7 @@ Description: Development headers of Osmocom GSUP client library
 | 
				
			|||||||
  .
 | 
					  .
 | 
				
			||||||
  This package contains the development headers.
 | 
					  This package contains the development headers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package: libosmo-mslookup1
 | 
					Package: libosmo-mslookup0
 | 
				
			||||||
Section: libs
 | 
					Section: libs
 | 
				
			||||||
Architecture: any
 | 
					Architecture: any
 | 
				
			||||||
Multi-Arch: same
 | 
					Multi-Arch: same
 | 
				
			||||||
@@ -73,7 +73,7 @@ Package: libosmo-mslookup-dev
 | 
				
			|||||||
Architecture: any
 | 
					Architecture: any
 | 
				
			||||||
Multi-Arch: same
 | 
					Multi-Arch: same
 | 
				
			||||||
Depends: ${misc:Depends},
 | 
					Depends: ${misc:Depends},
 | 
				
			||||||
	 libosmo-mslookup1 (= ${binary:Version}),
 | 
						 libosmo-mslookup0 (= ${binary:Version}),
 | 
				
			||||||
	 libosmocore-dev
 | 
						 libosmocore-dev
 | 
				
			||||||
Pre-Depends: ${misc:Pre-Depends}
 | 
					Pre-Depends: ${misc:Pre-Depends}
 | 
				
			||||||
Description: Development headers of Osmocom MS lookup library
 | 
					Description: Development headers of Osmocom MS lookup library
 | 
				
			||||||
@@ -81,16 +81,6 @@ Description: Development headers of Osmocom MS lookup library
 | 
				
			|||||||
  .
 | 
					  .
 | 
				
			||||||
  This package contains the development headers.
 | 
					  This package contains the development headers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package: osmo-mslookup-utils
 | 
					 | 
				
			||||||
Architecture: any
 | 
					 | 
				
			||||||
Section: utils
 | 
					 | 
				
			||||||
Depends: ${shlibs:Depends},
 | 
					 | 
				
			||||||
	 libosmo-mslookup1 (= ${binary:Version}),
 | 
					 | 
				
			||||||
         ${misc:Depends}
 | 
					 | 
				
			||||||
Multi-Arch: same
 | 
					 | 
				
			||||||
Description: Utilities for Osmocom MS lookup
 | 
					 | 
				
			||||||
  This package contains a simple MS lookup client utility.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Package: osmo-hlr-doc
 | 
					Package: osmo-hlr-doc
 | 
				
			||||||
Architecture: all
 | 
					Architecture: all
 | 
				
			||||||
Section: doc
 | 
					Section: doc
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@ Upstream-Name: OsmoHLR
 | 
				
			|||||||
Source: http://cgit.osmocom.org/osmo-hlr/
 | 
					Source: http://cgit.osmocom.org/osmo-hlr/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Files: *
 | 
					Files: *
 | 
				
			||||||
Copyright: 2016-2022 Sysmocom s. f. m. c. GmbH <info@sysmocom.de>
 | 
					Copyright: 2016-2017 Sysmocom s. f. m. c. GmbH <info@sysmocom.de>
 | 
				
			||||||
License: AGPL-3+
 | 
					License: AGPL-3+
 | 
				
			||||||
 | 
					
 | 
				
			||||||
License: AGPL-3+
 | 
					License: AGPL-3+
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							@@ -5,5 +5,4 @@
 | 
				
			|||||||
/usr/share/doc/osmo-hlr/sql/hlr.sql
 | 
					/usr/share/doc/osmo-hlr/sql/hlr.sql
 | 
				
			||||||
/usr/share/doc/osmo-hlr/sql/hlr_data.sql
 | 
					/usr/share/doc/osmo-hlr/sql/hlr_data.sql
 | 
				
			||||||
/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
 | 
					/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
 | 
				
			||||||
/usr/share/doc/osmo-hlr/examples/osmo-hlr-dgsm.cfg
 | 
					/var/lib/osmocom
 | 
				
			||||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								debian/osmo-mslookup-utils.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mslookup-utils.install
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
usr/bin/osmo-mslookup-client
 | 
					 | 
				
			||||||
							
								
								
									
										5
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +0,0 @@
 | 
				
			|||||||
#!/bin/sh -e
 | 
					 | 
				
			||||||
# Debian's postinst script is called on both installation and upgrade. Call the
 | 
					 | 
				
			||||||
# post-upgrade script in both cases, it won't do anything if there is nothing
 | 
					 | 
				
			||||||
# to do.
 | 
					 | 
				
			||||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
 | 
					 | 
				
			||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
# OsmoHLR example configuration for Distributed GSM (mslookup)
 | 
					 | 
				
			||||||
hlr
 | 
					 | 
				
			||||||
 gsup
 | 
					 | 
				
			||||||
  # For D-GSM roaming, osmo-hlr's GSUP must listen on a public IP:
 | 
					 | 
				
			||||||
  bind ip 10.9.8.7
 | 
					 | 
				
			||||||
  # Each HLR should identify with a distinct name
 | 
					 | 
				
			||||||
  ipa-name hlr-23
 | 
					 | 
				
			||||||
mslookup
 | 
					 | 
				
			||||||
 # Bind mslookup mDNS server and client on default multicast address and port:
 | 
					 | 
				
			||||||
 # 239.192.23.42 port 4266
 | 
					 | 
				
			||||||
 mdns bind
 | 
					 | 
				
			||||||
 # Tell the mslookup server in osmo-hlr which IP+ports to return when a
 | 
					 | 
				
			||||||
 # remote voice call or SMS wants to deliver to a local subscriber:
 | 
					 | 
				
			||||||
 server
 | 
					 | 
				
			||||||
  # local osmo-sip-connector SIP port
 | 
					 | 
				
			||||||
  service sip.voice at 10.9.8.7 5060
 | 
					 | 
				
			||||||
  # local osmo-msc SMPP port
 | 
					 | 
				
			||||||
  service smpp.sms at 10.9.8.7 2775
 | 
					 | 
				
			||||||
  # experimental: SMS-over-GSUP: this HLR's GSUP port
 | 
					 | 
				
			||||||
  service gsup.sms at 10.9.8.7 4222
 | 
					 | 
				
			||||||
  # only required if different from above 'gsup'/'bind ip':
 | 
					 | 
				
			||||||
  #service gsup.hlr at 10.9.8.7 4222
 | 
					 | 
				
			||||||
@@ -12,7 +12,7 @@ log stderr
 | 
				
			|||||||
 logging level main notice
 | 
					 logging level main notice
 | 
				
			||||||
 logging level db notice
 | 
					 logging level db notice
 | 
				
			||||||
 logging level auc notice
 | 
					 logging level auc notice
 | 
				
			||||||
 logging level ss notice
 | 
					 logging level ss info
 | 
				
			||||||
 logging level linp error
 | 
					 logging level linp error
 | 
				
			||||||
!
 | 
					!
 | 
				
			||||||
line vty
 | 
					line vty
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,6 @@
 | 
				
			|||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = example_subscriber_add_update_delete.vty \
 | 
				
			||||||
    example_subscriber_add_update_delete.vty \
 | 
					 | 
				
			||||||
    example_subscriber_aud2g.ctrl \
 | 
					 | 
				
			||||||
    example_subscriber_aud3g.ctrl \
 | 
					 | 
				
			||||||
    example_subscriber_cs_ps_enabled.ctrl \
 | 
					    example_subscriber_cs_ps_enabled.ctrl \
 | 
				
			||||||
    example_subscriber_info.ctrl \
 | 
					    example_subscriber_info.ctrl \
 | 
				
			||||||
    example_subscriber_msisdn.ctrl \
 | 
					 | 
				
			||||||
    osmohlr-usermanual.adoc \
 | 
					    osmohlr-usermanual.adoc \
 | 
				
			||||||
    osmohlr-usermanual-docinfo.xml \
 | 
					    osmohlr-usermanual-docinfo.xml \
 | 
				
			||||||
    osmohlr-vty-reference.xml \
 | 
					    osmohlr-vty-reference.xml \
 | 
				
			||||||
@@ -18,12 +14,6 @@ if BUILD_MANUALS
 | 
				
			|||||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
 | 
					  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  VTY_REFERENCE = osmohlr-vty-reference.xml
 | 
					  VTY_REFERENCE = osmohlr-vty-reference.xml
 | 
				
			||||||
 | 
					 | 
				
			||||||
  BUILT_REFERENCE_XML = $(builddir)/vty/hlr_vty_reference.xml
 | 
					 | 
				
			||||||
  $(builddir)/vty/hlr_vty_reference.xml: $(top_builddir)/src/osmo-hlr
 | 
					 | 
				
			||||||
	mkdir -p $(builddir)/vty
 | 
					 | 
				
			||||||
	$(top_builddir)/src/osmo-hlr --vty-ref-xml > $@
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
					  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  OSMO_REPOSITORY = osmo-hlr
 | 
					  OSMO_REPOSITORY = osmo-hlr
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,16 +5,6 @@ The actual protocol is described in <<common-control-if>>, the variables common
 | 
				
			|||||||
to all programs using it are described in <<ctrl_common_vars>>. This section
 | 
					to all programs using it are described in <<ctrl_common_vars>>. This section
 | 
				
			||||||
describes the CTRL interface variables specific to OsmoHLR.
 | 
					describes the CTRL interface variables specific to OsmoHLR.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Subscribers can be created and deleted using the following SET commands:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.Subscriber management commands available on OsmoHLR's Control interface
 | 
					 | 
				
			||||||
[options="header",width="100%",cols="35%,65%"]
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
|Command|Comment
 | 
					 | 
				
			||||||
|subscriber.create '123456'|Create a new subscriber with IMSI "123456" to the database. Returns database ID of the subscriber being created.
 | 
					 | 
				
			||||||
|subscriber.delete '123456'|Delete subscriber with IMSI "123456" from database. Returns database ID of the subscriber being deleted.
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
All subscriber variables are available by different selectors, which are freely
 | 
					All subscriber variables are available by different selectors, which are freely
 | 
				
			||||||
interchangeable:
 | 
					interchangeable:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,9 +28,6 @@ Each of the above selectors feature all of these control variables:
 | 
				
			|||||||
|subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one
 | 
					|subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one
 | 
				
			||||||
|subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access
 | 
					|subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access
 | 
				
			||||||
|subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access
 | 
					|subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access
 | 
				
			||||||
|subscriber.by-\*.*msisdn*|RW|No|valid MSISDN string|Get/Set assigned MSISDN
 | 
					 | 
				
			||||||
|subscriber.by-\*.*aud2g*|RW|No|'algo[,KI]'|Get/Set 2g Authentication Data
 | 
					 | 
				
			||||||
|subscriber.by-\*.*aud2g*|RW|No|'algo[,KI,("op"|"opc"),OP_C[,ind_bitlen]]'|Get/Set 3g Authentication Data
 | 
					 | 
				
			||||||
|===
 | 
					|===
 | 
				
			||||||
 | 
					
 | 
				
			||||||
=== subscriber.by-*.info, info-aud, info-all
 | 
					=== subscriber.by-*.info, info-aud, info-all
 | 
				
			||||||
@@ -117,63 +104,3 @@ commands:
 | 
				
			|||||||
----
 | 
					----
 | 
				
			||||||
include::../example_subscriber_cs_ps_enabled.ctrl[]
 | 
					include::../example_subscriber_cs_ps_enabled.ctrl[]
 | 
				
			||||||
----
 | 
					----
 | 
				
			||||||
 | 
					 | 
				
			||||||
=== subscriber.by-*.msisdn
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Get or set the MSISDN currently assigned to a subscriber.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is an example transcript that illustrates use of this command:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
include::../example_subscriber_msisdn.ctrl[]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== subscriber.by-*.aud2g
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Get or set the 2G Authentication data of a subscriber.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The information is stored/retrieved as a comma separated list of fields:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
algo[,KI]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Where::
 | 
					 | 
				
			||||||
* *KI* is the KI as a hexadecimal string.
 | 
					 | 
				
			||||||
* *algo* is one of the following algorithms: _none, xor, comp128v1, comp128v2,
 | 
					 | 
				
			||||||
  comp128v3_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
All values are case insensitive.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is an example transcript that illustrates use of this command:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
include::../example_subscriber_aud2g.ctrl[]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== subscriber.by-*.aud3g
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Get or set the 3G Authentication data of a subscriber.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The information is stored/retrieved as a comma separated list of fields:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
algo[,KI,("op"|"opc"),OP_C[,ind_bitlen]]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Where:
 | 
					 | 
				
			||||||
* *KI* is the KI as a hexadecimal string.
 | 
					 | 
				
			||||||
* *algo* is one of the following algorithms: _none, xor, milenage_.
 | 
					 | 
				
			||||||
* "op" or "opc" indicates whether next field is an OP or OPC value.
 | 
					 | 
				
			||||||
* *OP_C* contains an OP or OPC values as hexadecimal string, based on what the
 | 
					 | 
				
			||||||
  previous field specifies.
 | 
					 | 
				
			||||||
* *ind_bitlen* is set to 5 by default if not provided.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
All values are case insensitive.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is an example transcript that illustrates use of this command:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
include::../example_subscriber_aud3g.ctrl[]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,491 +0,0 @@
 | 
				
			|||||||
== Distributed GSM / Multicast MS Lookup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Distributed GSM (D-GSM) allows independent mobile core network stacks to provide voice, SMS and Roaming services to each
 | 
					 | 
				
			||||||
other, without the need for centralised entities or administration authority, and in a way that is resilient against
 | 
					 | 
				
			||||||
unstable network links between sites.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
D-GSM aims at communal networks, where several independent sites, let's call them villages, each have a full mobile core
 | 
					 | 
				
			||||||
network infrastructure. It elegantly provides ad-hoc service for subscribers moving across villages, and allows villages
 | 
					 | 
				
			||||||
to dynamically join or leave the cooperative network without the need for configuration changes at other sites.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A challenge for linking separate sites is to find the current location of a subscriber. Typically, in mobile networks, a
 | 
					 | 
				
			||||||
centralized entity keeps track of where to Page for subscribers. Running several fully independent sites with unreliable
 | 
					 | 
				
			||||||
links between them makes it hard to provide such centralisation.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
D-GSM finds subscribers by mslookup, a service provided by OsmoHLR, typically using multicast DNS queries.  This allows
 | 
					 | 
				
			||||||
routing Location Updating requests, calls, and SMS to the right site without administrative delay nor the need for a
 | 
					 | 
				
			||||||
reliable link to a central database.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
D-GSM is highly resilient against single sites or links becoming temporarily unavailable. Service between still
 | 
					 | 
				
			||||||
reachable sites simply continues; Service to a disconnected site resumes as soon as it becomes reachable again.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This brings an entirely new paradigm to mobile core network infrastructure: as sites become reachable on the IP network
 | 
					 | 
				
			||||||
and join the common IP multicast group, services between them become available immediately. Basically, the only premise
 | 
					 | 
				
			||||||
is that IP routing and multicast works across sites, and that each site uses unique IPA names in the GSUP config.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This chapter describes how D-GSM and mslookup work, and how to configure sites to use D-GSM, using Osmocom core network
 | 
					 | 
				
			||||||
infrastructure.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== Finding Subscribers: mslookup Clients
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are two fundamentally distinct subscriber lookups provided by the mslookup service.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Find the Current Location of an MSISDN
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[fig_dgsm_connect]]
 | 
					 | 
				
			||||||
.mslookup for connecting subscribers: Alice is visiting village C; a phone call gets routed directly to her current location independently from her resident village infrastructure
 | 
					 | 
				
			||||||
[graphviz]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
digraph G {
 | 
					 | 
				
			||||||
rankdir=LR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subgraph cluster_village_b {
 | 
					 | 
				
			||||||
	label="Village B"
 | 
					 | 
				
			||||||
	ms_bob [label="Bob\n(from village B)",shape=box]
 | 
					 | 
				
			||||||
	pbx_b [label="SIP B"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subgraph cluster_village_c {
 | 
					 | 
				
			||||||
	label="Village C"
 | 
					 | 
				
			||||||
	ms_alice [label="Alice\n(from village A)",shape=box]
 | 
					 | 
				
			||||||
	msc_c [label="MSC C"]
 | 
					 | 
				
			||||||
	hlr_c [label="HLR C"]
 | 
					 | 
				
			||||||
	sip_c [label="SIP C"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ms_alice -> msc_c [style=dashed,arrowhead=none]
 | 
					 | 
				
			||||||
msc_c -> hlr_c [label="attached",style=dashed,arrowhead=none]
 | 
					 | 
				
			||||||
ms_bob -> pbx_b [label="call Alice"]
 | 
					 | 
				
			||||||
pbx_b -> hlr_c [label="mslookup by MSISDN",style=dotted,dir=both]
 | 
					 | 
				
			||||||
pbx_b -> sip_c -> msc_c -> ms_alice [label="call"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For example, if a subscriber is currently visiting another village, establish a phone call / send SMS towards that
 | 
					 | 
				
			||||||
village.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- To deliver a phone call, a SIP agent integrates an mslookup client to request the SIP service of an MSISDN's current
 | 
					 | 
				
			||||||
  location (example: <<dgsm_conf_dialplan>>). It receives an IP address and port to send the SIP Invite to.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- To deliver an SMS, an ESME integrates an mslookup client to request the SMPP service of an MSISDN's current location
 | 
					 | 
				
			||||||
  (example: <<dgsm_conf_esme_smpp>>).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The current location of a subscriber may change at any time, and, when moving across locations, a subscriber may
 | 
					 | 
				
			||||||
suddenly lose reception to the previous location without explicitly detaching. Hence an mslookup request for the current
 | 
					 | 
				
			||||||
location of an MSISDN may get numerous responses. To find the currently valid location, mslookup includes the age of the
 | 
					 | 
				
			||||||
subscriber record, i.e. how long ago the subscriber was last reached. The one response with the youngest age reflects
 | 
					 | 
				
			||||||
the current location.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In order to evaluate several responses, mslookup always waits for a fixed amount of time (1 second), and then evaluates
 | 
					 | 
				
			||||||
the available responses.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Services are not limited to SIP and SMPP, arbitrarily named services can be added to the mslookup configuration.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.Message sequence for locating an MSISDN to deliver a voice call
 | 
					 | 
				
			||||||
["mscgen"]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
msc {
 | 
					 | 
				
			||||||
  hscale="2";
 | 
					 | 
				
			||||||
  moms[label="MS,BSS\nvillage A"],momsc[label="MSC,MGW\nvillage A"],mosipcon[label="osmo-sip-connector\nvillage A"],mopbx[label="PBX\nvillage A"],mthlr[label="OsmoHLR\nvillage B"],mtsipcon[label="osmo-sip-connector\nvillage B"],mtmsc[label="MGW,MSC\nvillage B"],mtms[label="RAN,MS\nvillage B"];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  moms =>> momsc [label="CC Setup"];
 | 
					 | 
				
			||||||
  momsc =>> mosipcon [label="MNCC_SETUP_IND"];
 | 
					 | 
				
			||||||
  mosipcon =>> mopbx [label="SIP INVITE"];
 | 
					 | 
				
			||||||
  mopbx rbox mopbx [label="dialplan: launch mslookup by MSISDN"];
 | 
					 | 
				
			||||||
  --- [label="multicast-DNS query to all connected sites"];
 | 
					 | 
				
			||||||
  ...;
 | 
					 | 
				
			||||||
  mopbx <<= mthlr [label="mDNS response\n(age)"];
 | 
					 | 
				
			||||||
  mopbx rbox mopbx [label="wait ~ 1s for more mDNS responses"];
 | 
					 | 
				
			||||||
  ...;
 | 
					 | 
				
			||||||
  mopbx =>> mtsipcon [label="SIP INVITE (MT)"];
 | 
					 | 
				
			||||||
  mtmsc <<= mtsipcon [label="MNCC_SETUP_REQ"];
 | 
					 | 
				
			||||||
  mtms <<= mtmsc [label="Paging (CC)"];
 | 
					 | 
				
			||||||
  moms rbox mtms [label="voice call commences"];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Find the Home HLR for an IMSI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[fig_dgsm_roaming]]
 | 
					 | 
				
			||||||
.mslookup for Roaming: Alice visits village B; she can attach to the local mobile network, which proxies HLR administration to her home village.
 | 
					 | 
				
			||||||
[graphviz]
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
digraph G {
 | 
					 | 
				
			||||||
rankdir=LR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subgraph cluster_village_b {
 | 
					 | 
				
			||||||
	label="Village B"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ms_alice [label="Alice\n(from village A)",shape=box]
 | 
					 | 
				
			||||||
	msc_b [label="MSC B"]
 | 
					 | 
				
			||||||
	hlr_b [label="HLR B"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subgraph cluster_village_a {
 | 
					 | 
				
			||||||
	label="Village A"
 | 
					 | 
				
			||||||
	hlr_alice [label="Alice's home HLR"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ms_alice -> msc_b -> hlr_b [label="Location\nUpdating"]
 | 
					 | 
				
			||||||
hlr_b -> hlr_alice [label="mslookup by IMSI",style=dotted,dir=both]
 | 
					 | 
				
			||||||
hlr_b -> hlr_alice [label="GSUP proxy forwarding"]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For example, when attaching to a local network, a local resident gets serviced directly by the local village's HLR,
 | 
					 | 
				
			||||||
while a visitor from another village gets serviced by the remote village's HLR (Roaming).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A home HLR typically stays the same for a given IMSI. If the home site is reachable, there should be exactly one
 | 
					 | 
				
			||||||
response to an mslookup request asking for it. The age of such a home-HLR response is always sent as zero.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If a response's age is zero, mslookup does not wait for further responses and immediately uses the result.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If there were more than one HLR accepting service for an IMSI, the one with the shortest response latency is used.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== mslookup Configuration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OsmoHLR the main mslookup agent. It provides the responses for both current location services as well as for locating
 | 
					 | 
				
			||||||
the fixed home-HLR. But naturally, depending on the mslookup request's purpose, different OsmoHLR instances will respond
 | 
					 | 
				
			||||||
for a given subscriber.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- When querying the home HLR, it is always the (typically single) home HLR instance that sends the mslookup response. As
 | 
					 | 
				
			||||||
  soon as it finds the queried IMSI in the local HLR database, an OsmoHLR will respond to home-HLR requests.
 | 
					 | 
				
			||||||
  In <<fig_dgsm_roaming>>, Alice's home HLR responds to the Roaming request ("where is the home HLR?").
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- When querying the location of an MSISDN, it is always the HLR proxy nearest to the servicing MSC that sends the
 | 
					 | 
				
			||||||
  mslookup response. Even though the home HLR keeps the Location Updating record also for Roaming cases, it will only
 | 
					 | 
				
			||||||
  respond to an mslookup service request if the subscriber has attached at a directly connected MSC. If attached at a
 | 
					 | 
				
			||||||
  remote MSC, that MSC's remote HLR will be the GSUP proxy for the home HLR, and the remote HLR is responsible for
 | 
					 | 
				
			||||||
  responding to service requests.
 | 
					 | 
				
			||||||
  In <<fig_dgsm_roaming>>, HLR B is the nearest proxy and will answer all service requests ("where is this MSISDN?").
 | 
					 | 
				
			||||||
  Alice's home HLR will not answer service requests, because it detects that the servicing MSC is connected via another
 | 
					 | 
				
			||||||
  HLR proxy.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[dgsm_example_config]]
 | 
					 | 
				
			||||||
==== Example
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Here is an osmo-hlr.cfg mslookup configuration example for one site, which is explained in subsequent chapters.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 hlr
 | 
					 | 
				
			||||||
  gsup
 | 
					 | 
				
			||||||
   bind ip 10.9.8.7
 | 
					 | 
				
			||||||
   ipa-name hlr-23
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  mdns bind
 | 
					 | 
				
			||||||
  server
 | 
					 | 
				
			||||||
   service sip.voice at 10.9.8.7 5060
 | 
					 | 
				
			||||||
   service smpp.sms at 10.9.8.7 2775
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OsmoHLR has both an mslookup server and a client.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- The server responds to incoming service and home-HLR requests, when the local HLR is responsible.
 | 
					 | 
				
			||||||
- The client is used as GSUP proxy to a remote home HLR (found by mslookup upon a locally unknown IMSI).
 | 
					 | 
				
			||||||
- The client may also be used for forwarding SMS-over-GSUP.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The mslookup service can be implemented by various methods.
 | 
					 | 
				
			||||||
At the time of writing, the only method implemented is mDNS.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== mDNS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The stock mslookup method is mDNS, multicast DNS. It consists of standard DNS encoding according to <<ietf-rfc1035>> and
 | 
					 | 
				
			||||||
<<ietf-rfc3596>>, but sent and received on IP multicast. In the response, standard A and AAAA records return the
 | 
					 | 
				
			||||||
service's IP address, while additional TXT records provide the service's port number and the MS attach age.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TIP: To watch D-GSM mDNS conversations in wireshark, select "udp.port == 4266" (the default mslookup mDNS port
 | 
					 | 
				
			||||||
number), right click on the packet to "Decode as...", and select "DNS".
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In OsmoHLR, the mDNS server and client are typically both enabled at the same time:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  mdns bind
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Server and client can also be enabled/disabled individually:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  server
 | 
					 | 
				
			||||||
   mdns bind
 | 
					 | 
				
			||||||
  client
 | 
					 | 
				
			||||||
   mdns bind
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
These examples use the default mslookup multicast IP address and port. It is possible to configure custom IP address and
 | 
					 | 
				
			||||||
port, but beware that the IP address must be from a multicast range, see <<ietf-rfc5771>>:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  mdns bind 239.192.23.42 4266
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Domain names generated from mslookup queries (e.g. "sip.voice.123.msisdn") should not collide with IANA permitted
 | 
					 | 
				
			||||||
domains. Therefore we add the "mdns.osmocom.org" suffix. It can be overridden as follows:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  mdns domain-suffix mdns.osmocom.org
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Server: Site Services
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The mslookup server requires a list of service addresses provided at the local site, in order to respond to service
 | 
					 | 
				
			||||||
requests matching locally attached subscribers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  server
 | 
					 | 
				
			||||||
   service sip.voice at 10.9.8.7 5060
 | 
					 | 
				
			||||||
   service smpp.sms at 10.9.8.7 2775
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In this example:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- "10.9.8.7 5060" are the IP address and port on which the local site's osmo-sip-connector is bound to receive SIP
 | 
					 | 
				
			||||||
  Invite requests.
 | 
					 | 
				
			||||||
- "10.9.8.7 2775" are the local site's OsmoMSC SMPP bind address and port.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Obviously, these IP addresses must be routable back to this site from all other sites. Using link-local or "ANY"
 | 
					 | 
				
			||||||
addresses, like 127.0.0.1 or 0.0.0.0, will not work here. Instead, each service config requires a public IP address that
 | 
					 | 
				
			||||||
all remote requestors are able to reach (not necessarily on the host that osmo-hlr is running on).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If a site has more than one MSC, services can also be configured for each MSC individually, keyed by the IPA unit name
 | 
					 | 
				
			||||||
that each MSC sends on the GSUP link:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  server
 | 
					 | 
				
			||||||
   msc ipa-name msc-262-42-0
 | 
					 | 
				
			||||||
    service sip.voice at 10.11.12.13 5060
 | 
					 | 
				
			||||||
    service smpp.sms at 10.11.12.13 2775
 | 
					 | 
				
			||||||
   msc ipa-name msc-901-70-0
 | 
					 | 
				
			||||||
    service sip.voice at 10.9.8.7 5060
 | 
					 | 
				
			||||||
    service smpp.sms at 10.9.8.7 2775
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Here, "msc-262-42-0" is the IPA name of a local OsmoMSC instance. To configure an OsmoMSC's IPA name on the GSUP link,
 | 
					 | 
				
			||||||
see osmo-msc.cfg, setting `hlr` / `ipa-name`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For mslookup service responses, only Location Updatings in the Circuit Switched domain are relevant. OsmoHLR does manage
 | 
					 | 
				
			||||||
IMSIs attaching in the Packet Switched domain (via an SGSN) similarly to Circuit Switched (via an MSC), but mslookup
 | 
					 | 
				
			||||||
completely ignores the Packet Switched attach status.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Server: Own GSUP Address
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
When responding to home-HLR requests, OsmoHLR implicitly by default responds with its locally configured GSUP bind
 | 
					 | 
				
			||||||
address (setting `hlr` / `gsup` / `bind ip`). If required, an explicit local GSUP address and port can be configured,
 | 
					 | 
				
			||||||
for example:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 hlr
 | 
					 | 
				
			||||||
  gsup
 | 
					 | 
				
			||||||
   bind ip 0.0.0.0
 | 
					 | 
				
			||||||
   ipa-name hlr-23
 | 
					 | 
				
			||||||
 mslookup
 | 
					 | 
				
			||||||
  server
 | 
					 | 
				
			||||||
   # osmo-hlr's own GSUP address to send in mslookup responses:
 | 
					 | 
				
			||||||
   service gsup.hlr at 10.9.8.7 4222
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The gsup.hlr service can only be configured globally (because requests come from arbitrary mDNS clients, before a
 | 
					 | 
				
			||||||
Location Updating has associated the IMSI with the requesting MSC).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Client IPA Naming
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For reliable GSUP proxy routing to a remote HLR (Roaming), it is important that each GSUP client, i.e. each HLR, MSC and
 | 
					 | 
				
			||||||
SGSN instance, has a unique IPA name.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example for configuring an OsmoHLR instance's IPA name:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 hlr
 | 
					 | 
				
			||||||
  gsup
 | 
					 | 
				
			||||||
   ipa-name hlr-23
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Here, "hlr-23" is the unique identification of this OsmoHLR instance across all potentially connected D-GSM sites.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Furthermore, each MSC and SGSN must have a uniquely distinct IPA name across all sites (here "msc-262-42-0" and
 | 
					 | 
				
			||||||
"msc-901-70-0" are used as example IPA names for local MSCs).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
When this OsmoHLR connects to a remote HLR, be it for GSUP proxying or SMS-over-GSUP, it communicates its own IPA name
 | 
					 | 
				
			||||||
(on GSUP link-up) as well as the IPA name of the requesting client MSC/SGSN (as Source Name in each message) to the
 | 
					 | 
				
			||||||
remote OsmoHLR GSUP server. These names are used to route GSUP responses back to the respective requesting peer.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If two MSCs were accidentally configured with identical names, a problem will occur as soon as both MSCs attempt to
 | 
					 | 
				
			||||||
attach to the same OsmoHLR (either directly or via GSUP proxying). The MSC that shows up first will work normally, but
 | 
					 | 
				
			||||||
any duplicate that shows up later will be rejected, since a route for its name already exists.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== Queries
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In URL notation, typical mslookup queries look like:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 gsup.hlr.123456789.imsi
 | 
					 | 
				
			||||||
 sip.voice.123.msisdn
 | 
					 | 
				
			||||||
 smpp.sms.123.msisdn
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A query consists of
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- a service name ("gsup.hlr"),
 | 
					 | 
				
			||||||
- an id ("123456789"),
 | 
					 | 
				
			||||||
- the id type ("imsi").
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The calling client also defines a timeout to wait for responses.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The mslookup ID types are fixed, while service names can be chosen arbitrarily.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mslookup ID types, no other ID types are understood by mslookup
 | 
					 | 
				
			||||||
[options="header",width="100%",cols="20%,80%"]
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
|ID Type|Description
 | 
					 | 
				
			||||||
|imsi|An IMSI as existing in an OsmoHLR subscriber database
 | 
					 | 
				
			||||||
|msisdn|A phone number as configured in an OsmoHLR subscriber database
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mslookup service name conventions, arbitrary service names can be added as required
 | 
					 | 
				
			||||||
[options="header",width="100%",cols="20%,20%,60%"]
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
|Service Name|Protocol|Description
 | 
					 | 
				
			||||||
|gsup.hlr | GSUP | Home HLR's GSUP server, to handle Location Updating related procedures
 | 
					 | 
				
			||||||
|sip.voice | SIP | OsmoSIPConnector, to receive a SIP Invite (MT side of a call)
 | 
					 | 
				
			||||||
|smpp.sms | SMPP | Destination OsmoMSC (or other SMPP server) to deliver an SMS to the recipient
 | 
					 | 
				
			||||||
|gsup.sms | GSUP | GSUP peer to deliver an SMS to the recipient using SMS-over-GSUP
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Arbitrarily named services can be added to the mslookup configuration and queried by mslookup clients; as soon as a
 | 
					 | 
				
			||||||
service name is present in osmo-hlr.cfg, it can be queried from any mslookup client.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Service names should consist of a protocol name (like "sip", "gsup", "english") and an intended action/entity (like
 | 
					 | 
				
			||||||
"voice", "hlr", "greeting").
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== Service Client Implementation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In principle, arbitrary services could query target addresses via mslookup, leaving it up to any and all kinds of
 | 
					 | 
				
			||||||
clients to find their respective destination addresses. But of course, mslookup was designed with specific services in
 | 
					 | 
				
			||||||
mind, namely:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- SIP call agents and
 | 
					 | 
				
			||||||
- SMS delivery (an ESME or SMSC)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The following chapters describe examples of setting up a working distributed core network providing SIP voice calls and
 | 
					 | 
				
			||||||
SMS forwarding across sites.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== mslookup Library
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The OsmoHLR provides an mslookup client C library, libosmo-mslookup. Service lookups can be integrated directly
 | 
					 | 
				
			||||||
in client programs using this library. However, its mDNS implementation requires the libosmocore select() loop, which
 | 
					 | 
				
			||||||
can be challenging to integrate in practice. An alternative solution is the osmo-mslookup-client tool.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[dgsm_osmo_mslookup_client]]
 | 
					 | 
				
			||||||
==== osmo-mslookup-client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The mslookup C library is available, but often, a simpler approach for client implementations is desirable:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- When querying for a service address, the client is typically interested in the single final best result (youngest age
 | 
					 | 
				
			||||||
  / first responding home HLR).
 | 
					 | 
				
			||||||
- Voice call and SMS clients typically would block until an mslookup result is known. For example, the FreeSwitch
 | 
					 | 
				
			||||||
  dialplan integration expects a result synchronously, i.e. without waiting for mslookup responses via a select() loop.
 | 
					 | 
				
			||||||
- Integrating the libosmocore select() loop required for mDNS can break the already existing socket handling in the
 | 
					 | 
				
			||||||
  client program.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The osmo-mslookup-client cmdline tool provides a trivial way to synchronously acquire the single result for an mslookup
 | 
					 | 
				
			||||||
request. The service client can invoke an osmo-mslookup-client process per request and read the result from stdout.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Each invocation obviously spawns a separate process and opens a multicast socket for mDNS. For better scalability,
 | 
					 | 
				
			||||||
osmo-mslookup-client can also be run as a daemon, providing results via a unix domain socket. Using synchronous write()
 | 
					 | 
				
			||||||
and recv() allows blocking until a result is received without interfering with the client program's select() setup.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
By itself, osmo-mslookup-client is also helpful as a diagnostic tool:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
$ osmo-mslookup-client sip.voice.1001.msisdn
 | 
					 | 
				
			||||||
sip.voice.1001.msisdn	ok	10.9.8.7	5060
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ osmo-mslookup-client gsup.hlr.901700000014701.imsi
 | 
					 | 
				
			||||||
gsup.hlr.901700000014701.imsi	ok	10.9.8.7	4222
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ osmo-mslookup-client gsup.hlr.111111.imsi
 | 
					 | 
				
			||||||
gsup.hlr.111111.imsi	not-found
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ osmo-mslookup-client gsup.hlr.1001.msisdn sip.voice.1001.msisdn smpp.sms.1001.msisdn foo.1001.msisdn
 | 
					 | 
				
			||||||
gsup.hlr.1001.msisdn	ok	10.9.8.7	4222
 | 
					 | 
				
			||||||
foo.1001.msisdn	not-found
 | 
					 | 
				
			||||||
smpp.sms.1001.msisdn	ok	10.9.8.7	2775
 | 
					 | 
				
			||||||
sip.voice.1001.msisdn	ok	10.9.8.7	5060
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ osmo-mslookup-client --csv-headers gsup.hlr.901700000014701.imsi
 | 
					 | 
				
			||||||
QUERY	RESULT	V4_IP	V4_PORT	V6_IP	V6_PORT
 | 
					 | 
				
			||||||
gsup.hlr.901700000014701.imsi	ok	10.9.8.7	4222
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ osmo-mslookup-client -f json gsup.hlr.901700000014701.imsi
 | 
					 | 
				
			||||||
{"query": "gsup.hlr.901700000014701.imsi", "result": "ok", "v4": ["10.9.8.7", "4222"]}
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For full help including example client invocations in Python, see the output of:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 osmo-mslookup-client -h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== SIP Service Client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[dgsm_conf_dialplan]]
 | 
					 | 
				
			||||||
===== FreeSwitch dialplan.py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The FreeSWITCH PBX software <<freeswitch_pbx>> offers a Python integration to determine a SIP call recipient by a custom
 | 
					 | 
				
			||||||
dialplan implementation. An example dialplan implementation for FreeSWITCH that uses D-GSM mslookup is provided in the
 | 
					 | 
				
			||||||
osmo-hlr source tree under `contrib`, called `freeswitch_dialplan_dgsm.py`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To integrate it with your FREESWITCH setup, add a new `extension` block to your `dialplan/public.xml`:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
    <extension name="outbound">
 | 
					 | 
				
			||||||
      <condition field="destination_number" expression=".*">
 | 
					 | 
				
			||||||
	<action application="set" data="hangup_after_bridge=true"/>
 | 
					 | 
				
			||||||
	<action application="set" data="session_in_hangup_hook=true"/>
 | 
					 | 
				
			||||||
	<action application="set" data="ringback=%(2000, 4000, 440.0, 480.0)"/>
 | 
					 | 
				
			||||||
	<action application="python" data="freeswitch_dialplan_dgsm"/>
 | 
					 | 
				
			||||||
      </condition>
 | 
					 | 
				
			||||||
    </extension>
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Make sure that the dir containing `freeswitch_dialplan_dgsm.py` is in your `PYTHONPATH` environment variable, and start
 | 
					 | 
				
			||||||
the server:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
$ export PYTHONPATH="$PYTHONPATH:/home/user/code/osmo-hlr/contrib/dgsm"
 | 
					 | 
				
			||||||
$ freeswitch -nf -nonat -nonatmap -nocal -nort -c
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== SMS Service Client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[dgsm_conf_esme_smpp]]
 | 
					 | 
				
			||||||
===== SMS via SMPP Port
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An example ESME using D-GSM mslookup, `esme_dgsm.py`, is provided in the osmo-hlr source tree under `contrib`. It
 | 
					 | 
				
			||||||
attaches to OsmoMSC's SMPP port to send SMS to recipients determined by mslookup.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OsmoMSC should be configured as "smpp-first", so that all SMS routing is determined by mslookup. If configured without
 | 
					 | 
				
			||||||
smpp-first, OsmoMSC may try to deliver an SMS locally, even though the recipient has recently moved to a different site.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An example OsmoMSC configuration to work with esme_dgsm.py:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
smpp
 | 
					 | 
				
			||||||
 local-tcp-ip 127.0.0.1 2775
 | 
					 | 
				
			||||||
 system-id test-msc
 | 
					 | 
				
			||||||
 policy closed
 | 
					 | 
				
			||||||
 smpp-first
 | 
					 | 
				
			||||||
 # outgoing to esme_dgsm.py
 | 
					 | 
				
			||||||
 esme OSMPP
 | 
					 | 
				
			||||||
  no alert-notifications
 | 
					 | 
				
			||||||
  password foo
 | 
					 | 
				
			||||||
  default-route
 | 
					 | 
				
			||||||
 # incoming from esme_dgsm.py
 | 
					 | 
				
			||||||
 esme ISMPP
 | 
					 | 
				
			||||||
  no alert-notifications
 | 
					 | 
				
			||||||
  password foo
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Launch esme_dgsm.py alongside OsmoMSC:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
./esme_dgsm.py --src-host 127.0.0.1
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
esme_dgsm.py will be notified via SMPP for each SMS to be delivered, and will forward them either to a remote
 | 
					 | 
				
			||||||
recipient, or back to the same OsmoMSC, depending on the mslookup result. If the MSISDN is not reachable (or
 | 
					 | 
				
			||||||
esme_dgsm.py can't handle the message for other reasons), it returns the RSYSERR code back to OsmoMSC.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that the esme_dgsm.py is a proof of concept and should not be used in production. It has several limitations, such
 | 
					 | 
				
			||||||
as not supporting multipart SMS messages.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
===== SMS-Over-GSUP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The GSUP protocol defines SMS delivery messages. When OsmoMSC is configured to deliver SMS via GSUP, MO SMS are directly
 | 
					 | 
				
			||||||
forwarded to the HLR, which will determine where to forward the SMS-over-GSUP messages using its mslookup client.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FIXME implement this
 | 
					 | 
				
			||||||
@@ -54,7 +54,7 @@ this database file will be created in the current working directory.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along
 | 
					Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along
 | 
				
			||||||
with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data
 | 
					with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data
 | 
				
			||||||
from an old 'OsmoNITB' database. See <<db_import_nitb>>.
 | 
					from an old 'OsmoNITB' database. See `osmo-hlr-db-tool --help`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
=== Multiple instances
 | 
					=== Multiple instances
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,83 +127,3 @@ OsmoHLR# subscriber imei 35761300444848 show
 | 
				
			|||||||
----
 | 
					----
 | 
				
			||||||
<1> Randomly generated 5 digit MSISDN
 | 
					<1> Randomly generated 5 digit MSISDN
 | 
				
			||||||
<2> Disabled CS and PS NAM prevent the subscriber from accessing the network
 | 
					<2> Disabled CS and PS NAM prevent the subscriber from accessing the network
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== Import Subscriber Data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==== Scripted Import
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WARNING: It is not generally a good idea to depend on the HLR database's internal table structure, but in the lack of an
 | 
					 | 
				
			||||||
automated import procedure, this example is provided as an ad-hoc method to aid automated subscriber import. This is not
 | 
					 | 
				
			||||||
guaranteed to remain valid.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
NOTE: We may add CSV and other import methods to the `osmo-hlr-db-tool`, but so far that is not implemented. Contact the
 | 
					 | 
				
			||||||
community if you are interested in such a feature being implemented.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
NOTE: `sqlite3` is available from your distribution packages or `sqlite.org`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Currently, probably the easiest way to automatically import subscribers to OsmoHLR is to write out a text file with SQL
 | 
					 | 
				
			||||||
commands per subscriber, and feed that to `sqlite3`, as described below.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A difficulty is to always choose subscriber IDs that are not yet in use. For an initial import, the subscriber ID may be
 | 
					 | 
				
			||||||
incremented per subscriber record. If adding more subscribers to an existing database, it is necessary to choose
 | 
					 | 
				
			||||||
subscriber IDs that are not yet in use. Get the highest ID in use with:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
sqlite3 hlr.db 'select max(id) from subscriber'
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A full SQL example of adding a single subscriber with id 23, IMSI 001010123456789, MSISDN 1234, Ki for COMP128v1, and K
 | 
					 | 
				
			||||||
and OPC for Milenage:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
INSERT subscriber (id, imsi, msisdn) VALUES (23, '001010123456789', '1234');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)
 | 
					 | 
				
			||||||
VALUES(23, 1, '0123456789abcdef0123456789abcdef');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc)
 | 
					 | 
				
			||||||
VALUES(23, 5, '0123456789abcdef0123456789abcdef',NULL,'0123456789abcdef0123456789abcdef');
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Table entries to `auc_2g` and/or `auc_3g` may be omitted if no such key material is required.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
UMTS Milenage auth (on both 2G and 3G RAN) is configured by the `auc_3g` table. `algo_id_3g` must currently always be 5
 | 
					 | 
				
			||||||
(MILENAGE).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The algorithm IDs for `algo_id_2g` and `algo_id_3g` are:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.Algorithm IDs in OsmoHLR's database
 | 
					 | 
				
			||||||
[options="header",width="50%",cols="40%,60%"]
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
|`algo_id_2g` / `algo_id_3g` | Authentication Algorithm
 | 
					 | 
				
			||||||
| 1 | COMP128v1
 | 
					 | 
				
			||||||
| 2 | COMP128v2
 | 
					 | 
				
			||||||
| 3 | COMP128v3
 | 
					 | 
				
			||||||
| 4 | XOR
 | 
					 | 
				
			||||||
| 5 | MILENAGE
 | 
					 | 
				
			||||||
|===
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Create an empty HLR database with
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
osmo-hlr-db-tool -l hlr.db create
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Repeat above SQL commands per subscriber, incrementing the subscriber ID for each block, then feed the SQL commands for
 | 
					 | 
				
			||||||
the subscribers to be imported to the `sqlite3` command line tool:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
sqlite3 hlr.db < subscribers.sql
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[db_import_nitb]]
 | 
					 | 
				
			||||||
==== Import OsmoNITB database
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To upgrade from old OsmoNITB to OsmoHLR, use `osmo-hlr-db-tool`:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
osmo-hlr-db-tool -l hlr.db import-nitb-db nitb.db
 | 
					 | 
				
			||||||
----
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Be aware that the import is lossy, only the IMSI, MSISDN, nam_cs/ps and 2G auth data are set.
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,29 +50,15 @@ prefix route to the named EUSE.  All USSD short codes starting with *123 will be
 | 
				
			|||||||
routed to the named EUSE.
 | 
					routed to the named EUSE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`ussd route prefix *#100# internal own-msisdn` installs a prefix route
 | 
					`ussd route prefix *#100# internal own-msisdn` installs a prefix route
 | 
				
			||||||
to the named internal USSD handler.  The above command will restore
 | 
					to the named internal USSD handler.  There above command will restore
 | 
				
			||||||
the old behavior, in which *#100# will return a text message containing
 | 
					the old behavior, in which *#100# will return a text message containing
 | 
				
			||||||
the subscribers own phone number.  More information on internal USSD
 | 
					the subscribers own phone number.  There is one other handler called
 | 
				
			||||||
handlers can be found in <<iuse_handlers>>.
 | 
					`own-imsi` which will return the IMSI instead of the MSISDN.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`ussd default-route external foobar-00-00-00-00-00-00` installs a
 | 
					`ussd default-route external foobar-00-00-00-00-00-00` installs a
 | 
				
			||||||
default route to the named EUSE.  This means that all USSD codes for
 | 
					default route to the named EUSE.  This means that all USSD codes for
 | 
				
			||||||
which no more specific route exists will be routed to the named EUSE.
 | 
					which no more specific route exists will be routed to the named EUSE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[iuse_handlers]]
 | 
					 | 
				
			||||||
=== Built-in USSD handlers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OsmoHLR has an Internal USSD Entity (IUSE) that allows to handle some
 | 
					 | 
				
			||||||
USSD requests internally.  It features a set of simple handlers, which
 | 
					 | 
				
			||||||
can be assigned to one or more USSD request prefixes:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* `own-msisdn` returns subscriber's MSISDN (if assigned);
 | 
					 | 
				
			||||||
* `own-imsi` returns subscriber's IMSI;
 | 
					 | 
				
			||||||
* `test-idle` keeps the session idle until the MS terminates it, or
 | 
					 | 
				
			||||||
  the guard timer expires (may be useful for testing).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Additional handlers can be added on request.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=== Example EUSE program
 | 
					=== Example EUSE program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We have provided an example EUSE developed in C language using existing
 | 
					We have provided an example EUSE developed in C language using existing
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
GET 1 subscriber.by-imsi-901991234567891.aud2g
 | 
					 | 
				
			||||||
GET_REPLY 1 subscriber.by-imsi-901991234567891.aud2g none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 2 subscriber.by-imsi-901991234567891.aud2g xor,c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					 | 
				
			||||||
SET_REPLY 2 subscriber.by-imsi-901991234567891.aud2g OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 3 subscriber.by-imsi-901991234567891.aud2g
 | 
					 | 
				
			||||||
GET_REPLY 3 subscriber.by-imsi-901991234567891.aud2g XOR,c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 4 subscriber.by-imsi-901991234567891.aud2g none
 | 
					 | 
				
			||||||
SET_REPLY 4 subscriber.by-imsi-901991234567891.aud2g OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 5 subscriber.by-imsi-901991234567891.aud2g
 | 
					 | 
				
			||||||
GET_REPLY 5 subscriber.by-imsi-901991234567891.aud2g none
 | 
					 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
GET 117 subscriber.by-imsi-901991234567891.aud3g
 | 
					 | 
				
			||||||
GET_REPLY 117 subscriber.by-imsi-901991234567891.aud3g none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 118 subscriber.by-imsi-901991234567891.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OP,FB2A3D1B360F599ABAB99DB8669F8308
 | 
					 | 
				
			||||||
SET_REPLY 118 subscriber.by-imsi-901991234567891.aud3g OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 119 subscriber.by-imsi-901991234567891.aud3g
 | 
					 | 
				
			||||||
GET_REPLY 119 subscriber.by-imsi-901991234567891.aud3g MILENAGE,c01ffedc1cadaeac1d1f1edacac1ab0a,OP,fb2a3d1b360f599abab99db8669f8308,5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 120 subscriber.by-imsi-901991234567891.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,FB2A3D1B360F599ABAB99DB8669F8308,7
 | 
					 | 
				
			||||||
SET_REPLY 120 subscriber.by-imsi-901991234567891.aud3g OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 121 subscriber.by-imsi-901991234567891.aud3g
 | 
					 | 
				
			||||||
GET_REPLY 121 subscriber.by-imsi-901991234567891.aud3g MILENAGE,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,fb2a3d1b360f599abab99db8669f8308,7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 122 subscriber.by-imsi-901991234567891.aud3g none
 | 
					 | 
				
			||||||
SET_REPLY 122 subscriber.by-imsi-901991234567891.aud3g OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 123 subscriber.by-imsi-901991234567891.aud3g
 | 
					 | 
				
			||||||
GET_REPLY 123 subscriber.by-imsi-901991234567891.aud3g none
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
GET 1 subscriber.by-imsi-901991234567891.msisdn
 | 
					 | 
				
			||||||
GET_REPLY 1 subscriber.by-imsi-901991234567891.msisdn none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SET 2 subscriber.by-imsi-901991234567891.msisdn 555666
 | 
					 | 
				
			||||||
SET_REPLY 2 subscriber.by-imsi-901991234567891.msisdn OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GET 3 subscriber.by-imsi-901991234567891.msisdn
 | 
					 | 
				
			||||||
GET_REPLY 3 subscriber.by-imsi-901991234567891.msisdn 555666
 | 
					 | 
				
			||||||
@@ -24,12 +24,8 @@ include::{srcdir}/chapters/control.adoc[]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
include::./common/chapters/control_if.adoc[]
 | 
					include::./common/chapters/control_if.adoc[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include::{srcdir}/chapters/dgsm.adoc[]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include::./common/chapters/gsup.adoc[]
 | 
					include::./common/chapters/gsup.adoc[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include::./common/chapters/vty_cpu_sched.adoc[]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include::./common/chapters/port_numbers.adoc[]
 | 
					include::./common/chapters/port_numbers.adoc[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include::./common/chapters/bibliography.adoc[]
 | 
					include::./common/chapters/bibliography.adoc[]
 | 
				
			||||||
@@ -37,3 +33,4 @@ include::./common/chapters/bibliography.adoc[]
 | 
				
			|||||||
include::./common/chapters/glossary.adoc[]
 | 
					include::./common/chapters/glossary.adoc[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include::./common/chapters/gfdl.adoc[]
 | 
					include::./common/chapters/gfdl.adoc[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1431
									
								
								doc/manuals/vty/hlr_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1431
									
								
								doc/manuals/vty/hlr_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,9 +1,7 @@
 | 
				
			|||||||
SUBDIRS = osmocom
 | 
					SUBDIRS = osmocom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nobase_include_HEADERS = \
 | 
					nobase_include_HEADERS = \
 | 
				
			||||||
	osmocom/gsupclient/cni_peer_id.h \
 | 
					 | 
				
			||||||
	osmocom/gsupclient/gsup_client.h \
 | 
						osmocom/gsupclient/gsup_client.h \
 | 
				
			||||||
	osmocom/gsupclient/gsup_req.h \
 | 
					 | 
				
			||||||
	osmocom/mslookup/mdns.h \
 | 
						osmocom/mslookup/mdns.h \
 | 
				
			||||||
	osmocom/mslookup/mdns_sock.h \
 | 
						osmocom/mslookup/mdns_sock.h \
 | 
				
			||||||
	osmocom/mslookup/mslookup_client_fake.h \
 | 
						osmocom/mslookup/mslookup_client_fake.h \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,66 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * All Rights Reserved
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU General Public License along
 | 
					 | 
				
			||||||
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/utils.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! IPA Name: Arbitrary length blob, not necessarily zero-terminated.
 | 
					 | 
				
			||||||
 * In osmo-hlr, struct hlr_subscriber is mostly used as static reference and cannot serve as talloc context, which is
 | 
					 | 
				
			||||||
 * why this is also implemented as a fixed-maximum-size buffer instead of a talloc'd arbitrary sized buffer.
 | 
					 | 
				
			||||||
 * NOTE: The length of val may be extended in the future if it becomes necessary.
 | 
					 | 
				
			||||||
 * At the time of writing, this holds IPA unit name strings of very limited length.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct osmo_ipa_name {
 | 
					 | 
				
			||||||
	size_t len;
 | 
					 | 
				
			||||||
	uint8_t val[128];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name);
 | 
					 | 
				
			||||||
int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len);
 | 
					 | 
				
			||||||
int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...);
 | 
					 | 
				
			||||||
int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b);
 | 
					 | 
				
			||||||
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name);
 | 
					 | 
				
			||||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum osmo_cni_peer_id_type {
 | 
					 | 
				
			||||||
	OSMO_CNI_PEER_ID_EMPTY=0,
 | 
					 | 
				
			||||||
	OSMO_CNI_PEER_ID_IPA_NAME,
 | 
					 | 
				
			||||||
	/* OSMO_CNI_PEER_ID_GLOBAL_TITLE, <-- currently not implemented, but likely future possibility */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct value_string osmo_cni_peer_id_type_names[];
 | 
					 | 
				
			||||||
static inline const char *osmo_cni_peer_id_type_name(enum osmo_cni_peer_id_type val)
 | 
					 | 
				
			||||||
{ return get_value_string(osmo_cni_peer_id_type_names, val); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_cni_peer_id {
 | 
					 | 
				
			||||||
	enum osmo_cni_peer_id_type type;
 | 
					 | 
				
			||||||
	union {
 | 
					 | 
				
			||||||
		struct osmo_ipa_name ipa_name;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id);
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
 | 
					 | 
				
			||||||
			  const uint8_t *val, size_t len);
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
 | 
					 | 
				
			||||||
			      const char *str_fmt, ...);
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b);
 | 
					 | 
				
			||||||
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cni_peer_id);
 | 
					 | 
				
			||||||
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cni_peer_id);
 | 
					 | 
				
			||||||
@@ -38,8 +38,6 @@ struct osmo_gsup_client;
 | 
				
			|||||||
/* Expects message in msg->l2h */
 | 
					/* Expects message in msg->l2h */
 | 
				
			||||||
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
 | 
					typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef bool (*osmo_gsup_client_up_down_cb_t)(struct osmo_gsup_client *gsupc, bool up);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_client {
 | 
					struct osmo_gsup_client {
 | 
				
			||||||
	const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
 | 
						const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,31 +53,8 @@ struct osmo_gsup_client {
 | 
				
			|||||||
	int got_ipa_pong;
 | 
						int got_ipa_pong;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */
 | 
						struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */
 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_gsup_client_up_down_cb_t up_down_cb;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_client_config {
 | 
					 | 
				
			||||||
	/*! IP access unit which contains client identification information; must be allocated in talloc_ctx as well to
 | 
					 | 
				
			||||||
	 * ensure it lives throughout the lifetime of the connection. */
 | 
					 | 
				
			||||||
	struct ipaccess_unit *ipa_dev;
 | 
					 | 
				
			||||||
	/*! GSUP server IP address to connect to. */
 | 
					 | 
				
			||||||
	const char *ip_addr;
 | 
					 | 
				
			||||||
	/*! GSUP server TCP port to connect to. */
 | 
					 | 
				
			||||||
	unsigned int tcp_port;
 | 
					 | 
				
			||||||
	/*! OPA client configuration, or NULL. */
 | 
					 | 
				
			||||||
	struct osmo_oap_client_config *oapc_config;
 | 
					 | 
				
			||||||
	/*! callback for reading from the GSUP connection. */
 | 
					 | 
				
			||||||
	osmo_gsup_client_read_cb_t read_cb;
 | 
					 | 
				
			||||||
	/*! Invoked when the GSUP link is ready for communication, and when the link drops. */
 | 
					 | 
				
			||||||
	osmo_gsup_client_up_down_cb_t up_down_cb;
 | 
					 | 
				
			||||||
	/*! User data stored in the returned gsupc->data, as context for the callbacks. */
 | 
					 | 
				
			||||||
	void *data;
 | 
					 | 
				
			||||||
	/*! Marker for future extension, always pass this as false. */
 | 
					 | 
				
			||||||
	bool more;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_gsup_client_config *config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
 | 
					struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
 | 
				
			||||||
						  struct ipaccess_unit *ipa_dev,
 | 
											  struct ipaccess_unit *ipa_dev,
 | 
				
			||||||
						  const char *ip_addr,
 | 
											  const char *ip_addr,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,119 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * All Rights Reserved
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU General Public License along
 | 
					 | 
				
			||||||
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_req;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \
 | 
					 | 
				
			||||||
		(req) ? (req)->nr : 0, \
 | 
					 | 
				
			||||||
		(req) ? osmo_cni_peer_id_to_str(&(req)->source_name) : "NULL", \
 | 
					 | 
				
			||||||
		(req) ? (req)->gsup.imsi : "NULL", \
 | 
					 | 
				
			||||||
		(req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
 | 
					 | 
				
			||||||
		##args)
 | 
					 | 
				
			||||||
#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ_CAT_SRC(req, subsys, level, __FILE__, __LINE__, fmt, ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_GSUP_REQ_SRC(req, level, file, line, fmt, args...) \
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ_CAT_SRC(req, DLGSUP, level, file, line, fmt, ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_GSUP_REQ(req, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ_SRC(req, level, __FILE__, __LINE__, fmt, ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef void (*osmo_gsup_req_send_response_t)(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Keep track of an incoming request, to route back a response when it is ready.
 | 
					 | 
				
			||||||
 * Particularly, a GSUP response to a request must contain various bits of information that need to be copied from the
 | 
					 | 
				
			||||||
 * request for proxy/routing to work and for session states to remain valid. That is the main reason why (almost) all
 | 
					 | 
				
			||||||
 * GSUP request/response should go through an osmo_gsup_req, even if it is handled synchronously.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct osmo_gsup_req {
 | 
					 | 
				
			||||||
	/* The incoming GSUP message in decoded form. */
 | 
					 | 
				
			||||||
	const struct osmo_gsup_message gsup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Decoding result code. If decoding failed, this will be != 0. */
 | 
					 | 
				
			||||||
	int decode_rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
 | 
					 | 
				
			||||||
	 * immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
 | 
					 | 
				
			||||||
	 * This source_name is implicitly added to the routes for the conn the message was received on. */
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id source_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
 | 
					 | 
				
			||||||
	 * and source_name. */
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id via_proxy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Identify this request by number, for logging. */
 | 
					 | 
				
			||||||
	unsigned int nr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* osmo_gsup_req can be used by both gsup_server and gsup_client. The individual method of actually sending a
 | 
					 | 
				
			||||||
	 * GSUP message is provided by this callback. */
 | 
					 | 
				
			||||||
	osmo_gsup_req_send_response_t send_response_cb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* User supplied data pointer, may be used to provide context to send_response_cb(). */
 | 
					 | 
				
			||||||
	void *cb_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* List entry that can be used to keep a list of osmo_gsup_req instances; not used directly by osmo_gsup_req.c,
 | 
					 | 
				
			||||||
	 * it is up to using implementations to keep a list. If this is non-NULL, osmo_gsup_req_free() calls
 | 
					 | 
				
			||||||
	 * llist_del() on this. */
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,
 | 
					 | 
				
			||||||
	 * we also need to keep the msgb. */
 | 
					 | 
				
			||||||
	struct msgb *msg;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
 | 
					 | 
				
			||||||
					osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
 | 
					 | 
				
			||||||
					struct llist_head *add_to_list);
 | 
					 | 
				
			||||||
void osmo_gsup_req_free(struct osmo_gsup_req *req);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! See _osmo_gsup_req_respond() for details.
 | 
					 | 
				
			||||||
 * Call _osmo_gsup_req_respond(), passing the caller's source file and line for logging. */
 | 
					 | 
				
			||||||
#define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \
 | 
					 | 
				
			||||||
	_osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__)
 | 
					 | 
				
			||||||
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
 | 
					 | 
				
			||||||
			   bool error, bool final_response, const char *file, int line);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! See _osmo_gsup_req_respond_msgt() for details.
 | 
					 | 
				
			||||||
 * Call _osmo_gsup_req_respond_msgt(), passing the caller's source file and line for logging. */
 | 
					 | 
				
			||||||
#define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \
 | 
					 | 
				
			||||||
	_osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__)
 | 
					 | 
				
			||||||
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
 | 
					 | 
				
			||||||
				bool final_response, const char *file, int line);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! See _osmo_gsup_req_respond_err() for details.
 | 
					 | 
				
			||||||
 * Log an error message, and call _osmo_gsup_req_respond_err(), passing the caller's source file and line for logging.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
 | 
					 | 
				
			||||||
			     get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
 | 
					 | 
				
			||||||
		_osmo_gsup_req_respond_err(REQ, CAUSE, __FILE__, __LINE__); \
 | 
					 | 
				
			||||||
	} while(0)
 | 
					 | 
				
			||||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
 | 
					 | 
				
			||||||
				const char *file, int line);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_gsup_make_response(struct osmo_gsup_message *reply,
 | 
					 | 
				
			||||||
			    const struct osmo_gsup_message *rx, bool error, bool final_response);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t osmo_gsup_message_to_str_buf(char *buf, size_t bufsize, const struct osmo_gsup_message *msg);
 | 
					 | 
				
			||||||
char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg);
 | 
					 | 
				
			||||||
@@ -2,7 +2,6 @@ noinst_HEADERS = \
 | 
				
			|||||||
	auc.h \
 | 
						auc.h \
 | 
				
			||||||
	ctrl.h \
 | 
						ctrl.h \
 | 
				
			||||||
	db.h \
 | 
						db.h \
 | 
				
			||||||
	dgsm.h \
 | 
					 | 
				
			||||||
	gsup_router.h \
 | 
						gsup_router.h \
 | 
				
			||||||
	gsup_server.h \
 | 
						gsup_server.h \
 | 
				
			||||||
	hlr.h \
 | 
						hlr.h \
 | 
				
			||||||
@@ -10,11 +9,6 @@ noinst_HEADERS = \
 | 
				
			|||||||
	hlr_vty.h \
 | 
						hlr_vty.h \
 | 
				
			||||||
	hlr_vty_subscr.h \
 | 
						hlr_vty_subscr.h \
 | 
				
			||||||
	logging.h \
 | 
						logging.h \
 | 
				
			||||||
	lu_fsm.h \
 | 
						luop.h \
 | 
				
			||||||
	mslookup_server.h \
 | 
					 | 
				
			||||||
	mslookup_server_mdns.h \
 | 
					 | 
				
			||||||
	proxy.h \
 | 
					 | 
				
			||||||
	rand.h \
 | 
						rand.h \
 | 
				
			||||||
	remote_hlr.h \
 | 
					 | 
				
			||||||
	timestamp.h \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,4 +30,5 @@ enum hlr_ctrl_node {
 | 
				
			|||||||
	_LAST_CTRL_NODE_HLR
 | 
						_LAST_CTRL_NODE_HLR
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int hlr_ctrl_cmds_install();
 | 
				
			||||||
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);
 | 
					struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,19 +3,9 @@
 | 
				
			|||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <sqlite3.h>
 | 
					#include <sqlite3.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct hlr;
 | 
					struct hlr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum stmt_idx {
 | 
					enum stmt_idx {
 | 
				
			||||||
	DB_STMT_SEL_ALL,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_ALL_ORDER_LAST_SEEN,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_FILTER_MSISDN,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_FILTER_IMSI,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_FILTER_IMEI,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_FILTER_CS,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_FILTER_PS,
 | 
					 | 
				
			||||||
	DB_STMT_SEL_BY_IMSI,
 | 
						DB_STMT_SEL_BY_IMSI,
 | 
				
			||||||
	DB_STMT_SEL_BY_MSISDN,
 | 
						DB_STMT_SEL_BY_MSISDN,
 | 
				
			||||||
	DB_STMT_SEL_BY_ID,
 | 
						DB_STMT_SEL_BY_ID,
 | 
				
			||||||
@@ -41,9 +31,6 @@ enum stmt_idx {
 | 
				
			|||||||
	DB_STMT_SET_LAST_LU_SEEN_PS,
 | 
						DB_STMT_SET_LAST_LU_SEEN_PS,
 | 
				
			||||||
	DB_STMT_EXISTS_BY_IMSI,
 | 
						DB_STMT_EXISTS_BY_IMSI,
 | 
				
			||||||
	DB_STMT_EXISTS_BY_MSISDN,
 | 
						DB_STMT_EXISTS_BY_MSISDN,
 | 
				
			||||||
	DB_STMT_IND_ADD,
 | 
					 | 
				
			||||||
	DB_STMT_IND_SELECT,
 | 
					 | 
				
			||||||
	DB_STMT_IND_DEL,
 | 
					 | 
				
			||||||
	_NUM_DB_STMT
 | 
						_NUM_DB_STMT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,7 +49,6 @@ void db_remove_reset(sqlite3_stmt *stmt);
 | 
				
			|||||||
bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
 | 
					bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
 | 
				
			||||||
bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
 | 
					bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
 | 
				
			||||||
bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr);
 | 
					bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr);
 | 
				
			||||||
bool db_bind_null(sqlite3_stmt *stmt, const char *param_name);
 | 
					 | 
				
			||||||
void db_close(struct db_context *dbc);
 | 
					void db_close(struct db_context *dbc);
 | 
				
			||||||
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging, bool allow_upgrades);
 | 
					struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging, bool allow_upgrades);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,9 +97,6 @@ struct hlr_subscriber {
 | 
				
			|||||||
	bool		ms_purged_ps;
 | 
						bool		ms_purged_ps;
 | 
				
			||||||
	time_t		last_lu_seen;
 | 
						time_t		last_lu_seen;
 | 
				
			||||||
	time_t		last_lu_seen_ps;
 | 
						time_t		last_lu_seen_ps;
 | 
				
			||||||
	/* talloc'd IPA unit name */
 | 
					 | 
				
			||||||
	struct osmo_ipa_name	vlr_via_proxy;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name	sgsn_via_proxy;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* A format string for use with strptime(3). This format string is
 | 
					/* A format string for use with strptime(3). This format string is
 | 
				
			||||||
@@ -159,9 +142,6 @@ int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, cons
 | 
				
			|||||||
int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
 | 
					int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
 | 
				
			||||||
int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
 | 
					int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
 | 
					 | 
				
			||||||
		   void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
 | 
					 | 
				
			||||||
		   int *count, const char **err);
 | 
					 | 
				
			||||||
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
					int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
				
			||||||
			  struct hlr_subscriber *subscr);
 | 
								  struct hlr_subscriber *subscr);
 | 
				
			||||||
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
					int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
				
			||||||
@@ -171,14 +151,12 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
				
			|||||||
int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
 | 
					int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
 | 
				
			||||||
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
					int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const struct osmo_ipa_name *vlr_name, bool is_ps,
 | 
							 const char *vlr_or_sgsn_number, bool is_ps);
 | 
				
			||||||
		 const struct osmo_ipa_name *via_proxy);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
					int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
				
			||||||
		    bool purge_val, bool is_ps);
 | 
							    bool purge_val, bool is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind);
 | 
					int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
				
			||||||
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Call sqlite3_column_text() and copy result to a char[].
 | 
					/*! Call sqlite3_column_text() and copy result to a char[].
 | 
				
			||||||
 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
					 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
				
			||||||
@@ -190,14 +168,3 @@ int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
 | 
				
			|||||||
		const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
 | 
							const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
 | 
				
			||||||
		osmo_strlcpy(buf, _txt, sizeof(buf)); \
 | 
							osmo_strlcpy(buf, _txt, sizeof(buf)); \
 | 
				
			||||||
	} while (0)
 | 
						} while (0)
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Call sqlite3_column_text() and copy result to a struct osmo_ipa_name.
 | 
					 | 
				
			||||||
 * \param[out] ipa_name  A struct osmo_ipa_name* to write to.
 | 
					 | 
				
			||||||
 * \param[in] stmt  An sqlite3_stmt*.
 | 
					 | 
				
			||||||
 * \param[in] idx  Index in stmt's returned columns.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#define copy_sqlite3_text_to_ipa_name(ipa_name, stmt, idx) \
 | 
					 | 
				
			||||||
	do { \
 | 
					 | 
				
			||||||
		const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
 | 
					 | 
				
			||||||
		osmo_ipa_name_set_str(ipa_name, _txt); \
 | 
					 | 
				
			||||||
	} while (0)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS	2000
 | 
					 | 
				
			||||||
#define LOG_DGSM(imsi, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct vty;
 | 
					 | 
				
			||||||
struct remote_hlr;
 | 
					 | 
				
			||||||
struct hlr_subscriber;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void *dgsm_ctx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_init(void *ctx);
 | 
					 | 
				
			||||||
void dgsm_start(void *ctx);
 | 
					 | 
				
			||||||
void dgsm_stop();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_vty_init();
 | 
					 | 
				
			||||||
void dgsm_mdns_client_config_apply(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool hlr_subscr_lu_age(const struct hlr_subscriber *subscr, uint32_t *age_p);
 | 
					 | 
				
			||||||
@@ -3,8 +3,6 @@
 | 
				
			|||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_ipa_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct gsup_route {
 | 
					struct gsup_route {
 | 
				
			||||||
	struct llist_head list;
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,12 +12,10 @@ struct gsup_route {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
					struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
				
			||||||
					const uint8_t *addr, size_t addrlen);
 | 
										const uint8_t *addr, size_t addrlen);
 | 
				
			||||||
struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
 | 
					struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* add a new route for the given address to the given conn */
 | 
					/* add a new route for the given address to the given conn */
 | 
				
			||||||
int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name);
 | 
					 | 
				
			||||||
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
 | 
					int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* delete all routes for the given connection */
 | 
					/* delete all routes for the given connection */
 | 
				
			||||||
@@ -28,6 +24,3 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn);
 | 
				
			|||||||
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
					int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
				
			||||||
			const uint8_t *addr, size_t addrlen,
 | 
								const uint8_t *addr, size_t addrlen,
 | 
				
			||||||
			struct msgb *msg);
 | 
								struct msgb *msg);
 | 
				
			||||||
int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg);
 | 
					 | 
				
			||||||
int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,
 | 
					 | 
				
			||||||
			  const struct osmo_gsup_message *gsup);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,6 @@
 | 
				
			|||||||
#include <osmocom/abis/ipa.h>
 | 
					#include <osmocom/abis/ipa.h>
 | 
				
			||||||
#include <osmocom/abis/ipaccess.h>
 | 
					#include <osmocom/abis/ipaccess.h>
 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
 | 
					#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
 | 
				
			||||||
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN	43 /* TS 24.008 10.5.4.7 */
 | 
					#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN	43 /* TS 24.008 10.5.4.7 */
 | 
				
			||||||
@@ -24,12 +22,12 @@ struct osmo_gsup_server {
 | 
				
			|||||||
	/* list of osmo_gsup_conn */
 | 
						/* list of osmo_gsup_conn */
 | 
				
			||||||
	struct llist_head clients;
 | 
						struct llist_head clients;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* lu_operations list */
 | 
				
			||||||
 | 
						struct llist_head *luop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ipa_server_link *link;
 | 
						struct ipa_server_link *link;
 | 
				
			||||||
	osmo_gsup_read_cb_t read_cb;
 | 
						osmo_gsup_read_cb_t read_cb;
 | 
				
			||||||
	struct llist_head routes;
 | 
						struct llist_head routes;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Proxy requests from this server's clients to remote GSUP servers. */
 | 
					 | 
				
			||||||
	struct proxy *proxy;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,18 +40,15 @@ struct osmo_gsup_conn {
 | 
				
			|||||||
	//struct oap_state oap_state;
 | 
						//struct oap_state oap_state;
 | 
				
			||||||
	struct tlv_parsed ccm;
 | 
						struct tlv_parsed ccm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set when Location Update is received: */
 | 
						/* Set when Location Update is received: */
 | 
				
			||||||
	bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
 | 
						bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
 | 
				
			||||||
	bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
 | 
						bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in
 | 
					 | 
				
			||||||
	 * osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */
 | 
					 | 
				
			||||||
	struct osmo_ipa_name peer_name;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct msgb *osmo_gsup_msgb_alloc(const char *label);
 | 
					struct msgb *osmo_gsup_msgb_alloc(const char *label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg);
 | 
					 | 
				
			||||||
int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
 | 
					int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
 | 
				
			||||||
int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
 | 
					int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
 | 
				
			||||||
			   uint8_t tag);
 | 
								   uint8_t tag);
 | 
				
			||||||
@@ -62,6 +57,7 @@ struct osmo_gsup_server *osmo_gsup_server_create(void *ctx,
 | 
				
			|||||||
						 const char *ip_addr,
 | 
											 const char *ip_addr,
 | 
				
			||||||
						 uint16_t tcp_port,
 | 
											 uint16_t tcp_port,
 | 
				
			||||||
						 osmo_gsup_read_cb_t read_cb,
 | 
											 osmo_gsup_read_cb_t read_cb,
 | 
				
			||||||
 | 
											 struct llist_head *lu_op_lst,
 | 
				
			||||||
						 void *priv);
 | 
											 void *priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
 | 
					void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
 | 
				
			||||||
@@ -72,5 +68,3 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
 | 
				
			|||||||
					    uint8_t *msisdn_enc, size_t msisdn_enc_size,
 | 
										    uint8_t *msisdn_enc, size_t msisdn_enc_size,
 | 
				
			||||||
				            uint8_t *apn_buf, size_t apn_buf_size,
 | 
									            uint8_t *apn_buf, size_t apn_buf_size,
 | 
				
			||||||
					    enum osmo_gsup_cn_domain cn_domain);
 | 
										    enum osmo_gsup_cn_domain cn_domain);
 | 
				
			||||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
 | 
					 | 
				
			||||||
				    struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,21 +23,11 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
#include <osmocom/gsm/ipa.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/tdef.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/sockaddr_str.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/hlr/dgsm.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
 | 
					#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr_euse;
 | 
					struct hlr_euse;
 | 
				
			||||||
struct osmo_gsup_conn;
 | 
					 | 
				
			||||||
enum osmo_gsup_message_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern struct osmo_tdef g_hlr_tdefs[];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr {
 | 
					struct hlr {
 | 
				
			||||||
	/* GSUP server pointer */
 | 
						/* GSUP server pointer */
 | 
				
			||||||
@@ -49,15 +39,13 @@ struct hlr {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* Control Interface */
 | 
						/* Control Interface */
 | 
				
			||||||
	struct ctrl_handle *ctrl;
 | 
						struct ctrl_handle *ctrl;
 | 
				
			||||||
 | 
						const char *ctrl_bind_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Local bind addr */
 | 
						/* Local bind addr */
 | 
				
			||||||
	char *gsup_bind_addr;
 | 
						char *gsup_bind_addr;
 | 
				
			||||||
	struct ipaccess_unit gsup_unit_name;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct llist_head euse_list;
 | 
						struct llist_head euse_list;
 | 
				
			||||||
	struct hlr_euse *euse_default;
 | 
						struct hlr_euse *euse_default;
 | 
				
			||||||
	enum gsm48_gmm_cause reject_cause;
 | 
					 | 
				
			||||||
	enum gsm48_gmm_cause no_proxy_reject_cause;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* NCSS (call independent) session guard timeout value */
 | 
						/* NCSS (call independent) session guard timeout value */
 | 
				
			||||||
	int ncss_guard_timeout;
 | 
						int ncss_guard_timeout;
 | 
				
			||||||
@@ -72,47 +60,6 @@ struct hlr {
 | 
				
			|||||||
	/* Bitmask of DB_SUBSCR_FLAG_* */
 | 
						/* Bitmask of DB_SUBSCR_FLAG_* */
 | 
				
			||||||
	uint8_t subscr_create_on_demand_flags;
 | 
						uint8_t subscr_create_on_demand_flags;
 | 
				
			||||||
	unsigned int subscr_create_on_demand_rand_msisdn_len;
 | 
						unsigned int subscr_create_on_demand_rand_msisdn_len;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		bool allow_startup;
 | 
					 | 
				
			||||||
		struct {
 | 
					 | 
				
			||||||
			/* Whether the mslookup server should be active in general (all lookup methods) */
 | 
					 | 
				
			||||||
			bool enable;
 | 
					 | 
				
			||||||
			uint32_t local_attach_max_age;
 | 
					 | 
				
			||||||
			struct llist_head local_site_services;
 | 
					 | 
				
			||||||
			struct {
 | 
					 | 
				
			||||||
				/* Whether the mDNS method of the mslookup server should be active. */
 | 
					 | 
				
			||||||
				bool enable;
 | 
					 | 
				
			||||||
				/* The mDNS bind address and domain suffix as set by the VTY, not necessarily in use. */
 | 
					 | 
				
			||||||
				struct osmo_sockaddr_str bind_addr;
 | 
					 | 
				
			||||||
				char *domain_suffix;
 | 
					 | 
				
			||||||
				struct osmo_mslookup_server_mdns *running;
 | 
					 | 
				
			||||||
			} mdns;
 | 
					 | 
				
			||||||
		} server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* The mslookup client in osmo-hlr is used to find out which remote HLRs service a locally unknown IMSI.
 | 
					 | 
				
			||||||
		 * (It may also be used to resolve recipients for SMS-over-GSUP in the future.) */
 | 
					 | 
				
			||||||
		struct {
 | 
					 | 
				
			||||||
			/* Whether to proxy/forward to remote HLRs */
 | 
					 | 
				
			||||||
			bool enable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* If this is set, all GSUP for unknown IMSIs is forwarded directly to this GSUP address,
 | 
					 | 
				
			||||||
			 * unconditionally. */
 | 
					 | 
				
			||||||
			struct osmo_sockaddr_str gsup_gateway_proxy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* mslookup client request handling */
 | 
					 | 
				
			||||||
			unsigned int result_timeout_milliseconds;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			struct osmo_mslookup_client *client;
 | 
					 | 
				
			||||||
			struct {
 | 
					 | 
				
			||||||
				/* Whether to use mDNS for IMSI MS Lookup */
 | 
					 | 
				
			||||||
				bool enable;
 | 
					 | 
				
			||||||
				struct osmo_sockaddr_str query_addr;
 | 
					 | 
				
			||||||
				char *domain_suffix;
 | 
					 | 
				
			||||||
				struct osmo_mslookup_client_method *running;
 | 
					 | 
				
			||||||
			} mdns;
 | 
					 | 
				
			||||||
		} client;
 | 
					 | 
				
			||||||
	} mslookup;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct hlr *g_hlr;
 | 
					extern struct hlr *g_hlr;
 | 
				
			||||||
@@ -120,4 +67,3 @@ extern struct hlr *g_hlr;
 | 
				
			|||||||
struct hlr_subscriber;
 | 
					struct hlr_subscriber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
 | 
					void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
 | 
				
			||||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,8 +46,8 @@ struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *
 | 
				
			|||||||
						   struct hlr_euse *euse);
 | 
											   struct hlr_euse *euse);
 | 
				
			||||||
void ussd_route_del(struct hlr_ussd_route *rt);
 | 
					void ussd_route_del(struct hlr_ussd_route *rt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void rx_proc_ss_req(struct osmo_gsup_req *req);
 | 
					int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
 | 
				
			||||||
void rx_proc_ss_error(struct osmo_gsup_req *req);
 | 
					int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ss_session;
 | 
					struct ss_session;
 | 
				
			||||||
struct ss_request;
 | 
					struct ss_request;
 | 
				
			||||||
@@ -56,5 +56,6 @@ struct ss_request;
 | 
				
			|||||||
struct hlr_iuse {
 | 
					struct hlr_iuse {
 | 
				
			||||||
	const char *name;
 | 
						const char *name;
 | 
				
			||||||
	/* call-back to be called for any incoming USSD messages for this IUSE */
 | 
						/* call-back to be called for any incoming USSD messages for this IUSE */
 | 
				
			||||||
	int (*handle_ussd)(struct ss_session *ss, const struct osmo_gsup_message *gsup, const struct ss_request *req);
 | 
						int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								   const struct osmo_gsup_message *gsup, const struct ss_request *req);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,19 +31,8 @@ enum hlr_vty_node {
 | 
				
			|||||||
	HLR_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
						HLR_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
				
			||||||
	GSUP_NODE,
 | 
						GSUP_NODE,
 | 
				
			||||||
	EUSE_NODE,
 | 
						EUSE_NODE,
 | 
				
			||||||
	MSLOOKUP_NODE,
 | 
					 | 
				
			||||||
	MSLOOKUP_SERVER_NODE,
 | 
					 | 
				
			||||||
	MSLOOKUP_SERVER_MSC_NODE,
 | 
					 | 
				
			||||||
	MSLOOKUP_CLIENT_NODE,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#define A38_XOR_MIN_KEY_LEN	12
 | 
					 | 
				
			||||||
#define A38_XOR_MAX_KEY_LEN	16
 | 
					 | 
				
			||||||
#define A38_COMP128_KEY_LEN	16
 | 
					 | 
				
			||||||
#define MILENAGE_KEY_LEN	16
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
					int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
				
			||||||
int hlr_vty_go_parent(struct vty *vty);
 | 
					int hlr_vty_go_parent(struct vty *vty);
 | 
				
			||||||
void hlr_vty_init(void *hlr_ctx);
 | 
					void hlr_vty_init(void);
 | 
				
			||||||
void dgsm_vty_init(void);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,6 @@ enum {
 | 
				
			|||||||
	DAUC,
 | 
						DAUC,
 | 
				
			||||||
	DSS,
 | 
						DSS,
 | 
				
			||||||
	DMSLOOKUP,
 | 
						DMSLOOKUP,
 | 
				
			||||||
	DLU,
 | 
					 | 
				
			||||||
	DDGSM,
 | 
					 | 
				
			||||||
	DCTRL,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const struct log_info hlr_log_info;
 | 
					extern const struct log_info hlr_log_info;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void lu_rx_gsup(struct osmo_gsup_req *req);
 | 
					 | 
				
			||||||
							
								
								
									
										81
									
								
								include/osmocom/hlr/luop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								include/osmocom/hlr/luop.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/* OsmoHLR TX/RX lu operations */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/timer.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CANCEL_TIMEOUT_SECS	30
 | 
				
			||||||
 | 
					#define ISD_TIMEOUT_SECS	30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum lu_state {
 | 
				
			||||||
 | 
						LU_S_NULL,
 | 
				
			||||||
 | 
						LU_S_LU_RECEIVED,
 | 
				
			||||||
 | 
						LU_S_CANCEL_SENT,
 | 
				
			||||||
 | 
						LU_S_CANCEL_ACK_RECEIVED,
 | 
				
			||||||
 | 
						LU_S_ISD_SENT,
 | 
				
			||||||
 | 
						LU_S_ISD_ACK_RECEIVED,
 | 
				
			||||||
 | 
						LU_S_COMPLETE,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern const struct value_string lu_state_names[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lu_operation {
 | 
				
			||||||
 | 
						/*! entry in global list of location update operations */
 | 
				
			||||||
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
						/*! to which gsup_server do we belong */
 | 
				
			||||||
 | 
						struct osmo_gsup_server *gsup_server;
 | 
				
			||||||
 | 
						/*! state of the location update */
 | 
				
			||||||
 | 
						enum lu_state state;
 | 
				
			||||||
 | 
						/*! CS (false) or PS (true) Location Update? */
 | 
				
			||||||
 | 
						bool is_ps;
 | 
				
			||||||
 | 
						/*! currently running timer */
 | 
				
			||||||
 | 
						struct osmo_timer_list timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*! subscriber related to this operation */
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						/*! peer VLR/SGSN starting the request */
 | 
				
			||||||
 | 
						uint8_t *peer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn);
 | 
				
			||||||
 | 
					void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state);
 | 
				
			||||||
 | 
					bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,
 | 
				
			||||||
 | 
							       const char *imsi);
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_by_imsi(const char *imsi,
 | 
				
			||||||
 | 
									   const struct llist_head *lst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause);
 | 
				
			||||||
 | 
					void lu_op_tx_ack(struct lu_operation *luop);
 | 
				
			||||||
 | 
					void lu_op_tx_cancel_old(struct lu_operation *luop);
 | 
				
			||||||
 | 
					void lu_op_tx_insert_subscr_data(struct lu_operation *luop);
 | 
				
			||||||
 | 
					void lu_op_tx_del_subscr_data(struct lu_operation *luop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lu_op_free(struct lu_operation *luop);
 | 
				
			||||||
@@ -1,72 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_mslookup_query;
 | 
					 | 
				
			||||||
struct osmo_mslookup_result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! mslookup service name used for roaming/proxying between osmo-hlr instances. */
 | 
					 | 
				
			||||||
#define OSMO_MSLOOKUP_SERVICE_HLR_GSUP "gsup.hlr"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! What addresses to return to mslookup queries when a subscriber is attached at the local site.
 | 
					 | 
				
			||||||
 * Mapping of service name to IP address and port. This corresponds to the VTY config for
 | 
					 | 
				
			||||||
 * 'mslookup' / 'server' [/ 'msc MSC-1-2-3'] / 'service sip.voice at 1.2.3.4 1234'.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct mslookup_service_host {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	char service[OSMO_MSLOOKUP_SERVICE_MAXLEN+1];
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str host_v4;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str host_v6;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Sets of mslookup_service_host per connected MSC.
 | 
					 | 
				
			||||||
 * When there are more than one MSC connected to this osmo-hlr, this allows keeping separate sets of service addresses
 | 
					 | 
				
			||||||
 * for each MSC. The entry with mslookup_server_msc_wildcard as MSC name is used for all MSCs (if no match for that
 | 
					 | 
				
			||||||
 * particular MSC is found). This corresponds to the VTY config for
 | 
					 | 
				
			||||||
 * 'mslookup' / 'server' / 'msc MSC-1-2-3'.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct mslookup_server_msc_cfg {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name name;
 | 
					 | 
				
			||||||
	struct llist_head service_hosts;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
							      bool create);
 | 
					 | 
				
			||||||
int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
				    const struct osmo_sockaddr_str *addr);
 | 
					 | 
				
			||||||
int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
				    const struct osmo_sockaddr_str *addr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct osmo_ipa_name mslookup_server_msc_wildcard;
 | 
					 | 
				
			||||||
struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr();
 | 
					 | 
				
			||||||
void mslookup_server_rx(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
			     struct osmo_mslookup_result *result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
				 uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name,
 | 
					 | 
				
			||||||
				 char *ret_imsi, size_t ret_imsi_len);
 | 
					 | 
				
			||||||
@@ -1,36 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/sockaddr_str.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mdns_sock.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_mslookup_server_mdns {
 | 
					 | 
				
			||||||
	struct osmo_mslookup_server *mslookup;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str bind_addr;
 | 
					 | 
				
			||||||
	char *domain_suffix;
 | 
					 | 
				
			||||||
	struct osmo_mdns_sock *sock;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_mslookup_server_mdns *osmo_mslookup_server_mdns_start(void *ctx, const struct osmo_sockaddr_str *bind_addr,
 | 
					 | 
				
			||||||
								  const char *domain_suffix);
 | 
					 | 
				
			||||||
void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server);
 | 
					 | 
				
			||||||
void mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
@@ -1,95 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/sockaddr_str.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/timestamp.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_req;
 | 
					 | 
				
			||||||
struct remote_hlr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct proxy {
 | 
					 | 
				
			||||||
	struct llist_head subscr_list;
 | 
					 | 
				
			||||||
	struct llist_head pending_gsup_reqs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* When messages arrive back from a remote HLR that this is the proxy for, reach the VLR to forward the response
 | 
					 | 
				
			||||||
	 * to via this osmo_gsup_server. */
 | 
					 | 
				
			||||||
	struct osmo_gsup_server *gsup_server_to_vlr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* How long to keep proxy entries without a refresh, in seconds. */
 | 
					 | 
				
			||||||
	uint32_t fresh_time;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* How often to garbage collect the proxy cache, period in seconds.
 | 
					 | 
				
			||||||
	 * To change this and take effect immediately, rather use proxy_set_gc_period(). */
 | 
					 | 
				
			||||||
	uint32_t gc_period;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct osmo_timer_list gc_timer;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct proxy_subscr_domain_state {
 | 
					 | 
				
			||||||
	struct osmo_ipa_name vlr_name;
 | 
					 | 
				
			||||||
	timestamp_t last_lu;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* The name from which an Update Location Request was received. Copied to vlr_name as soon as the LU is
 | 
					 | 
				
			||||||
	 * completed successfully. */
 | 
					 | 
				
			||||||
	struct osmo_ipa_name vlr_name_preliminary;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Set if this is a middle proxy, i.e. a proxy behind another proxy.
 | 
					 | 
				
			||||||
	 * That is mostly to know whether the MS is attached at a local MSC/SGSN or further away.
 | 
					 | 
				
			||||||
	 * It could be a boolean, but store the full name for logging. Set only at successful LU acceptance. */
 | 
					 | 
				
			||||||
	struct osmo_ipa_name vlr_via_proxy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct proxy_subscr {
 | 
					 | 
				
			||||||
	char imsi[GSM23003_IMSI_MAX_DIGITS+1];
 | 
					 | 
				
			||||||
	char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str remote_hlr_addr;
 | 
					 | 
				
			||||||
	struct proxy_subscr_domain_state cs, ps;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr);
 | 
					 | 
				
			||||||
void proxy_del(struct proxy *proxy);
 | 
					 | 
				
			||||||
void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The API to access / modify proxy entries keeps the implementation opaque, to make sure that we can easily move proxy
 | 
					 | 
				
			||||||
 * storage to SQLite db. */
 | 
					 | 
				
			||||||
int proxy_subscr_get_by_imsi(struct proxy_subscr *dst, struct proxy *proxy, const char *imsi);
 | 
					 | 
				
			||||||
int proxy_subscr_get_by_msisdn(struct proxy_subscr *dst, struct proxy *proxy, const char *msisdn);
 | 
					 | 
				
			||||||
void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sockaddr_str *remote_hlr_addr,
 | 
					 | 
				
			||||||
				     bool (*yield)(struct proxy *proxy, const struct proxy_subscr *subscr, void *data),
 | 
					 | 
				
			||||||
				     void *data);
 | 
					 | 
				
			||||||
int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr);
 | 
					 | 
				
			||||||
int proxy_subscr_del(struct proxy *proxy, const char *imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				       struct osmo_gsup_req *req);
 | 
					 | 
				
			||||||
void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
						 struct remote_hlr *remote_hlr, struct osmo_gsup_req *req);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				      const struct osmo_sockaddr_str *remote_hlr_addr);
 | 
					 | 
				
			||||||
void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				struct remote_hlr *remote_hlr);
 | 
					 | 
				
			||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/sockaddr_str.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_client;
 | 
					 | 
				
			||||||
struct osmo_gsup_message;
 | 
					 | 
				
			||||||
struct osmo_gsup_req;
 | 
					 | 
				
			||||||
struct msgb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_REMOTE_HLR(remote_hlr, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGP(DDGSM, level, "(Proxy HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \
 | 
					 | 
				
			||||||
	     OSMO_SOCKADDR_STR_FMT_ARGS((remote_hlr) ? &(remote_hlr)->addr : NULL), ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_REMOTE_HLR_MSG(remote_hlr, gsup_msg, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOG_REMOTE_HLR(remote_hlr, level, "%s: " fmt, osmo_gsup_message_type_name((gsup_msg)->message_type), ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* GSUP client link for proxying to a remote HLR. */
 | 
					 | 
				
			||||||
struct remote_hlr {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
	struct osmo_gsup_client *gsupc;
 | 
					 | 
				
			||||||
	struct llist_head pending_up_callbacks;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Receive a remote_hlr address when connecting succeeded, or remote_hlr == NULL on error.
 | 
					 | 
				
			||||||
 * \param addr  GSUP IP address and port for which the connection was requested.
 | 
					 | 
				
			||||||
 * \param remote_hlr  The connected remote_hlr ready for sending, or NULL if connecting failed.
 | 
					 | 
				
			||||||
 * \param data  Same a passed to remote_hlr_get_or_connect(). */
 | 
					 | 
				
			||||||
typedef void (*remote_hlr_connect_result_cb_t)(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect,
 | 
					 | 
				
			||||||
					     remote_hlr_connect_result_cb_t connect_result_cb, void *data);
 | 
					 | 
				
			||||||
void remote_hlr_destroy(struct remote_hlr *remote_hlr);
 | 
					 | 
				
			||||||
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg);
 | 
					 | 
				
			||||||
void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req,
 | 
					 | 
				
			||||||
					   struct osmo_gsup_message *modified_gsup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool remote_hlr_is_up(struct remote_hlr *remote_hlr);
 | 
					 | 
				
			||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/>.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <sys/time.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef time_t timestamp_t;
 | 
					 | 
				
			||||||
void timestamp_update(timestamp_t *timestamp);
 | 
					 | 
				
			||||||
bool timestamp_age(const timestamp_t *timestamp, uint32_t *age);
 | 
					 | 
				
			||||||
@@ -71,7 +71,7 @@ struct osmo_mdns_rfc_header {
 | 
				
			|||||||
	uint16_t nscount; /* Number of authority records */
 | 
						uint16_t nscount; /* Number of authority records */
 | 
				
			||||||
	uint16_t arcount; /* Number of additional records */
 | 
						uint16_t arcount; /* Number of additional records */
 | 
				
			||||||
#elif OSMO_IS_BIG_ENDIAN
 | 
					#elif OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
					/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
 | 
				
			||||||
	uint16_t id;
 | 
						uint16_t id;
 | 
				
			||||||
	uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1;
 | 
						uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1;
 | 
				
			||||||
	uint8_t ra:1, z:3, rcode:4;
 | 
						uint8_t ra:1, z:3, rcode:4;
 | 
				
			||||||
@@ -99,12 +99,15 @@ struct osmo_mdns_rfc_record {
 | 
				
			|||||||
	uint8_t *rdata;
 | 
						uint8_t *rdata;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain);
 | 
				
			||||||
 | 
					char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr);
 | 
					void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr);
 | 
				
			||||||
int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr);
 | 
					int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int osmo_mdns_rfc_question_encode(struct msgb *msg, const struct osmo_mdns_rfc_question *qst);
 | 
					int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst);
 | 
				
			||||||
struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len);
 | 
					struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_record *rec);
 | 
					int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec);
 | 
				
			||||||
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
 | 
					struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
 | 
				
			||||||
							 size_t *record_len);
 | 
												 size_t *record_len);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								sql/hlr.sql
									
									
									
									
									
								
							@@ -43,12 +43,7 @@ CREATE TABLE subscriber (
 | 
				
			|||||||
	-- Timestamp of last location update seen from subscriber
 | 
						-- Timestamp of last location update seen from subscriber
 | 
				
			||||||
	-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
						-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
				
			||||||
	last_lu_seen TIMESTAMP default NULL,
 | 
						last_lu_seen TIMESTAMP default NULL,
 | 
				
			||||||
	last_lu_seen_ps TIMESTAMP default NULL,
 | 
						last_lu_seen_ps TIMESTAMP default NULL
 | 
				
			||||||
 | 
					 | 
				
			||||||
	-- When a LU was received via a proxy, that proxy's hlr_number is stored here,
 | 
					 | 
				
			||||||
	-- while vlr_number reflects the MSC on the far side of that proxy.
 | 
					 | 
				
			||||||
	vlr_via_proxy	VARCHAR,
 | 
					 | 
				
			||||||
	sgsn_via_proxy	VARCHAR
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE subscriber_apn (
 | 
					CREATE TABLE subscriber_apn (
 | 
				
			||||||
@@ -79,16 +74,8 @@ CREATE TABLE auc_3g (
 | 
				
			|||||||
	ind_bitlen	INTEGER NOT NULL DEFAULT 5
 | 
						ind_bitlen	INTEGER NOT NULL DEFAULT 5
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE ind (
 | 
					 | 
				
			||||||
	-- 3G auth IND pool to be used for this VLR
 | 
					 | 
				
			||||||
	ind     INTEGER PRIMARY KEY,
 | 
					 | 
				
			||||||
	-- VLR identification, usually the GSUP source_name
 | 
					 | 
				
			||||||
	vlr     TEXT NOT NULL,
 | 
					 | 
				
			||||||
	UNIQUE (vlr)
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
					CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Set HLR database schema version number
 | 
					-- Set HLR database schema version number
 | 
				
			||||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
					-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
				
			||||||
PRAGMA user_version = 6;
 | 
					PRAGMA user_version = 4;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ AM_CFLAGS = \
 | 
				
			|||||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
						$(LIBOSMOGSM_CFLAGS) \
 | 
				
			||||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
						$(LIBOSMOVTY_CFLAGS) \
 | 
				
			||||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
						$(LIBOSMOCTRL_CFLAGS) \
 | 
				
			||||||
	$(LIBOSMOMSLOOKUP_CFLAGS) \
 | 
					 | 
				
			||||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
						$(LIBOSMOABIS_CFLAGS) \
 | 
				
			||||||
	$(SQLITE3_CFLAGS) \
 | 
						$(SQLITE3_CFLAGS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
@@ -42,6 +41,7 @@ osmo_hlr_SOURCES = \
 | 
				
			|||||||
	auc.c \
 | 
						auc.c \
 | 
				
			||||||
	ctrl.c \
 | 
						ctrl.c \
 | 
				
			||||||
	db.c \
 | 
						db.c \
 | 
				
			||||||
 | 
						luop.c \
 | 
				
			||||||
	db_auc.c \
 | 
						db_auc.c \
 | 
				
			||||||
	db_hlr.c \
 | 
						db_hlr.c \
 | 
				
			||||||
	gsup_router.c \
 | 
						gsup_router.c \
 | 
				
			||||||
@@ -53,24 +53,13 @@ osmo_hlr_SOURCES = \
 | 
				
			|||||||
	hlr_vty_subscr.c \
 | 
						hlr_vty_subscr.c \
 | 
				
			||||||
	gsup_send.c \
 | 
						gsup_send.c \
 | 
				
			||||||
	hlr_ussd.c \
 | 
						hlr_ussd.c \
 | 
				
			||||||
	proxy.c \
 | 
					 | 
				
			||||||
	dgsm.c \
 | 
					 | 
				
			||||||
	remote_hlr.c \
 | 
					 | 
				
			||||||
	lu_fsm.c \
 | 
					 | 
				
			||||||
	timestamp.c \
 | 
					 | 
				
			||||||
	mslookup_server.c \
 | 
					 | 
				
			||||||
	mslookup_server_mdns.c \
 | 
					 | 
				
			||||||
	dgsm_vty.c \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo_hlr_LDADD = \
 | 
					osmo_hlr_LDADD = \
 | 
				
			||||||
	$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
 | 
					 | 
				
			||||||
	$(top_builddir)/src/mslookup/libosmo-mslookup.la \
 | 
					 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
	$(LIBOSMOGSM_LIBS) \
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
	$(LIBOSMOVTY_LIBS) \
 | 
						$(LIBOSMOVTY_LIBS) \
 | 
				
			||||||
	$(LIBOSMOCTRL_LIBS) \
 | 
						$(LIBOSMOCTRL_LIBS) \
 | 
				
			||||||
	$(LIBOSMOMSLOOKUP_LIBS) \
 | 
					 | 
				
			||||||
	$(LIBOSMOABIS_LIBS) \
 | 
						$(LIBOSMOABIS_LIBS) \
 | 
				
			||||||
	$(SQLITE3_LIBS) \
 | 
						$(SQLITE3_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
@@ -82,7 +71,6 @@ osmo_hlr_db_tool_SOURCES = \
 | 
				
			|||||||
	logging.c \
 | 
						logging.c \
 | 
				
			||||||
	rand_urandom.c \
 | 
						rand_urandom.c \
 | 
				
			||||||
	dbd_decode_binary.c \
 | 
						dbd_decode_binary.c \
 | 
				
			||||||
	$(srcdir)/gsupclient/cni_peer_id.c \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo_hlr_db_tool_LDADD = \
 | 
					osmo_hlr_db_tool_LDADD = \
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										402
									
								
								src/ctrl.c
									
									
									
									
									
								
							
							
						
						
									
										402
									
								
								src/ctrl.c
									
									
									
									
									
								
							@@ -31,16 +31,12 @@
 | 
				
			|||||||
#include <osmocom/hlr/hlr.h>
 | 
					#include <osmocom/hlr/hlr.h>
 | 
				
			||||||
#include <osmocom/hlr/ctrl.h>
 | 
					#include <osmocom/hlr/ctrl.h>
 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
#include <osmocom/hlr/hlr_vty.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SEL_BY "by-"
 | 
					#define SEL_BY "by-"
 | 
				
			||||||
#define SEL_BY_IMSI SEL_BY "imsi-"
 | 
					#define SEL_BY_IMSI SEL_BY "imsi-"
 | 
				
			||||||
#define SEL_BY_MSISDN SEL_BY "msisdn-"
 | 
					#define SEL_BY_MSISDN SEL_BY "msisdn-"
 | 
				
			||||||
#define SEL_BY_ID SEL_BY "id-"
 | 
					#define SEL_BY_ID SEL_BY "id-"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
 | 
					 | 
				
			||||||
			    int *minlen, int *maxlen);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
					#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool startswith(const char *str, const char *start)
 | 
					static bool startswith(const char *str, const char *start)
 | 
				
			||||||
@@ -201,77 +197,6 @@ static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
 | 
				
			|||||||
		aud->u.umts.sqn);
 | 
							aud->u.umts.sqn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CTRL_CMD_DEFINE_WO_NOVRF(subscr_create, "create");
 | 
					 | 
				
			||||||
static int set_subscr_create(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *imsi = cmd->value;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_imsi_str_valid(imsi)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Invalid IMSI value.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Create the subscriber in the DB */
 | 
					 | 
				
			||||||
	rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		if (rc == -EEXIST)
 | 
					 | 
				
			||||||
			cmd->reply = "Subscriber already exists.";
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			cmd->reply = "Cannot create subscriber.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGP(DCTRL, LOGL_INFO, "Created subscriber IMSI='%s'\n",
 | 
					 | 
				
			||||||
	     imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Retrieve data of newly created subscriber: */
 | 
					 | 
				
			||||||
	rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
 | 
					 | 
				
			||||||
	if (rc < 0) {
 | 
					 | 
				
			||||||
		cmd->reply = "Failed retrieving ID of newly created subscriber.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CTRL_CMD_DEFINE_WO_NOVRF(subscr_delete, "delete");
 | 
					 | 
				
			||||||
static int set_subscr_delete(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *imsi = cmd->value;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_imsi_str_valid(imsi)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Invalid IMSI value.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Retrieve data of newly created subscriber: */
 | 
					 | 
				
			||||||
	rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
 | 
					 | 
				
			||||||
	if (rc < 0) {
 | 
					 | 
				
			||||||
		cmd->reply = "Subscriber doesn't exist.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Create the subscriber in the DB */
 | 
					 | 
				
			||||||
	rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		cmd->reply = "Cannot delete subscriber.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGP(DCTRL, LOGL_INFO, "Deleted subscriber IMSI='%s'\n",
 | 
					 | 
				
			||||||
	     imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CTRL_CMD_DEFINE_RO(subscr_info, "info");
 | 
					CTRL_CMD_DEFINE_RO(subscr_info, "info");
 | 
				
			||||||
static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
 | 
					static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -426,302 +351,17 @@ static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
 | 
				
			|||||||
	return set_subscr_cs_ps_enabled(cmd, data, false);
 | 
						return set_subscr_cs_ps_enabled(cmd, data, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
 | 
					int hlr_ctrl_cmds_install()
 | 
				
			||||||
static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
						int rc = 0;
 | 
				
			||||||
	if (!value)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (strlen(value) > sizeof(subscr.msisdn) - 1)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (strcmp(value, "none") != 0 && !osmo_msisdn_str_valid(value))
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int get_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
						rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
						rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
 | 
				
			||||||
 | 
						rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
 | 
				
			||||||
 | 
						rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
 | 
				
			||||||
 | 
						rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strlen(subscr.msisdn) == 0)
 | 
						return rc;
 | 
				
			||||||
		snprintf(subscr.msisdn, sizeof(subscr.msisdn), "none");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = talloc_asprintf(cmd, "%s", subscr.msisdn);
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int set_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
	const char *msisdn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(cmd->value, "none") == 0)
 | 
					 | 
				
			||||||
		msisdn = NULL;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		msisdn = cmd->value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Update MSISDN failed";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = "OK";
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* value format: <algo[,KI]> */
 | 
					 | 
				
			||||||
CTRL_CMD_DEFINE(subscr_aud2g, "aud2g");
 | 
					 | 
				
			||||||
static int verify_subscr_aud2g(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!value)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int get_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud3g_unused;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g_unused, NULL);
 | 
					 | 
				
			||||||
	switch (rc) {
 | 
					 | 
				
			||||||
	case 0:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case -ENOENT:
 | 
					 | 
				
			||||||
	case -ENOKEY:
 | 
					 | 
				
			||||||
		aud2g.algo = OSMO_AUTH_ALG_NONE;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		cmd->reply = "Error retrieving data from database.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (aud2g.algo ==  OSMO_AUTH_ALG_NONE) {
 | 
					 | 
				
			||||||
		cmd->reply = "none";
 | 
					 | 
				
			||||||
		return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = talloc_asprintf(cmd, "%s,%s", osmo_auth_alg_name(aud2g.algo),
 | 
					 | 
				
			||||||
				     hexdump_buf(aud2g.u.gsm.ki));
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
	char *tmp = NULL, *tok, *saveptr;
 | 
					 | 
				
			||||||
	int minlen = 0;
 | 
					 | 
				
			||||||
	int maxlen = 0;
 | 
					 | 
				
			||||||
	struct sub_auth_data_str aud2g = {
 | 
					 | 
				
			||||||
		.type = OSMO_AUTH_TYPE_GSM
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tmp = talloc_strdup(cmd, cmd->value);
 | 
					 | 
				
			||||||
	if (!tmp) {
 | 
					 | 
				
			||||||
		cmd->reply = "OOM";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Parse alg_type: */
 | 
					 | 
				
			||||||
	tok = strtok_r(tmp, ",", &saveptr);
 | 
					 | 
				
			||||||
	if (!tok) {
 | 
					 | 
				
			||||||
		cmd->reply = "Invalid format";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (strcmp(tok, "none") == 0) {
 | 
					 | 
				
			||||||
		aud2g.algo = OSMO_AUTH_ALG_NONE;
 | 
					 | 
				
			||||||
	} else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Unknown auth algorithm.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (aud2g.algo != OSMO_AUTH_ALG_NONE) {
 | 
					 | 
				
			||||||
		tok = strtok_r(NULL, "\0", &saveptr);
 | 
					 | 
				
			||||||
		if (!tok) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		aud2g.u.gsm.ki = tok;
 | 
					 | 
				
			||||||
		if (!osmo_is_hexstr(aud2g.u.gsm.ki, minlen * 2, maxlen * 2, true)) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid KI.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Update aud2g failed.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = "OK";
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* value format: <algo[,KI,(op|opc),OP_C[,ind_bitlen]]> */
 | 
					 | 
				
			||||||
CTRL_CMD_DEFINE(subscr_aud3g, "aud3g");
 | 
					 | 
				
			||||||
static int verify_subscr_aud3g(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!value)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int get_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud2g_unused;
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud3g;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g_unused, &aud3g, NULL);
 | 
					 | 
				
			||||||
	switch (rc) {
 | 
					 | 
				
			||||||
	case 0:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case -ENOENT:
 | 
					 | 
				
			||||||
	case -ENOKEY:
 | 
					 | 
				
			||||||
		aud3g.algo = OSMO_AUTH_ALG_NONE;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		cmd->reply = "Error retrieving data from database.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (aud3g.algo ==  OSMO_AUTH_ALG_NONE) {
 | 
					 | 
				
			||||||
		cmd->reply = "none";
 | 
					 | 
				
			||||||
		return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = talloc_asprintf(cmd, "%s,%s,%s,%s,%u", osmo_auth_alg_name(aud3g.algo),
 | 
					 | 
				
			||||||
				     osmo_hexdump_nospc_c(cmd, aud3g.u.umts.k, sizeof(aud3g.u.umts.k)),
 | 
					 | 
				
			||||||
				     aud3g.u.umts.opc_is_op ? "OP" : "OPC",
 | 
					 | 
				
			||||||
				     osmo_hexdump_nospc_c(cmd, aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)),
 | 
					 | 
				
			||||||
				     aud3g.u.umts.ind_bitlen);
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	struct hlr *hlr = data;
 | 
					 | 
				
			||||||
	const char *by_selector = cmd->node;
 | 
					 | 
				
			||||||
	char *tmp = NULL, *tok, *saveptr;
 | 
					 | 
				
			||||||
	int minlen = 0;
 | 
					 | 
				
			||||||
	int maxlen = 0;
 | 
					 | 
				
			||||||
	struct sub_auth_data_str aud3g = {
 | 
					 | 
				
			||||||
		.type = OSMO_AUTH_TYPE_UMTS,
 | 
					 | 
				
			||||||
		.u.umts = {
 | 
					 | 
				
			||||||
			.ind_bitlen = 5,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	bool ind_bitlen_present;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tmp = talloc_strdup(cmd, cmd->value);
 | 
					 | 
				
			||||||
	if (!tmp) {
 | 
					 | 
				
			||||||
		cmd->reply = "OOM";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Parse alg_type: */
 | 
					 | 
				
			||||||
	tok = strtok_r(tmp, ",", &saveptr);
 | 
					 | 
				
			||||||
	if (!tok) {
 | 
					 | 
				
			||||||
		cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (strcmp(tok, "none") == 0) {
 | 
					 | 
				
			||||||
		aud3g.algo = OSMO_AUTH_ALG_NONE;
 | 
					 | 
				
			||||||
	} else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Unknown auth algorithm.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (aud3g.algo != OSMO_AUTH_ALG_NONE) {
 | 
					 | 
				
			||||||
		/* Parse K */
 | 
					 | 
				
			||||||
		tok = strtok_r(NULL, ",", &saveptr);
 | 
					 | 
				
			||||||
		if (!tok) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		aud3g.u.umts.k = tok;
 | 
					 | 
				
			||||||
		if (!osmo_is_hexstr(aud3g.u.umts.k, minlen * 2, maxlen * 2, true)) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid KI.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Parse OP/OPC choice */
 | 
					 | 
				
			||||||
		tok = strtok_r(NULL, ",", &saveptr);
 | 
					 | 
				
			||||||
		if (!tok) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (strcasecmp(tok, "op") == 0) {
 | 
					 | 
				
			||||||
			aud3g.u.umts.opc_is_op = true;
 | 
					 | 
				
			||||||
		} else if (strcasecmp(tok, "opc") == 0) {
 | 
					 | 
				
			||||||
			aud3g.u.umts.opc_is_op = false;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Parse OP/OPC value */
 | 
					 | 
				
			||||||
		ind_bitlen_present = !!strchr(saveptr, ',');
 | 
					 | 
				
			||||||
		tok = strtok_r(NULL, ind_bitlen_present ? "," : "\0", &saveptr);
 | 
					 | 
				
			||||||
		if (!tok) {
 | 
					 | 
				
			||||||
			cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		aud3g.u.umts.opc = tok;
 | 
					 | 
				
			||||||
		if (!osmo_is_hexstr(aud3g.u.umts.opc, MILENAGE_KEY_LEN * 2, MILENAGE_KEY_LEN * 2, true)) {
 | 
					 | 
				
			||||||
			cmd->reply = talloc_asprintf(cmd, "Invalid OP/OPC.");
 | 
					 | 
				
			||||||
			return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (ind_bitlen_present) {
 | 
					 | 
				
			||||||
			/* Parse bitlen_ind */
 | 
					 | 
				
			||||||
			tok = strtok_r(NULL, "\0", &saveptr);
 | 
					 | 
				
			||||||
			if (!tok || tok[0] == '\0') {
 | 
					 | 
				
			||||||
				cmd->reply = "Invalid format.";
 | 
					 | 
				
			||||||
				return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			aud3g.u.umts.ind_bitlen = atoi(tok);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g)) {
 | 
					 | 
				
			||||||
		cmd->reply = "Update aud3g failed.";
 | 
					 | 
				
			||||||
		return CTRL_CMD_ERROR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd->reply = "OK";
 | 
					 | 
				
			||||||
	return CTRL_CMD_REPLY;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
					static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
				
			||||||
@@ -749,30 +389,14 @@ static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
				
			|||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int hlr_ctrl_cmds_install(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_create);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_delete);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_msisdn);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud2g);
 | 
					 | 
				
			||||||
	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud3g);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
 | 
					struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	struct ctrl_handle *hdl = ctrl_interface_setup2(hlr, OSMO_CTRL_PORT_HLR, hlr_ctrl_node_lookup,
 | 
						struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr,
 | 
				
			||||||
							_LAST_CTRL_NODE_HLR);
 | 
												      hlr->ctrl_bind_addr,
 | 
				
			||||||
 | 
												      OSMO_CTRL_PORT_HLR,
 | 
				
			||||||
 | 
												      hlr_ctrl_node_lookup,
 | 
				
			||||||
 | 
												      _LAST_CTRL_NODE_HLR);
 | 
				
			||||||
	if (!hdl)
 | 
						if (!hdl)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										88
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								src/db.c
									
									
									
									
									
								
							@@ -28,7 +28,7 @@
 | 
				
			|||||||
#include "db_bootstrap.h"
 | 
					#include "db_bootstrap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
					/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
				
			||||||
#define CURRENT_SCHEMA_VERSION	6
 | 
					#define CURRENT_SCHEMA_VERSION	4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SEL_COLUMNS \
 | 
					#define SEL_COLUMNS \
 | 
				
			||||||
	"id," \
 | 
						"id," \
 | 
				
			||||||
@@ -46,25 +46,15 @@
 | 
				
			|||||||
	"ms_purged_cs," \
 | 
						"ms_purged_cs," \
 | 
				
			||||||
	"ms_purged_ps," \
 | 
						"ms_purged_ps," \
 | 
				
			||||||
	"last_lu_seen," \
 | 
						"last_lu_seen," \
 | 
				
			||||||
	"last_lu_seen_ps," \
 | 
						"last_lu_seen_ps" \
 | 
				
			||||||
	"vlr_via_proxy," \
 | 
					 | 
				
			||||||
	"sgsn_via_proxy"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *stmt_sql[] = {
 | 
					static const char *stmt_sql[] = {
 | 
				
			||||||
	[DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "
 | 
					 | 
				
			||||||
		"WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_FILTER_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei LIKE $search ORDER BY imei",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",
 | 
					 | 
				
			||||||
	[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
						[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
				
			||||||
	[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 | 
						[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 | 
				
			||||||
	[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
 | 
						[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
 | 
				
			||||||
	[DB_STMT_SEL_BY_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei = ?",
 | 
						[DB_STMT_SEL_BY_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei = ?",
 | 
				
			||||||
	[DB_STMT_UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = $number, vlr_via_proxy = $proxy WHERE id = $subscriber_id",
 | 
						[DB_STMT_UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = $number WHERE id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number, sgsn_via_proxy = $proxy WHERE id = $subscriber_id",
 | 
						[DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number WHERE id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_UPD_IMEI_BY_IMSI] = "UPDATE subscriber SET imei = $imei WHERE imsi = $imsi",
 | 
						[DB_STMT_UPD_IMEI_BY_IMSI] = "UPDATE subscriber SET imei = $imei WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_AUC_BY_IMSI] =
 | 
						[DB_STMT_AUC_BY_IMSI] =
 | 
				
			||||||
		"SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen"
 | 
							"SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen"
 | 
				
			||||||
@@ -93,9 +83,6 @@ static const char *stmt_sql[] = {
 | 
				
			|||||||
	[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
 | 
						[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
 | 
						[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
 | 
						[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
 | 
				
			||||||
	[DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)",
 | 
					 | 
				
			||||||
	[DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr",
 | 
					 | 
				
			||||||
	[DB_STMT_IND_DEL] = "DELETE FROM ind WHERE vlr = $vlr",
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
					static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
				
			||||||
@@ -196,25 +183,6 @@ bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr)
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool db_bind_null(sqlite3_stmt *stmt, const char *param_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1;
 | 
					 | 
				
			||||||
	if (idx < 1) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n",
 | 
					 | 
				
			||||||
		     param_name);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rc = sqlite3_bind_null(stmt, idx);
 | 
					 | 
				
			||||||
	if (rc != SQLITE_OK) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Error binding NULL to SQL parameter %s: %d\n",
 | 
					 | 
				
			||||||
		     param_name ? param_name : "#1", rc);
 | 
					 | 
				
			||||||
		db_remove_reset(stmt);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void db_close(struct db_context *dbc)
 | 
					void db_close(struct db_context *dbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
@@ -237,7 +205,7 @@ void db_close(struct db_context *dbc)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
 | 
					static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc = 0;
 | 
						int rc;
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
	for (i = 0; i < statements_count; i++) {
 | 
						for (i = 0; i < statements_count; i++) {
 | 
				
			||||||
		const char *stmt_str = statements[i];
 | 
							const char *stmt_str = statements[i];
 | 
				
			||||||
@@ -473,54 +441,12 @@ static int db_upgrade_v4(struct db_context *dbc)
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int db_upgrade_v5(struct db_context *dbc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	const char *statements[] = {
 | 
					 | 
				
			||||||
		"ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR",
 | 
					 | 
				
			||||||
		"ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR",
 | 
					 | 
				
			||||||
		"PRAGMA user_version = 5",
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
 | 
					 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 5\n");
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int db_upgrade_v6(struct db_context *dbc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	const char *statements[] = {
 | 
					 | 
				
			||||||
		"CREATE TABLE ind (\n"
 | 
					 | 
				
			||||||
		"	-- 3G auth IND pool to be used for this VLR\n"
 | 
					 | 
				
			||||||
		"	ind     INTEGER PRIMARY KEY,\n"
 | 
					 | 
				
			||||||
		"	-- VLR identification, usually the GSUP source_name\n"
 | 
					 | 
				
			||||||
		"	vlr     TEXT NOT NULL,\n"
 | 
					 | 
				
			||||||
		"	UNIQUE (vlr)\n"
 | 
					 | 
				
			||||||
		")"
 | 
					 | 
				
			||||||
		,
 | 
					 | 
				
			||||||
		"PRAGMA user_version = 6",
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
 | 
					 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 6\n");
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef int (*db_upgrade_func_t)(struct db_context *dbc);
 | 
					typedef int (*db_upgrade_func_t)(struct db_context *dbc);
 | 
				
			||||||
static db_upgrade_func_t db_upgrade_path[] = {
 | 
					static db_upgrade_func_t db_upgrade_path[] = {
 | 
				
			||||||
	db_upgrade_v1,
 | 
						db_upgrade_v1,
 | 
				
			||||||
	db_upgrade_v2,
 | 
						db_upgrade_v2,
 | 
				
			||||||
	db_upgrade_v3,
 | 
						db_upgrade_v3,
 | 
				
			||||||
	db_upgrade_v4,
 | 
						db_upgrade_v4,
 | 
				
			||||||
	db_upgrade_v5,
 | 
					 | 
				
			||||||
	db_upgrade_v6,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int db_get_user_version(struct db_context *dbc)
 | 
					static int db_get_user_version(struct db_context *dbc)
 | 
				
			||||||
@@ -609,11 +535,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	char *err_msg;
 | 
						char *err_msg;
 | 
				
			||||||
	rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg);
 | 
						rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg);
 | 
				
			||||||
	if (rc != SQLITE_OK) {
 | 
						if (rc != SQLITE_OK)
 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
 | 
				
			||||||
			err_msg);
 | 
								err_msg);
 | 
				
			||||||
		sqlite3_free(err_msg);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	version = db_get_user_version(dbc);
 | 
						version = db_get_user_version(dbc);
 | 
				
			||||||
	if (version < 0) {
 | 
						if (version < 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/db_auc.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/db_auc.c
									
									
									
									
									
								
							@@ -95,10 +95,7 @@ static int hexparse_stmt(uint8_t *dst, size_t dst_len, sqlite3_stmt *stmt, int c
 | 
				
			|||||||
		LOGAUC(imsi, LOGL_ERROR, "Error reading %s\n", col_name);
 | 
							LOGAUC(imsi, LOGL_ERROR, "Error reading %s\n", col_name);
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						osmo_hexparse((void *)text, dst, dst_len);
 | 
				
			||||||
	if (osmo_hexparse((void *)text, dst, dst_len) != col_len)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -203,16 +200,15 @@ int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
				
			|||||||
	if (rc)
 | 
						if (rc)
 | 
				
			||||||
		return rc;
 | 
							return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* modulo by the IND bitlen value range. For example, ind_bitlen == 5 would modulo 32:
 | 
					 | 
				
			||||||
	 * 1 << 5 == 0b0100000 == 32
 | 
					 | 
				
			||||||
	 * - 1 == 0b0011111 == bitmask of 5 lowest bits
 | 
					 | 
				
			||||||
	 * x &= 0b0011111 == modulo 32
 | 
					 | 
				
			||||||
	 * Why do this? osmo-hlr cannot possibly choose individual VLR INDs always matching all subscribers' IND_bitlen,
 | 
					 | 
				
			||||||
	 * which might vary wildly. Instead, let hlr.c pass in an arbitrarily high number here, and the modulo does a
 | 
					 | 
				
			||||||
	 * round-robin if the IND pools that this subscriber has available. */
 | 
					 | 
				
			||||||
	auc_3g_ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
 | 
					 | 
				
			||||||
	aud3g.u.umts.ind = auc_3g_ind;
 | 
						aud3g.u.umts.ind = auc_3g_ind;
 | 
				
			||||||
 | 
						if (aud3g.type == OSMO_AUTH_TYPE_UMTS
 | 
				
			||||||
 | 
						    && aud3g.u.umts.ind >= (1U << aud3g.u.umts.ind_bitlen)) {
 | 
				
			||||||
 | 
							LOGAUC(imsi, LOGL_NOTICE, "3G auth: SQN's IND bitlen %u is"
 | 
				
			||||||
 | 
							       " too small to hold an index of %u. Truncating. This"
 | 
				
			||||||
 | 
							       " may cause numerous additional AUTS resyncing.\n",
 | 
				
			||||||
 | 
							       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind);
 | 
				
			||||||
 | 
							aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	/* the first bit (bit0) cannot be used as AMF anymore, but has been
 | 
						/* the first bit (bit0) cannot be used as AMF anymore, but has been
 | 
				
			||||||
	 * re-appropriated as the separation bit.  See 3GPP TS 33.102 Annex H
 | 
						 * re-appropriated as the separation bit.  See 3GPP TS 33.102 Annex H
 | 
				
			||||||
	 * together with 3GPP TS 33.401 / 33.402 / 33.501 */
 | 
						 * together with 3GPP TS 33.401 / 33.402 / 33.501 */
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										251
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								src/db_hlr.c
									
									
									
									
									
								
							@@ -28,7 +28,6 @@
 | 
				
			|||||||
#include <time.h>
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/utils.h>
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
#include <osmocom/core/timer.h>
 | 
					 | 
				
			||||||
#include <osmocom/crypt/auth.h>
 | 
					#include <osmocom/crypt/auth.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					#include <osmocom/gsm/gsm23003.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,7 +36,8 @@
 | 
				
			|||||||
#include <osmocom/hlr/logging.h>
 | 
					#include <osmocom/hlr/logging.h>
 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					#include <osmocom/hlr/hlr.h>
 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/luop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LOGHLR(imsi, level, fmt, args ...)	LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
 | 
					#define LOGHLR(imsi, level, fmt, args ...)	LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,8 +45,7 @@
 | 
				
			|||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] imsi  ASCII string of IMSI digits, is validated.
 | 
					 * \param[in] imsi  ASCII string of IMSI digits, is validated.
 | 
				
			||||||
 * \param[in] flags  Bitmask of DB_SUBSCR_FLAG_*.
 | 
					 * \param[in] flags  Bitmask of DB_SUBSCR_FLAG_*.
 | 
				
			||||||
 * \returns 0 on success, -EINVAL on invalid IMSI, -EEXIST if subscriber with
 | 
					 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
 | 
				
			||||||
 *          provided imsi already exists, -EIO on other database errors.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
 | 
					int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -74,8 +73,6 @@ int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
 | 
				
			|||||||
	if (rc != SQLITE_DONE) {
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
		LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
 | 
							LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
 | 
				
			||||||
		       rc, sqlite3_errmsg(dbc->db));
 | 
							       rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
		if (rc == SQLITE_CONSTRAINT_UNIQUE)
 | 
					 | 
				
			||||||
			return -EEXIST;
 | 
					 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -267,11 +264,11 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
 | 
				
			|||||||
		switch (aud->algo) {
 | 
							switch (aud->algo) {
 | 
				
			||||||
		case OSMO_AUTH_ALG_NONE:
 | 
							case OSMO_AUTH_ALG_NONE:
 | 
				
			||||||
		case OSMO_AUTH_ALG_MILENAGE:
 | 
							case OSMO_AUTH_ALG_MILENAGE:
 | 
				
			||||||
		case OSMO_AUTH_ALG_XOR:
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case OSMO_AUTH_ALG_COMP128v1:
 | 
							case OSMO_AUTH_ALG_COMP128v1:
 | 
				
			||||||
		case OSMO_AUTH_ALG_COMP128v2:
 | 
							case OSMO_AUTH_ALG_COMP128v2:
 | 
				
			||||||
		case OSMO_AUTH_ALG_COMP128v3:
 | 
							case OSMO_AUTH_ALG_COMP128v3:
 | 
				
			||||||
 | 
							case OSMO_AUTH_ALG_XOR:
 | 
				
			||||||
			LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
 | 
								LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
 | 
				
			||||||
			     " auth algo not suited for 3G: %s\n",
 | 
								     " auth algo not suited for 3G: %s\n",
 | 
				
			||||||
			     osmo_auth_alg_name(aud->algo));
 | 
								     osmo_auth_alg_name(aud->algo));
 | 
				
			||||||
@@ -508,8 +505,6 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
				
			|||||||
			   subscr->imsi, "CS");
 | 
								   subscr->imsi, "CS");
 | 
				
			||||||
	parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
 | 
						parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
 | 
				
			||||||
			   subscr->imsi, "PS");
 | 
								   subscr->imsi, "PS");
 | 
				
			||||||
	copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 16);
 | 
					 | 
				
			||||||
	copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 17);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	db_remove_reset(stmt);
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
@@ -571,7 +566,7 @@ int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
				
			|||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_sel(dbc, stmt, subscr, &err);
 | 
						rc = db_sel(dbc, stmt, subscr, &err);
 | 
				
			||||||
	if (rc && rc != -ENOENT)
 | 
						if (rc)
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
 | 
				
			||||||
		     imsi, err);
 | 
							     imsi, err);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
@@ -622,100 +617,12 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
				
			|||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_sel(dbc, stmt, subscr, &err);
 | 
						rc = db_sel(dbc, stmt, subscr, &err);
 | 
				
			||||||
	if (rc && rc != -ENOENT)
 | 
						if (rc)
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
 | 
				
			||||||
		     msisdn, err);
 | 
							     msisdn, err);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Retrieve subscriber data from the HLR database.
 | 
					 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 | 
				
			||||||
 * \param[in] filter_type  ASCII string of identifier type to search.
 | 
					 | 
				
			||||||
 * \param[in] filter  ASCII string to search.
 | 
					 | 
				
			||||||
 * \param[in] get_cb  pointer to call back function for data.
 | 
					 | 
				
			||||||
 * \param[in,out] data  pointer to pass to callback function.
 | 
					 | 
				
			||||||
 * \param[in,out] count  counter for number of matched subscribers.
 | 
					 | 
				
			||||||
 * \param[in,our] err
 | 
					 | 
				
			||||||
 * \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
 | 
					 | 
				
			||||||
 *          database error.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
 | 
					 | 
				
			||||||
		   void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
 | 
					 | 
				
			||||||
		   int *count, const char **err)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
					 | 
				
			||||||
	char search[256];
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	bool show_ls = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!filter_type) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_ALL];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "imei") == 0) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMEI];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "imsi") == 0) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "msisdn") == 0) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "cs") == 0) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "ps") == 0) {
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
 | 
					 | 
				
			||||||
	} else if (strcmp(filter_type, "last_lu_seen") == 0) {
 | 
					 | 
				
			||||||
		show_ls = true;
 | 
					 | 
				
			||||||
		stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (filter_type && filter && strcmp(filter_type, "last_lu_seen") != 0) {
 | 
					 | 
				
			||||||
		if (strcmp(filter, "on") == 0) {
 | 
					 | 
				
			||||||
			sprintf(search, "%s", "1");
 | 
					 | 
				
			||||||
		} else if (strcmp(filter, "off") == 0) {
 | 
					 | 
				
			||||||
			sprintf(search, "%s", "0");
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			sprintf(search, "%%%s%%", filter);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (!db_bind_text(stmt, "$search", search)) {
 | 
					 | 
				
			||||||
			*err = sqlite3_errmsg(dbc->db);
 | 
					 | 
				
			||||||
			return -EIO;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc == SQLITE_DONE) {
 | 
					 | 
				
			||||||
		db_remove_reset(stmt);
 | 
					 | 
				
			||||||
		*err = "No matching subscriber(s)";
 | 
					 | 
				
			||||||
		return -ENOENT;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (rc == SQLITE_ROW) {
 | 
					 | 
				
			||||||
		subscr = (struct hlr_subscriber){
 | 
					 | 
				
			||||||
			  .id = sqlite3_column_int64(stmt, 0),};
 | 
					 | 
				
			||||||
		copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
 | 
					 | 
				
			||||||
		copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
 | 
					 | 
				
			||||||
		copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
 | 
					 | 
				
			||||||
		subscr.nam_cs = sqlite3_column_int(stmt, 9);
 | 
					 | 
				
			||||||
		subscr.nam_ps = sqlite3_column_int(stmt, 10);
 | 
					 | 
				
			||||||
		if (show_ls)
 | 
					 | 
				
			||||||
			parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
 | 
					 | 
				
			||||||
					   subscr.imsi, "CS");
 | 
					 | 
				
			||||||
		get_cb(&subscr, data);
 | 
					 | 
				
			||||||
		rc = sqlite3_step(stmt);
 | 
					 | 
				
			||||||
		(*count)++;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db_remove_reset(stmt);
 | 
					 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
					 | 
				
			||||||
		*err = sqlite3_errmsg(dbc->db);
 | 
					 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	*err = NULL;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Retrieve subscriber data from the HLR database.
 | 
					/*! Retrieve subscriber data from the HLR database.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] id  ID of the subscriber in the HLR db.
 | 
					 * \param[in] id  ID of the subscriber in the HLR db.
 | 
				
			||||||
@@ -734,7 +641,7 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
				
			|||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_sel(dbc, stmt, subscr, &err);
 | 
						rc = db_sel(dbc, stmt, subscr, &err);
 | 
				
			||||||
	if (rc && rc != -ENOENT)
 | 
						if (rc)
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
 | 
				
			||||||
		     id, err);
 | 
							     id, err);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
@@ -757,7 +664,7 @@ int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_s
 | 
				
			|||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_sel(dbc, stmt, subscr, &err);
 | 
						rc = db_sel(dbc, stmt, subscr, &err);
 | 
				
			||||||
	if (rc && rc != -ENOENT)
 | 
						if (rc)
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -827,8 +734,7 @@ out:
 | 
				
			|||||||
 *         -EIO on database errors.
 | 
					 *         -EIO on database errors.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const struct osmo_ipa_name *vlr_name, bool is_ps,
 | 
							 const char *vlr_or_sgsn_number, bool is_ps)
 | 
				
			||||||
		 const struct osmo_ipa_name *via_proxy)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
	int rc, ret = 0;
 | 
						int rc, ret = 0;
 | 
				
			||||||
@@ -840,17 +746,9 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			|||||||
	if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
 | 
						if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!db_bind_text(stmt, "$number", (char*)vlr_name->val))
 | 
						if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (via_proxy && via_proxy->len) {
 | 
					 | 
				
			||||||
		if (!db_bind_text(stmt, "$proxy", (char*)via_proxy->val))
 | 
					 | 
				
			||||||
			return -EIO;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (!db_bind_null(stmt, "$proxy"))
 | 
					 | 
				
			||||||
			return -EIO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* execute the statement */
 | 
						/* execute the statement */
 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
@@ -976,105 +874,50 @@ out:
 | 
				
			|||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int _db_ind_run(struct db_context *dbc, sqlite3_stmt *stmt, const char *vlr, bool reset)
 | 
					/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
 | 
				
			||||||
 | 
					 * \param[in,out] hlr  Global hlr context.
 | 
				
			||||||
 | 
					 * \param[in] subscr   Subscriber from a fresh db_subscr_get_by_*() call.
 | 
				
			||||||
 | 
					 * \param[in] nam_val  True to enable CS/PS, false to disable.
 | 
				
			||||||
 | 
					 * \param[in] is_ps    True to enable/disable PS, false for CS.
 | 
				
			||||||
 | 
					 * \returns 0 on success, ENOEXEC if there is no need to change, a negative
 | 
				
			||||||
 | 
					 *          value on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					        struct lu_operation *luop;
 | 
				
			||||||
 | 
					        struct osmo_gsup_conn *co;
 | 
				
			||||||
 | 
						bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!db_bind_text(stmt, "$vlr", vlr))
 | 
						if (is_val == nam_val) {
 | 
				
			||||||
		return -EIO;
 | 
							LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
 | 
				
			||||||
 | 
							       nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
 | 
				
			||||||
	/* execute the statement */
 | 
							return ENOEXEC;
 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
					 | 
				
			||||||
	if (reset)
 | 
					 | 
				
			||||||
		db_remove_reset(stmt);
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int _db_ind_add(struct db_context *dbc, const char *vlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_ADD];
 | 
					 | 
				
			||||||
	if (_db_ind_run(dbc, stmt, vlr, true) != SQLITE_DONE) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Cannot create IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr, -1));
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int _db_ind_del(struct db_context *dbc, const char *vlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_DEL];
 | 
					 | 
				
			||||||
	_db_ind_run(dbc, stmt, vlr, true);
 | 
					 | 
				
			||||||
	/* We don't really care about the result. If it didn't exist, then that was the goal anyway. */
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int _db_ind_get(struct db_context *dbc, const char *vlr, unsigned int *ind)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret = 0;
 | 
					 | 
				
			||||||
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_SELECT];
 | 
					 | 
				
			||||||
	int rc = _db_ind_run(dbc, stmt, vlr, false);
 | 
					 | 
				
			||||||
	if (rc == SQLITE_DONE) {
 | 
					 | 
				
			||||||
		/* Does not exist yet */
 | 
					 | 
				
			||||||
		ret = -ENOENT;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	} else if (rc != SQLITE_ROW) {
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Error executing SQL: %d\n", rc);
 | 
					 | 
				
			||||||
		ret = -EIO;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(ind);
 | 
						rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
 | 
				
			||||||
	*ind = sqlite3_column_int64(stmt, 0);
 | 
						if (rc)
 | 
				
			||||||
out:
 | 
							return rc > 0? -rc : rc;
 | 
				
			||||||
	db_remove_reset(stmt);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
 | 
						/* If we're disabling, send a notice out to the GSUP client that is
 | 
				
			||||||
	    unsigned int *ind, bool del)
 | 
						 * responsible. Otherwise no need. */
 | 
				
			||||||
{
 | 
						if (nam_val)
 | 
				
			||||||
	const char *vlr_name = NULL;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (vlr->type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') {
 | 
					 | 
				
			||||||
			LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n",
 | 
					 | 
				
			||||||
			     osmo_ipa_name_to_str(&vlr->ipa_name));
 | 
					 | 
				
			||||||
			return -ENOTSUP;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		vlr_name = (const char*)vlr->ipa_name.val;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Unsupported osmo_cni_peer_id type: %s\n",
 | 
					 | 
				
			||||||
		     osmo_cni_peer_id_type_name(vlr->type));
 | 
					 | 
				
			||||||
		return -ENOTSUP;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (del)
 | 
					 | 
				
			||||||
		return _db_ind_del(dbc, vlr_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = _db_ind_get(dbc, vlr_name, ind);
 | 
					 | 
				
			||||||
	if (!rc)
 | 
					 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Does not exist yet, create. */
 | 
						/* FIXME: only send to single SGSN where latest update for IMSI came from */
 | 
				
			||||||
	rc = _db_ind_add(dbc, vlr_name);
 | 
						llist_for_each_entry(co, &hlr->gs->clients, list) {
 | 
				
			||||||
	if (rc) {
 | 
							luop = lu_op_alloc_conn(co);
 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "Error creating IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr_name, -1));
 | 
							if (!luop) {
 | 
				
			||||||
		return rc;
 | 
								LOGHLR(subscr->imsi, LOGL_ERROR,
 | 
				
			||||||
 | 
								       "Cannot notify GSUP client, cannot allocate lu_operation,"
 | 
				
			||||||
 | 
								       " for %s:%u\n",
 | 
				
			||||||
 | 
								       co && co->conn && co->conn->server? co->conn->server->addr : "unset",
 | 
				
			||||||
 | 
								       co && co->conn && co->conn->server? co->conn->server->port : 0);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							luop->subscr = *subscr;
 | 
				
			||||||
 | 
							lu_op_tx_del_subscr_data(luop);
 | 
				
			||||||
 | 
							lu_op_free(luop);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
	/* To be sure, query again from scratch. */
 | 
					 | 
				
			||||||
	return _db_ind_get(dbc, vlr_name, ind);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return _db_ind(dbc, vlr, ind, false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return _db_ind(dbc, vlr, NULL, true);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,10 @@
 | 
				
			|||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
				
			||||||
 * Lesser General Public License for more details.
 | 
					 * Lesser General Public License for more details.
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Lesser General Public
 | 
				
			||||||
 | 
					 * License along with this library; if not, write to the Free Software
 | 
				
			||||||
 | 
					 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 * $Id: dbd_helper.c,v 1.44 2011/08/09 11:14:14 mhoenicka Exp $
 | 
					 * $Id: dbd_helper.c,v 1.44 2011/08/09 11:14:14 mhoenicka Exp $
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										247
									
								
								src/dgsm.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/dgsm.c
									
									
									
									
									
								
							@@ -1,247 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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 <errno.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup_client.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_client.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_router.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/dgsm.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/proxy.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/remote_hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server_mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/dgsm.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void *dgsm_ctx = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
 | 
					 | 
				
			||||||
				  uint32_t request_handle,
 | 
					 | 
				
			||||||
				  const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
				  const struct osmo_mslookup_result *result)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy *proxy = g_hlr->gs->proxy;
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr;
 | 
					 | 
				
			||||||
	const struct osmo_sockaddr_str *remote_hlr_addr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* A remote HLR is answering back, indicating that it is the home HLR for a given IMSI.
 | 
					 | 
				
			||||||
	 * There should be a mostly empty proxy entry for that IMSI.
 | 
					 | 
				
			||||||
	 * Add the remote address data in the proxy. */
 | 
					 | 
				
			||||||
	if (query->id.type != OSMO_MSLOOKUP_ID_IMSI) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_ERROR, "Expected IMSI ID type in mslookup query+result: %s\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, result));
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
 | 
					 | 
				
			||||||
		LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n",
 | 
					 | 
				
			||||||
			 osmo_mslookup_result_name_c(OTC_SELECT, query, result));
 | 
					 | 
				
			||||||
		proxy_subscr_del(proxy, query->id.imsi);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
 | 
					 | 
				
			||||||
		remote_hlr_addr = &result->host_v4;
 | 
					 | 
				
			||||||
	else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
 | 
					 | 
				
			||||||
		remote_hlr_addr = &result->host_v6;
 | 
					 | 
				
			||||||
	else {
 | 
					 | 
				
			||||||
		LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n",
 | 
					 | 
				
			||||||
			 osmo_mslookup_result_name_c(OTC_SELECT, query, result));
 | 
					 | 
				
			||||||
		proxy_subscr_del(proxy, query->id.imsi);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, query->id.imsi)) {
 | 
					 | 
				
			||||||
		LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n",
 | 
					 | 
				
			||||||
			 osmo_mslookup_result_name_c(OTC_SELECT, query, result));
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr_addr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Return true when the message has been handled by D-GSM. */
 | 
					 | 
				
			||||||
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr;
 | 
					 | 
				
			||||||
	struct proxy *proxy = g_hlr->gs->proxy;
 | 
					 | 
				
			||||||
	struct osmo_mslookup_query query;
 | 
					 | 
				
			||||||
	struct osmo_mslookup_query_handling handling;
 | 
					 | 
				
			||||||
	uint32_t request_handle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If the IMSI is known in the local HLR, then we won't proxy. */
 | 
					 | 
				
			||||||
	if (db_subscr_exists_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0)
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Are we already forwarding this IMSI to a remote HLR? */
 | 
					 | 
				
			||||||
	if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) {
 | 
					 | 
				
			||||||
		proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req);
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* The IMSI is not known locally, so we want to proxy to a remote HLR, but no proxy entry exists yet. We need to
 | 
					 | 
				
			||||||
	 * look up the subscriber in remote HLRs via D-GSM mslookup, forward GSUP and reply once a result is back from
 | 
					 | 
				
			||||||
	 * there.  Defer message and kick off MS lookup. */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
 | 
					 | 
				
			||||||
	proxy_subscr = (struct proxy_subscr){};
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(proxy_subscr.imsi, req->gsup.imsi);
 | 
					 | 
				
			||||||
	if (proxy_subscr_create_or_update(proxy, &proxy_subscr)) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to create proxy entry\n");
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Is a fixed gateway proxy configured? */
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) {
 | 
					 | 
				
			||||||
		proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, &g_hlr->mslookup.client.gsup_gateway_proxy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Proxy database modified, update info */
 | 
					 | 
				
			||||||
		if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi)) {
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Internal proxy error\n");
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req);
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Kick off an mslookup for the remote HLR?  This check could be up first on the top, but do it only now so that
 | 
					 | 
				
			||||||
	 * if the mslookup client disconnected, we still continue to service open proxy entries. */
 | 
					 | 
				
			||||||
	if (!osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n");
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* First spool message, then kick off mslookup. If the proxy denies this message type, then don't do anything. */
 | 
					 | 
				
			||||||
	if (proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req)) {
 | 
					 | 
				
			||||||
		/* If the proxy denied forwarding, an error response was already generated. */
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	query = (struct osmo_mslookup_query){
 | 
					 | 
				
			||||||
		.id = {
 | 
					 | 
				
			||||||
			.type = OSMO_MSLOOKUP_ID_IMSI,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi);
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
 | 
					 | 
				
			||||||
	handling = (struct osmo_mslookup_query_handling){
 | 
					 | 
				
			||||||
		.min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
 | 
					 | 
				
			||||||
		.result_cb = resolve_hlr_result_cb,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
 | 
					 | 
				
			||||||
	if (!request_handle) {
 | 
					 | 
				
			||||||
		LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n",
 | 
					 | 
				
			||||||
			 osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
 | 
					 | 
				
			||||||
		proxy_subscr_del(proxy, req->gsup.imsi);
 | 
					 | 
				
			||||||
		/* mslookup seems to not be working. Try handling it locally. */
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_init(void *ctx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	dgsm_ctx = talloc_named_const(ctx, 0, "dgsm");
 | 
					 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.local_attach_max_age = 60 * 60;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.result_timeout_milliseconds = OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->gsup_unit_name.unit_name = "HLR";
 | 
					 | 
				
			||||||
	g_hlr->gsup_unit_name.serno = "unnamed-HLR";
 | 
					 | 
				
			||||||
	g_hlr->gsup_unit_name.swversion = PACKAGE_NAME "-" PACKAGE_VERSION;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_sockaddr_str_from_str(&g_hlr->mslookup.server.mdns.bind_addr,
 | 
					 | 
				
			||||||
				   OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
 | 
					 | 
				
			||||||
	osmo_sockaddr_str_from_str(&g_hlr->mslookup.client.mdns.query_addr,
 | 
					 | 
				
			||||||
				   OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_start(void *ctx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.client = osmo_mslookup_client_new(dgsm_ctx);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(g_hlr->mslookup.client.client);
 | 
					 | 
				
			||||||
	g_hlr->mslookup.allow_startup = true;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_stop(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.allow_startup = false;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_mdns_client_config_apply(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* Check whether to start/stop/restart mDNS client */
 | 
					 | 
				
			||||||
	const struct osmo_sockaddr_str *current_bind_addr;
 | 
					 | 
				
			||||||
	const char *current_domain_suffix;
 | 
					 | 
				
			||||||
	current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns.running);
 | 
					 | 
				
			||||||
	current_domain_suffix = osmo_mslookup_client_method_mdns_get_domain_suffix(g_hlr->mslookup.client.mdns.running);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool should_run = g_hlr->mslookup.allow_startup
 | 
					 | 
				
			||||||
		&& g_hlr->mslookup.client.enable && g_hlr->mslookup.client.mdns.enable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool should_stop = g_hlr->mslookup.client.mdns.running &&
 | 
					 | 
				
			||||||
		(!should_run
 | 
					 | 
				
			||||||
		 || osmo_sockaddr_str_cmp(&g_hlr->mslookup.client.mdns.query_addr,
 | 
					 | 
				
			||||||
					  current_bind_addr)
 | 
					 | 
				
			||||||
		 || strcmp(g_hlr->mslookup.client.mdns.domain_suffix,
 | 
					 | 
				
			||||||
			   current_domain_suffix));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (should_stop) {
 | 
					 | 
				
			||||||
		osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns.running);
 | 
					 | 
				
			||||||
		g_hlr->mslookup.client.mdns.running = NULL;
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS client\n");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (should_run && !g_hlr->mslookup.client.mdns.running) {
 | 
					 | 
				
			||||||
		g_hlr->mslookup.client.mdns.running =
 | 
					 | 
				
			||||||
			osmo_mslookup_client_add_mdns(g_hlr->mslookup.client.client,
 | 
					 | 
				
			||||||
						      g_hlr->mslookup.client.mdns.query_addr.ip,
 | 
					 | 
				
			||||||
						      g_hlr->mslookup.client.mdns.query_addr.port,
 | 
					 | 
				
			||||||
						      -1,
 | 
					 | 
				
			||||||
						      g_hlr->mslookup.client.mdns.domain_suffix);
 | 
					 | 
				
			||||||
		if (!g_hlr->mslookup.client.mdns.running)
 | 
					 | 
				
			||||||
			LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS client with target "
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr));
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast "
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (g_hlr->mslookup.client.enable && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy))
 | 
					 | 
				
			||||||
			LOGP(DDGSM, LOGL_NOTICE,
 | 
					 | 
				
			||||||
			     "mslookup client: all GSUP requests for unknown IMSIs will be forwarded to"
 | 
					 | 
				
			||||||
			     " gateway-proxy " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.gsup_gateway_proxy));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										584
									
								
								src/dgsm_vty.c
									
									
									
									
									
								
							
							
						
						
									
										584
									
								
								src/dgsm_vty.c
									
									
									
									
									
								
							@@ -1,584 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/vty/vty.h>
 | 
					 | 
				
			||||||
#include <osmocom/vty/command.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr_vty.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server_mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node mslookup_node = {
 | 
					 | 
				
			||||||
	MSLOOKUP_NODE,
 | 
					 | 
				
			||||||
	"%s(config-mslookup)# ",
 | 
					 | 
				
			||||||
	1,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup,
 | 
					 | 
				
			||||||
      cfg_mslookup_cmd,
 | 
					 | 
				
			||||||
      "mslookup",
 | 
					 | 
				
			||||||
      "Configure Distributed GSM mslookup")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vty->node = MSLOOKUP_NODE;
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int mslookup_server_mdns_bind(struct vty *vty, int argc, const char **argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *ip_str = argc > 0? argv[0] : g_hlr->mslookup.server.mdns.bind_addr.ip;
 | 
					 | 
				
			||||||
	const char *port_str = argc > 1? argv[1] : NULL;
 | 
					 | 
				
			||||||
	uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.server.mdns.bind_addr.port;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
 | 
					 | 
				
			||||||
	    || !osmo_sockaddr_str_is_nonzero(&addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: Invalid mDNS bind address: %s %u%s",
 | 
					 | 
				
			||||||
			ip_str, port_nr, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.mdns.bind_addr = addr;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.mdns.enable = true;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.enable = true;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int mslookup_client_mdns_to(struct vty *vty, int argc, const char **argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *ip_str = argc > 0? argv[0] : g_hlr->mslookup.client.mdns.query_addr.ip;
 | 
					 | 
				
			||||||
	const char *port_str = argc > 1? argv[1] : NULL;
 | 
					 | 
				
			||||||
	uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.client.mdns.query_addr.port;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
 | 
					 | 
				
			||||||
	    || !osmo_sockaddr_str_is_nonzero(&addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup client: Invalid mDNS target address: %s %u%s",
 | 
					 | 
				
			||||||
			ip_str, port_nr, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.mdns.query_addr = addr;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.mdns.enable = true;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.enable = true;
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MDNS_STR "Multicast DNS related configuration\n"
 | 
					 | 
				
			||||||
#define MDNS_IP46_STR "multicast IPv4 address like " OSMO_MSLOOKUP_MDNS_IP4 \
 | 
					 | 
				
			||||||
			" or IPv6 address like " OSMO_MSLOOKUP_MDNS_IP6 "\n"
 | 
					 | 
				
			||||||
#define MDNS_PORT_STR "mDNS UDP Port number\n"
 | 
					 | 
				
			||||||
#define MDNS_DOMAIN_SUFFIX_STR "mDNS domain suffix (default: " OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT "). This is appended" \
 | 
					 | 
				
			||||||
				 " and stripped from mDNS packets during encoding/decoding, so we don't collide with" \
 | 
					 | 
				
			||||||
				 " top-level domains administrated by IANA\n"
 | 
					 | 
				
			||||||
#define IP46_STR "IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1\n"
 | 
					 | 
				
			||||||
#define PORT_STR "Service-specific port number\n"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_mdns,
 | 
					 | 
				
			||||||
      cfg_mslookup_mdns_cmd,
 | 
					 | 
				
			||||||
      "mdns bind [IP] [<1-65535>]",
 | 
					 | 
				
			||||||
      MDNS_STR
 | 
					 | 
				
			||||||
      "Convenience shortcut: enable and configure both server and client for mDNS mslookup\n"
 | 
					 | 
				
			||||||
      MDNS_IP46_STR MDNS_PORT_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc1 = mslookup_server_mdns_bind(vty, argc, argv);
 | 
					 | 
				
			||||||
	int rc2 = mslookup_client_mdns_to(vty, argc, argv);
 | 
					 | 
				
			||||||
	if (rc1 != CMD_SUCCESS)
 | 
					 | 
				
			||||||
		return rc1;
 | 
					 | 
				
			||||||
	return rc2;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_mdns_domain_suffix,
 | 
					 | 
				
			||||||
      cfg_mslookup_mdns_domain_suffix_cmd,
 | 
					 | 
				
			||||||
      "mdns domain-suffix DOMAIN_SUFFIX",
 | 
					 | 
				
			||||||
      MDNS_STR MDNS_DOMAIN_SUFFIX_STR MDNS_DOMAIN_SUFFIX_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.server.mdns.domain_suffix, argv[0]);
 | 
					 | 
				
			||||||
	osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.client.mdns.domain_suffix, argv[0]);
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_no_mdns,
 | 
					 | 
				
			||||||
      cfg_mslookup_no_mdns_cmd,
 | 
					 | 
				
			||||||
      "no mdns bind",
 | 
					 | 
				
			||||||
      NO_STR "Disable both server and client for mDNS mslookup\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.mdns.enable = false;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.mdns.enable = false;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node mslookup_server_node = {
 | 
					 | 
				
			||||||
	MSLOOKUP_SERVER_NODE,
 | 
					 | 
				
			||||||
	"%s(config-mslookup-server)# ",
 | 
					 | 
				
			||||||
	1,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_cmd,
 | 
					 | 
				
			||||||
      "server",
 | 
					 | 
				
			||||||
      "Enable and configure Distributed GSM mslookup server")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vty->node = MSLOOKUP_SERVER_NODE;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.enable = true;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_no_server,
 | 
					 | 
				
			||||||
      cfg_mslookup_no_server_cmd,
 | 
					 | 
				
			||||||
      "no server",
 | 
					 | 
				
			||||||
      NO_STR "Disable Distributed GSM mslookup server")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.enable = false;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_mdns_bind,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_mdns_bind_cmd,
 | 
					 | 
				
			||||||
      "mdns bind [IP] [<1-65535>]",
 | 
					 | 
				
			||||||
      MDNS_STR
 | 
					 | 
				
			||||||
      "Configure where the mDNS server listens for mslookup requests\n"
 | 
					 | 
				
			||||||
      MDNS_IP46_STR MDNS_PORT_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return mslookup_server_mdns_bind(vty, argc, argv);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_mdns_domain_suffix,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_mdns_domain_suffix_cmd,
 | 
					 | 
				
			||||||
      "mdns domain-suffix DOMAIN_SUFFIX",
 | 
					 | 
				
			||||||
      MDNS_STR
 | 
					 | 
				
			||||||
      MDNS_DOMAIN_SUFFIX_STR
 | 
					 | 
				
			||||||
      MDNS_DOMAIN_SUFFIX_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.server.mdns.domain_suffix, argv[0]);
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_no_mdns_bind,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_no_mdns_bind_cmd,
 | 
					 | 
				
			||||||
      "no mdns bind",
 | 
					 | 
				
			||||||
      NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.server.mdns.enable = false;
 | 
					 | 
				
			||||||
	mslookup_server_mdns_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node mslookup_server_msc_node = {
 | 
					 | 
				
			||||||
	MSLOOKUP_SERVER_MSC_NODE,
 | 
					 | 
				
			||||||
	"%s(config-mslookup-server-msc)# ",
 | 
					 | 
				
			||||||
	1,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_msc,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_msc_cmd,
 | 
					 | 
				
			||||||
      "msc ipa-name .IPA_NAME",
 | 
					 | 
				
			||||||
      "Configure services for individual local MSCs\n"
 | 
					 | 
				
			||||||
      "Identify locally connected MSC by IPA Unit Name\n"
 | 
					 | 
				
			||||||
      "IPA Unit Name of the local MSC to configure\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_ipa_name msc_name;
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc;
 | 
					 | 
				
			||||||
	osmo_ipa_name_set_str(&msc_name, argv_concat(argv, argc, 0));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msc = mslookup_server_msc_get(&msc_name, true);
 | 
					 | 
				
			||||||
	if (!msc) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Error creating MSC %s%s", osmo_ipa_name_to_str(&msc_name), VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	vty->node = MSLOOKUP_SERVER_MSC_NODE;
 | 
					 | 
				
			||||||
	vty->index = msc;
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define SERVICE_NAME_STR \
 | 
					 | 
				
			||||||
	"mslookup service name, e.g. sip.voice or smpp.sms\n"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct mslookup_server_msc_cfg *msc_from_node(struct vty *vty)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	switch (vty->node) {
 | 
					 | 
				
			||||||
	case MSLOOKUP_SERVER_NODE:
 | 
					 | 
				
			||||||
		/* On the mslookup.server node, set services on the wildcard msc, without a particular name. */
 | 
					 | 
				
			||||||
		return mslookup_server_msc_get(&mslookup_server_msc_wildcard, true);
 | 
					 | 
				
			||||||
	case MSLOOKUP_SERVER_MSC_NODE:
 | 
					 | 
				
			||||||
		return vty->index;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_msc_service,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_msc_service_cmd,
 | 
					 | 
				
			||||||
      "service NAME at IP <1-65535>",
 | 
					 | 
				
			||||||
      "Configure addresses of local services, as sent in replies to remote mslookup requests.\n"
 | 
					 | 
				
			||||||
      SERVICE_NAME_STR "at\n" IP46_STR PORT_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
 | 
					 | 
				
			||||||
	 * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
 | 
					 | 
				
			||||||
	const char *service = argv[0];
 | 
					 | 
				
			||||||
	const char *ip_str = argv[1];
 | 
					 | 
				
			||||||
	const char *port_str = argv[2];
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msc) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
 | 
					 | 
				
			||||||
	    || !osmo_sockaddr_str_is_nonzero(&addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: Invalid address for service %s: %s %s%s",
 | 
					 | 
				
			||||||
			service, ip_str, port_str, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (mslookup_server_msc_service_set(msc, service, &addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: Error setting service %s to %s %s%s",
 | 
					 | 
				
			||||||
			service, ip_str, port_str, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define NO_SERVICE_AND_NAME_STR NO_STR "Remove one or more service address entries\n" SERVICE_NAME_STR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_msc_no_service,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_msc_no_service_cmd,
 | 
					 | 
				
			||||||
      "no service NAME",
 | 
					 | 
				
			||||||
      NO_SERVICE_AND_NAME_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
 | 
					 | 
				
			||||||
	 * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
 | 
					 | 
				
			||||||
	const char *service = argv[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msc) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (mslookup_server_msc_service_del(msc, service, NULL) < 1) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: cannot remove service '%s'%s",
 | 
					 | 
				
			||||||
			service, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_server_msc_no_service_addr,
 | 
					 | 
				
			||||||
      cfg_mslookup_server_msc_no_service_addr_cmd,
 | 
					 | 
				
			||||||
      "no service NAME at IP <1-65535>",
 | 
					 | 
				
			||||||
      NO_SERVICE_AND_NAME_STR "at\n" IP46_STR PORT_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
 | 
					 | 
				
			||||||
	 * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
 | 
					 | 
				
			||||||
	const char *service = argv[0];
 | 
					 | 
				
			||||||
	const char *ip_str = argv[1];
 | 
					 | 
				
			||||||
	const char *port_str = argv[2];
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msc) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
 | 
					 | 
				
			||||||
	    || !osmo_sockaddr_str_is_nonzero(&addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: Invalid address for 'no service' %s: %s %s%s",
 | 
					 | 
				
			||||||
			service, ip_str, port_str, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (mslookup_server_msc_service_del(msc, service, &addr) < 1) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup server: cannot remove service '%s' to %s %s%s",
 | 
					 | 
				
			||||||
			service, ip_str, port_str, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node mslookup_client_node = {
 | 
					 | 
				
			||||||
	MSLOOKUP_CLIENT_NODE,
 | 
					 | 
				
			||||||
	"%s(config-mslookup-client)# ",
 | 
					 | 
				
			||||||
	1,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_cmd,
 | 
					 | 
				
			||||||
      "client",
 | 
					 | 
				
			||||||
      "Enable and configure Distributed GSM mslookup client")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vty->node = MSLOOKUP_CLIENT_NODE;
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.enable = true;
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_no_client,
 | 
					 | 
				
			||||||
      cfg_mslookup_no_client_cmd,
 | 
					 | 
				
			||||||
      "no client",
 | 
					 | 
				
			||||||
      NO_STR "Disable Distributed GSM mslookup client")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.enable = false;
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_timeout,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_timeout_cmd,
 | 
					 | 
				
			||||||
      "timeout <1-100000>",
 | 
					 | 
				
			||||||
      "How long should the mslookup client wait for remote responses before evaluating received results\n"
 | 
					 | 
				
			||||||
      "timeout in milliseconds\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	uint32_t val = atol(argv[0]);
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.result_timeout_milliseconds = val;
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define EXIT_HINT() \
 | 
					 | 
				
			||||||
	if (vty->type != VTY_FILE) \
 | 
					 | 
				
			||||||
		vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_mdns_bind,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_mdns_bind_cmd,
 | 
					 | 
				
			||||||
      "mdns bind [IP] [<1-65535>]",
 | 
					 | 
				
			||||||
      MDNS_STR
 | 
					 | 
				
			||||||
      "Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
 | 
					 | 
				
			||||||
      MDNS_IP46_STR MDNS_PORT_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return mslookup_client_mdns_to(vty, argc, argv);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_mdns_domain_suffix,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_mdns_domain_suffix_cmd,
 | 
					 | 
				
			||||||
      "mdns domain-suffix DOMAIN_SUFFIX",
 | 
					 | 
				
			||||||
      MDNS_STR
 | 
					 | 
				
			||||||
      MDNS_DOMAIN_SUFFIX_STR
 | 
					 | 
				
			||||||
      MDNS_DOMAIN_SUFFIX_STR)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.client.mdns.domain_suffix, argv[0]);
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_no_mdns_bind,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_no_mdns_bind_cmd,
 | 
					 | 
				
			||||||
      "no mdns bind",
 | 
					 | 
				
			||||||
      NO_STR "Disable mDNS client, do not query remote services by mDNS\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.mdns.enable = false;
 | 
					 | 
				
			||||||
	dgsm_mdns_client_config_apply();
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void config_write_msc_services(struct vty *vty, const char *indent, struct mslookup_server_msc_cfg *msc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_service_host *e;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(e, &msc->service_hosts, entry) {
 | 
					 | 
				
			||||||
		if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
 | 
					 | 
				
			||||||
			vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v4.ip, e->host_v4.port,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
		if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
 | 
					 | 
				
			||||||
			vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v6.ip, e->host_v6.port,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int config_write_mslookup(struct vty *vty)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!g_hlr->mslookup.server.enable
 | 
					 | 
				
			||||||
	    && llist_empty(&g_hlr->mslookup.server.local_site_services)
 | 
					 | 
				
			||||||
	    && !g_hlr->mslookup.client.enable)
 | 
					 | 
				
			||||||
		return CMD_SUCCESS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vty_out(vty, "mslookup%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (g_hlr->mslookup.server.enable || !llist_empty(&g_hlr->mslookup.server.local_site_services)) {
 | 
					 | 
				
			||||||
		struct mslookup_server_msc_cfg *msc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vty_out(vty, " server%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (g_hlr->mslookup.server.mdns.enable) {
 | 
					 | 
				
			||||||
			vty_out(vty, "  mdns bind");
 | 
					 | 
				
			||||||
			if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.server.mdns.bind_addr)) {
 | 
					 | 
				
			||||||
				vty_out(vty, " %s %u",
 | 
					 | 
				
			||||||
					g_hlr->mslookup.server.mdns.bind_addr.ip,
 | 
					 | 
				
			||||||
					g_hlr->mslookup.server.mdns.bind_addr.port);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			vty_out(vty, "%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (strcmp(g_hlr->mslookup.server.mdns.domain_suffix, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT))
 | 
					 | 
				
			||||||
			vty_out(vty, "  mdns domain-suffix %s%s",
 | 
					 | 
				
			||||||
				g_hlr->mslookup.server.mdns.domain_suffix,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
 | 
					 | 
				
			||||||
		if (msc)
 | 
					 | 
				
			||||||
			config_write_msc_services(vty, "  ", msc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
 | 
					 | 
				
			||||||
			if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			vty_out(vty, "  msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
 | 
					 | 
				
			||||||
			config_write_msc_services(vty, "   ", msc);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* If the server is disabled, still output the above to not lose the service config. */
 | 
					 | 
				
			||||||
		if (!g_hlr->mslookup.server.enable)
 | 
					 | 
				
			||||||
			vty_out(vty, " no server%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (g_hlr->mslookup.client.enable) {
 | 
					 | 
				
			||||||
		vty_out(vty, " client%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy))
 | 
					 | 
				
			||||||
			vty_out(vty, "  gateway-proxy %s %u%s",
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.gsup_gateway_proxy.ip,
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.gsup_gateway_proxy.port,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (g_hlr->mslookup.client.mdns.enable
 | 
					 | 
				
			||||||
		    && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.mdns.query_addr))
 | 
					 | 
				
			||||||
			vty_out(vty, "  mdns bind %s %u%s",
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.mdns.query_addr.ip,
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.mdns.query_addr.port,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
		if (strcmp(g_hlr->mslookup.client.mdns.domain_suffix, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT))
 | 
					 | 
				
			||||||
			vty_out(vty, "  mdns domain-suffix %s%s",
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.mdns.domain_suffix,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
		if (g_hlr->mslookup.client.result_timeout_milliseconds != OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS)
 | 
					 | 
				
			||||||
			vty_out(vty, "  timeout %u%s",
 | 
					 | 
				
			||||||
				g_hlr->mslookup.client.result_timeout_milliseconds,
 | 
					 | 
				
			||||||
				VTY_NEWLINE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_gateway_proxy,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_gateway_proxy_cmd,
 | 
					 | 
				
			||||||
      "gateway-proxy IP [<1-65535>]",
 | 
					 | 
				
			||||||
      "Configure a fixed IP address to send all GSUP requests for unknown IMSIs to, without invoking a lookup for IMSI\n"
 | 
					 | 
				
			||||||
      "IP address of the remote HLR\n" "GSUP port number (omit for default " OSMO_STRINGIFY_VAL(OSMO_GSUP_PORT) ")\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *ip_str = argv[0];
 | 
					 | 
				
			||||||
	const char *port_str = argc > 1 ? argv[1] : NULL;
 | 
					 | 
				
			||||||
	struct osmo_sockaddr_str addr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_from_str(&addr, ip_str, port_str ? atoi(port_str) : OSMO_GSUP_PORT)
 | 
					 | 
				
			||||||
	    || !osmo_sockaddr_str_is_nonzero(&addr)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% mslookup client: Invalid address for gateway-proxy: %s %s%s",
 | 
					 | 
				
			||||||
			ip_str, port_str ? : "", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.gsup_gateway_proxy = addr;
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_mslookup_client_no_gateway_proxy,
 | 
					 | 
				
			||||||
      cfg_mslookup_client_no_gateway_proxy_cmd,
 | 
					 | 
				
			||||||
      "no gateway-proxy",
 | 
					 | 
				
			||||||
      NO_STR "Disable gateway proxy for GSUP with unknown IMSIs\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.gsup_gateway_proxy = (struct osmo_sockaddr_str){};
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(do_mslookup_show_services,
 | 
					 | 
				
			||||||
      do_mslookup_show_services_cmd,
 | 
					 | 
				
			||||||
      "show mslookup services",
 | 
					 | 
				
			||||||
      SHOW_STR "Distributed GSM / mslookup related information\n"
 | 
					 | 
				
			||||||
      "List configured service addresses as sent to remote mslookup requests\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc;
 | 
					 | 
				
			||||||
	const struct mslookup_service_host *local_hlr = mslookup_server_get_local_gsup_addr();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vty_out(vty, "Local GSUP HLR address returned in mslookup responses for local IMSIs:");
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v4))
 | 
					 | 
				
			||||||
		vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
 | 
					 | 
				
			||||||
			OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v4));
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v6))
 | 
					 | 
				
			||||||
		vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
 | 
					 | 
				
			||||||
			OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v6));
 | 
					 | 
				
			||||||
	vty_out(vty, "%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
 | 
					 | 
				
			||||||
	if (msc)
 | 
					 | 
				
			||||||
		config_write_msc_services(vty, "", msc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
 | 
					 | 
				
			||||||
		if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		vty_out(vty, "msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
 | 
					 | 
				
			||||||
		config_write_msc_services(vty, " ", msc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void dgsm_vty_init(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	install_element(CONFIG_NODE, &cfg_mslookup_cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_node(&mslookup_node, config_write_mslookup);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_domain_suffix_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_no_mdns_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_server_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_no_server_cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_node(&mslookup_server_node, NULL);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_bind_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_domain_suffix_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_bind_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_node(&mslookup_server_msc_node, NULL);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_service_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_client_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd);
 | 
					 | 
				
			||||||
	install_node(&mslookup_client_node, NULL);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_timeout_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_bind_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_domain_suffix_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_bind_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
 | 
					 | 
				
			||||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_element_ve(&do_mslookup_show_services_cmd);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -47,11 +47,6 @@ struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
				
			|||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return gsup_route_find(gs, ipa_name->val, ipa_name->len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Find a GSUP connection's route (to read the IPA address from the route).
 | 
					/*! Find a GSUP connection's route (to read the IPA address from the route).
 | 
				
			||||||
 * \param[in] conn GSUP connection
 | 
					 * \param[in] conn GSUP connection
 | 
				
			||||||
 * \return GSUP route
 | 
					 * \return GSUP route
 | 
				
			||||||
@@ -72,15 +67,10 @@ struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn)
 | 
				
			|||||||
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)
 | 
					int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gsup_route *gr;
 | 
						struct gsup_route *gr;
 | 
				
			||||||
	struct osmo_gsup_conn *exists_on_conn;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check if we already have a route for this address */
 | 
						/* Check if we already have a route for this address */
 | 
				
			||||||
	exists_on_conn = gsup_route_find(conn->server, addr, addrlen);
 | 
						if (gsup_route_find(conn->server, addr, addrlen))
 | 
				
			||||||
	if (exists_on_conn) {
 | 
							return -EEXIST;
 | 
				
			||||||
		if (exists_on_conn != conn)
 | 
					 | 
				
			||||||
			return -EEXIST;
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allocate new route and populate it */
 | 
						/* allocate new route and populate it */
 | 
				
			||||||
	gr = talloc_zero(conn->server, struct gsup_route);
 | 
						gr = talloc_zero(conn->server, struct gsup_route);
 | 
				
			||||||
@@ -96,11 +86,6 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return gsup_route_add(conn, ipa_name->val, ipa_name->len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* delete all routes for the given connection */
 | 
					/* delete all routes for the given connection */
 | 
				
			||||||
int gsup_route_del_conn(struct osmo_gsup_conn *conn)
 | 
					int gsup_route_del_conn(struct osmo_gsup_conn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -110,7 +95,7 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn)
 | 
				
			|||||||
	llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
 | 
						llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
 | 
				
			||||||
		if (gr->conn == conn) {
 | 
							if (gr->conn == conn) {
 | 
				
			||||||
			LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",
 | 
								LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",
 | 
				
			||||||
			     osmo_quote_str_c(OTC_SELECT, (char*)gr->addr, talloc_total_size(gr->addr)));
 | 
								     gr->addr);
 | 
				
			||||||
			llist_del(&gr->list);
 | 
								llist_del(&gr->list);
 | 
				
			||||||
			talloc_free(gr);
 | 
								talloc_free(gr);
 | 
				
			||||||
			num_deleted++;
 | 
								num_deleted++;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,8 +42,7 @@ int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	conn = gsup_route_find(gs, addr, addrlen);
 | 
						conn = gsup_route_find(gs, addr, addrlen);
 | 
				
			||||||
	if (!conn) {
 | 
						if (!conn) {
 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR,
 | 
							DEBUGP(DLGSUP, "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen));
 | 
				
			||||||
		     "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen));
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
							msgb_free(msg);
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -51,41 +50,3 @@ int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
				
			|||||||
	return osmo_gsup_conn_send(conn, msg);
 | 
						return osmo_gsup_conn_send(conn, msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Send a msgb to a given address using routing.
 | 
					 | 
				
			||||||
 * \param[in] gs  gsup server
 | 
					 | 
				
			||||||
 * \param[in] ipa_name  IPA unit name of the client (SGSN, MSC/VLR, proxy).
 | 
					 | 
				
			||||||
 * \param[in] msg  message buffer
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (ipa_name->val[ipa_name->len - 1]) {
 | 
					 | 
				
			||||||
		/* Is not nul terminated. But for legacy reasons we (still) require that. */
 | 
					 | 
				
			||||||
		if (ipa_name->len >= sizeof(ipa_name->val)) {
 | 
					 | 
				
			||||||
			LOGP(DLGSUP, LOGL_ERROR, "IPA unit name is too long: %s\n",
 | 
					 | 
				
			||||||
			     osmo_ipa_name_to_str(ipa_name));
 | 
					 | 
				
			||||||
			return -EINVAL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		struct osmo_ipa_name ipa_name2 = *ipa_name;
 | 
					 | 
				
			||||||
		ipa_name2.val[ipa_name->len] = '\0';
 | 
					 | 
				
			||||||
		ipa_name2.len++;
 | 
					 | 
				
			||||||
		return osmo_gsup_addr_send(gs, ipa_name2.val, ipa_name2.len, msg);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return osmo_gsup_addr_send(gs, ipa_name->val, ipa_name->len, msg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,
 | 
					 | 
				
			||||||
			  const struct osmo_gsup_message *gsup)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	rc = osmo_gsup_encode(msg, gsup);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "IMSI-%s: Cannot encode GSUP: %s\n",
 | 
					 | 
				
			||||||
		     gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGP(DLGSUP, LOGL_DEBUG, "IMSI-%s: Tx: %s\n", gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));
 | 
					 | 
				
			||||||
	return osmo_gsup_send_to_ipa_name(gs, ipa_name, msg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,8 +18,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <errno.h>
 | 
					#include <errno.h>
 | 
				
			||||||
#include <netinet/tcp.h>
 | 
					 | 
				
			||||||
#include <netinet/in.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/msgb.h>
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
@@ -28,18 +26,13 @@
 | 
				
			|||||||
#include <osmocom/abis/ipaccess.h>
 | 
					#include <osmocom/abis/ipaccess.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
					#include <osmocom/gsm/gsm48_ie.h>
 | 
				
			||||||
#include <osmocom/gsm/apn.h>
 | 
					#include <osmocom/gsm/apn.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
#include <osmocom/hlr/gsup_router.h>
 | 
					#include <osmocom/hlr/gsup_router.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LOG_GSUP_CONN(conn, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \
 | 
					 | 
				
			||||||
	     (conn) ? osmo_ipa_name_to_str(&(conn)->peer_name) : "NULL", ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct msgb *osmo_gsup_msgb_alloc(const char *label)
 | 
					struct msgb *osmo_gsup_msgb_alloc(const char *label)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct msgb *msg = msgb_alloc_headroom(1024+512, 512, label);
 | 
						struct msgb *msg = msgb_alloc_headroom(1024+16, 16, label);
 | 
				
			||||||
	OSMO_ASSERT(msg);
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
	return msg;
 | 
						return msg;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -64,91 +57,6 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_server *server = req->cb_data;
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id *routing;
 | 
					 | 
				
			||||||
	struct osmo_gsup_conn *conn = NULL;
 | 
					 | 
				
			||||||
	struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (response->message_type == OSMO_GSUP_MSGT_ROUTING_ERROR
 | 
					 | 
				
			||||||
	    && !osmo_cni_peer_id_is_empty(&req->via_proxy)) {
 | 
					 | 
				
			||||||
		/* If a routing error occured, we need to route back via the immediate sending peer, not via the
 | 
					 | 
				
			||||||
		 * intended final recipient -- because one source of routing errors is a duplicate name for a recipient.
 | 
					 | 
				
			||||||
		 * If we resolve to req->source_name, we may send to a completely unrelated recipient. */
 | 
					 | 
				
			||||||
		routing = &req->via_proxy;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		routing = &req->source_name;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	switch (routing->type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		conn = gsup_route_find_by_ipa_name(server, &routing->ipa_name);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
 | 
					 | 
				
			||||||
			     osmo_cni_peer_id_type_name(routing->type));
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!conn) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP client that sent this request not found, cannot respond\n");
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = osmo_gsup_encode(msg, response);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to encode: {%s}\n",
 | 
					 | 
				
			||||||
			     osmo_gsup_message_to_str_c(OTC_SELECT, response));
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = osmo_gsup_conn_send(conn, msg);
 | 
					 | 
				
			||||||
	if (rc)
 | 
					 | 
				
			||||||
		LOG_GSUP_CONN(conn, LOGL_ERROR, "Unable to send: %s\n", osmo_gsup_message_to_str_c(OTC_SELECT, response));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *req;
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id cpi = {
 | 
					 | 
				
			||||||
		.type = OSMO_CNI_PEER_ID_IPA_NAME,
 | 
					 | 
				
			||||||
		.ipa_name = conn->peer_name,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req = osmo_gsup_req_new(conn->server, &cpi, msg, gsup_server_send_req_response, conn->server, NULL);
 | 
					 | 
				
			||||||
	if (!req)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
 | 
					 | 
				
			||||||
		switch (req->via_proxy.type) {
 | 
					 | 
				
			||||||
		case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_type_name(req->source_name.type));
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
 | 
					 | 
				
			||||||
			return NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* The source of the GSUP message is not the immediate GSUP peer, but that peer is our proxy for that
 | 
					 | 
				
			||||||
		 * source. Add it to the routes for this conn (so we can route responses back). */
 | 
					 | 
				
			||||||
		if (gsup_route_add_ipa_name(conn, &req->source_name.ipa_name)) {
 | 
					 | 
				
			||||||
			LOG_GSUP_REQ(req, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "GSUP message received from %s via peer %s, but there already exists a"
 | 
					 | 
				
			||||||
				     " different route to this source, message is not routable\n",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_to_str(&req->source_name),
 | 
					 | 
				
			||||||
				     osmo_ipa_name_to_str(&conn->peer_name));
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
 | 
					 | 
				
			||||||
			return NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return req;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int osmo_gsup_conn_oap_handle(struct osmo_gsup_conn *conn,
 | 
					static int osmo_gsup_conn_oap_handle(struct osmo_gsup_conn *conn,
 | 
				
			||||||
				struct msgb *msg_rx)
 | 
									struct msgb *msg_rx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -276,7 +184,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
 | 
						struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
 | 
				
			||||||
	uint8_t *addr = NULL;
 | 
						uint8_t *addr = NULL;
 | 
				
			||||||
	int addr_len;
 | 
						size_t addr_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
 | 
						LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,18 +202,10 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
 | 
				
			|||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_ipa_name_set(&clnt->peer_name, addr, addr_len);
 | 
						gsup_route_add(clnt, addr, addr_len);
 | 
				
			||||||
	gsup_route_add_ipa_name(clnt, &clnt->peer_name);
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void osmo_gsup_conn_free(struct osmo_gsup_conn *conn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	gsup_route_del_conn(conn);
 | 
					 | 
				
			||||||
	llist_del(&conn->list);
 | 
					 | 
				
			||||||
	talloc_free(conn);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
 | 
					static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
 | 
						struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
 | 
				
			||||||
@@ -313,21 +213,48 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
 | 
				
			|||||||
	LOGP(DLGSUP, LOGL_INFO, "Lost GSUP client %s:%d\n",
 | 
						LOGP(DLGSUP, LOGL_INFO, "Lost GSUP client %s:%d\n",
 | 
				
			||||||
		conn->addr, conn->port);
 | 
							conn->addr, conn->port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_gsup_conn_free(clnt);
 | 
						gsup_route_del_conn(clnt);
 | 
				
			||||||
 | 
						llist_del(&clnt->list);
 | 
				
			||||||
 | 
						talloc_free(clnt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void update_fd_settings(int fd)
 | 
					/* Add conn to the clients list in a way that conn->auc_3g_ind takes the lowest
 | 
				
			||||||
 | 
					 * unused integer and the list of clients remains sorted by auc_3g_ind.
 | 
				
			||||||
 | 
					 * Keep this function non-static to allow linking in a unit test. */
 | 
				
			||||||
 | 
					void osmo_gsup_server_add_conn(struct llist_head *clients,
 | 
				
			||||||
 | 
								       struct osmo_gsup_conn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						struct osmo_gsup_conn *c;
 | 
				
			||||||
	int val;
 | 
						struct osmo_gsup_conn *prev_conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*TODO: Set keepalive settings here. See OS#4312 */
 | 
						c = llist_first_entry_or_null(clients, struct osmo_gsup_conn, list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	val = 1;
 | 
						/* Is the first index, 0, unused? */
 | 
				
			||||||
	ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
 | 
						if (!c || c->auc_3g_ind > 0) {
 | 
				
			||||||
	if (ret < 0)
 | 
							conn->auc_3g_ind = 0;
 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
 | 
							llist_add(&conn->list, clients);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Look for a gap later on */
 | 
				
			||||||
 | 
						prev_conn = NULL;
 | 
				
			||||||
 | 
						llist_for_each_entry(c, clients, list) {
 | 
				
			||||||
 | 
							/* skip first item, we know it has auc_3g_ind == 0. */
 | 
				
			||||||
 | 
							if (!prev_conn) {
 | 
				
			||||||
 | 
								prev_conn = c;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (c->auc_3g_ind > prev_conn->auc_3g_ind + 1)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							prev_conn = c;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(prev_conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn->auc_3g_ind = prev_conn->auc_3g_ind + 1;
 | 
				
			||||||
 | 
						llist_add(&conn->list, &prev_conn->list);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* a client has connected to the server socket and we have accept()ed it */
 | 
					/* a client has connected to the server socket and we have accept()ed it */
 | 
				
			||||||
@@ -349,11 +276,10 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* link data structure with server structure */
 | 
						/* link data structure with server structure */
 | 
				
			||||||
	conn->server = gsups;
 | 
						conn->server = gsups;
 | 
				
			||||||
	llist_add_tail(&conn->list, &gsups->clients);
 | 
						osmo_gsup_server_add_conn(&gsups->clients, conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d\n", conn->conn->addr, conn->conn->port);
 | 
						LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n",
 | 
				
			||||||
 | 
						     conn->conn->addr, conn->conn->port, conn->auc_3g_ind);
 | 
				
			||||||
	update_fd_settings(fd);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* request the identity of the client */
 | 
						/* request the identity of the client */
 | 
				
			||||||
	rc = ipa_ccm_send_id_req(fd);
 | 
						rc = ipa_ccm_send_id_req(fd);
 | 
				
			||||||
@@ -372,7 +298,8 @@ failed:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_server *
 | 
					struct osmo_gsup_server *
 | 
				
			||||||
osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
 | 
					osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
 | 
				
			||||||
			osmo_gsup_read_cb_t read_cb, void *priv)
 | 
								osmo_gsup_read_cb_t read_cb,
 | 
				
			||||||
 | 
								struct llist_head *lu_op_lst, void *priv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_server *gsups;
 | 
						struct osmo_gsup_server *gsups;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
@@ -398,6 +325,8 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
 | 
				
			|||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
		goto failed;
 | 
							goto failed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gsups->luop = lu_op_lst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return gsups;
 | 
						return gsups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
failed:
 | 
					failed:
 | 
				
			||||||
@@ -440,7 +369,7 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Populate a gsup message structure with an Insert Subscriber Data Message.
 | 
					 * Populate a gsup message structure with an Insert Subscriber Data Message.
 | 
				
			||||||
 * All required memory buffers for data pointed to by pointers in struct osmo_gsup_message
 | 
					 * All required memory buffers for data pointed to by pointers in struct omso_gsup_message
 | 
				
			||||||
 * must be allocated by the caller and should have the same lifetime as the gsup parameter.
 | 
					 * must be allocated by the caller and should have the same lifetime as the gsup parameter.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \param[out] gsup  The gsup message to populate.
 | 
					 * \param[out] gsup  The gsup message to populate.
 | 
				
			||||||
@@ -461,10 +390,8 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
 | 
				
			|||||||
	int len;
 | 
						int len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(gsup);
 | 
						OSMO_ASSERT(gsup);
 | 
				
			||||||
	*gsup = (struct osmo_gsup_message){
 | 
					 | 
				
			||||||
		.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gsup->message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
 | 
				
			||||||
	osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
 | 
						osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
 | 
						if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
 | 
				
			||||||
@@ -492,39 +419,3 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
 | 
					 | 
				
			||||||
				    struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	/* To forward to a remote entity (HLR, SMSC,...), we need to indicate the original source name in the Source
 | 
					 | 
				
			||||||
	 * Name IE to make sure the reply can be routed back. Store the sender in gsup->source_name -- the remote entity
 | 
					 | 
				
			||||||
	 * is required to return this as gsup->destination_name so that the reply gets routed to the original sender. */
 | 
					 | 
				
			||||||
	struct osmo_gsup_message forward = *(modified_gsup? : &req->gsup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
 | 
					 | 
				
			||||||
					  osmo_cni_peer_id_type_name(req->source_name.type));
 | 
					 | 
				
			||||||
		rc = -ENOTSUP;
 | 
					 | 
				
			||||||
		goto routing_error;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	forward.source_name = req->source_name.ipa_name.val;
 | 
					 | 
				
			||||||
	forward.source_name_len = req->source_name.ipa_name.len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (to_peer->type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
 | 
					 | 
				
			||||||
					  osmo_cni_peer_id_type_name(to_peer->type));
 | 
					 | 
				
			||||||
		rc = -ENOTSUP;
 | 
					 | 
				
			||||||
		goto routing_error;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_cni_peer_id_to_str(to_peer));
 | 
					 | 
				
			||||||
	rc = osmo_gsup_enc_send_to_ipa_name(server, &to_peer->ipa_name, &forward);
 | 
					 | 
				
			||||||
	if (rc)
 | 
					 | 
				
			||||||
		goto routing_error;
 | 
					 | 
				
			||||||
	osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
routing_error:
 | 
					 | 
				
			||||||
	osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,14 @@
 | 
				
			|||||||
# This is _NOT_ the library release version, it's an API version.
 | 
					# This is _NOT_ the library release version, it's an API version.
 | 
				
			||||||
# Please read chapter "Library interface versions" of the libtool documentation
 | 
					# Please read chapter "Library interface versions" of the libtool documentation
 | 
				
			||||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
 | 
					# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
 | 
				
			||||||
LIBVERSION=1:0:1
 | 
					LIBVERSION=0:0:0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
 | 
					AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
 | 
				
			||||||
	    $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
 | 
						    $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lib_LTLIBRARIES = libosmo-gsup-client.la
 | 
					lib_LTLIBRARIES = libosmo-gsup-client.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libosmo_gsup_client_la_SOURCES = \
 | 
					libosmo_gsup_client_la_SOURCES = gsup_client.c
 | 
				
			||||||
	cni_peer_id.c \
 | 
					 | 
				
			||||||
	gsup_client.c \
 | 
					 | 
				
			||||||
	gsup_req.c \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
 | 
					libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
 | 
				
			||||||
libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS)
 | 
					libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,193 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * All Rights Reserved
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU General Public License along
 | 
					 | 
				
			||||||
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/utils.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return (!ipa_name) || (!ipa_name->len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!val || !len) {
 | 
					 | 
				
			||||||
		*ipa_name = (struct osmo_ipa_name){};
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (len > sizeof(ipa_name->val))
 | 
					 | 
				
			||||||
		return -ENOSPC;
 | 
					 | 
				
			||||||
	ipa_name->len = len;
 | 
					 | 
				
			||||||
	memcpy(ipa_name->val, val, len);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int osmo_ipa_name_set_str_va(struct osmo_ipa_name *ipa_name, const char *str_fmt, va_list ap)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!str_fmt)
 | 
					 | 
				
			||||||
		return osmo_ipa_name_set(ipa_name, NULL, 0);
 | 
					 | 
				
			||||||
	vsnprintf((char*)(ipa_name->val), sizeof(ipa_name->val), str_fmt, ap);
 | 
					 | 
				
			||||||
	ipa_name->len = strlen((char*)(ipa_name->val))+1;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	va_list ap;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	va_start(ap, str_fmt);
 | 
					 | 
				
			||||||
	rc = osmo_ipa_name_set_str_va(ipa_name, str_fmt, ap);
 | 
					 | 
				
			||||||
	va_end(ap);
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int cmp;
 | 
					 | 
				
			||||||
	if (a == b)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	if (!a)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	if (!b)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (!a->len && !b->len)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	if (!a->len && b->len)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	if (!b->len && a->len)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (a->len == b->len)
 | 
					 | 
				
			||||||
		return memcmp(a->val, b->val, a->len);
 | 
					 | 
				
			||||||
	else if (a->len < b->len) {
 | 
					 | 
				
			||||||
		cmp = memcmp(a->val, b->val, a->len);
 | 
					 | 
				
			||||||
		if (!cmp)
 | 
					 | 
				
			||||||
			cmp = -1;
 | 
					 | 
				
			||||||
		return cmp;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a->len > b->len */
 | 
					 | 
				
			||||||
		cmp = memcmp(a->val, b->val, b->len);
 | 
					 | 
				
			||||||
		if (!cmp)
 | 
					 | 
				
			||||||
			cmp = 1;
 | 
					 | 
				
			||||||
		return cmp;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Call osmo_ipa_name_to_str_c with OTC_SELECT. */
 | 
					 | 
				
			||||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return osmo_ipa_name_to_str_c(OTC_SELECT, ipa_name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
 | 
					 | 
				
			||||||
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	size_t len = ipa_name->len;
 | 
					 | 
				
			||||||
	if (!len)
 | 
					 | 
				
			||||||
		return talloc_strdup(ctx, "");
 | 
					 | 
				
			||||||
	if (ipa_name->val[len-1] == '\0')
 | 
					 | 
				
			||||||
		len--;
 | 
					 | 
				
			||||||
	return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!cni_peer_id)
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	switch (cni_peer_id->type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_EMPTY:
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		return osmo_ipa_name_is_empty(&cni_peer_id->ipa_name);
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
 | 
					 | 
				
			||||||
			  const uint8_t *val, size_t len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cni_peer_id->type = type;
 | 
					 | 
				
			||||||
	switch (type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		return osmo_ipa_name_set(&cni_peer_id->ipa_name, val, len);
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
 | 
					 | 
				
			||||||
			      const char *str_fmt, ...)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	va_list ap;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*cni_peer_id = (struct osmo_cni_peer_id){};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		cni_peer_id->type = OSMO_CNI_PEER_ID_IPA_NAME;
 | 
					 | 
				
			||||||
		va_start(ap, str_fmt);
 | 
					 | 
				
			||||||
		rc = osmo_ipa_name_set_str_va(&cni_peer_id->ipa_name, str_fmt, ap);
 | 
					 | 
				
			||||||
		va_end(ap);
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (a == b)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	if (!a)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	if (!b)
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	if (a->type != b->type)
 | 
					 | 
				
			||||||
		return OSMO_CMP(a->type, b->type);
 | 
					 | 
				
			||||||
	switch (a->type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		return osmo_ipa_name_cmp(&a->ipa_name, &b->ipa_name);
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const struct value_string osmo_cni_peer_id_type_names[] = {
 | 
					 | 
				
			||||||
	{ OSMO_CNI_PEER_ID_IPA_NAME, "IPA-name" },
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Call osmo_cni_peer_id_to_str_c with OTC_SELECT */
 | 
					 | 
				
			||||||
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cpi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return osmo_cni_peer_id_to_str_c(OTC_SELECT, cpi);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
 | 
					 | 
				
			||||||
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cpi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	switch (cpi->type) {
 | 
					 | 
				
			||||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
					 | 
				
			||||||
		return osmo_ipa_name_to_str_c(ctx, &cpi->ipa_name);
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -31,8 +31,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <errno.h>
 | 
					#include <errno.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <netinet/tcp.h>
 | 
					 | 
				
			||||||
#include <netinet/in.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void start_test_procedure(struct osmo_gsup_client *gsupc);
 | 
					static void start_test_procedure(struct osmo_gsup_client *gsupc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -99,12 +97,6 @@ static void connect_timer_cb(void *gsupc_)
 | 
				
			|||||||
	if (gsupc->is_connected)
 | 
						if (gsupc->is_connected)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gsupc->up_down_cb) {
 | 
					 | 
				
			||||||
		/* When the up_down_cb() returns false, the user asks us not to retry connecting. */
 | 
					 | 
				
			||||||
		if (!gsupc->up_down_cb(gsupc, false))
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gsup_client_connect(gsupc);
 | 
						gsup_client_connect(gsupc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,19 +123,6 @@ static void gsup_client_oap_register(struct osmo_gsup_client *gsupc)
 | 
				
			|||||||
	client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
 | 
						client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void update_fd_settings(int fd)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
	int val;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*TODO: Set keepalive settings here. See OS#4312 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	val = 1;
 | 
					 | 
				
			||||||
	ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
 | 
					static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_client *gsupc = link->data;
 | 
						struct osmo_gsup_client *gsupc = link->data;
 | 
				
			||||||
@@ -154,25 +133,15 @@ static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
 | 
				
			|||||||
	gsupc->is_connected = up;
 | 
						gsupc->is_connected = up;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (up) {
 | 
						if (up) {
 | 
				
			||||||
		update_fd_settings(link->ofd->fd);
 | 
					 | 
				
			||||||
		start_test_procedure(gsupc);
 | 
							start_test_procedure(gsupc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)
 | 
							if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)
 | 
				
			||||||
			gsup_client_oap_register(gsupc);
 | 
								gsup_client_oap_register(gsupc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		osmo_timer_del(&gsupc->connect_timer);
 | 
							osmo_timer_del(&gsupc->connect_timer);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (gsupc->up_down_cb)
 | 
					 | 
				
			||||||
			gsupc->up_down_cb(gsupc, true);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		osmo_timer_del(&gsupc->ping_timer);
 | 
							osmo_timer_del(&gsupc->ping_timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (gsupc->up_down_cb) {
 | 
					 | 
				
			||||||
			/* When the up_down_cb() returns false, the user asks us not to retry connecting. */
 | 
					 | 
				
			||||||
			if (!gsupc->up_down_cb(gsupc, false))
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		osmo_timer_schedule(&gsupc->connect_timer,
 | 
							osmo_timer_schedule(&gsupc->connect_timer,
 | 
				
			||||||
				    OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0);
 | 
									    OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -294,40 +263,42 @@ static void start_test_procedure(struct osmo_gsup_client *gsupc)
 | 
				
			|||||||
 * Use the provided ipaccess unit as the client-side identifier; ipa_dev should
 | 
					 * Use the provided ipaccess unit as the client-side identifier; ipa_dev should
 | 
				
			||||||
 * be allocated in talloc_ctx talloc_ctx as well.
 | 
					 * be allocated in talloc_ctx talloc_ctx as well.
 | 
				
			||||||
 * \param[in] talloc_ctx talloc context.
 | 
					 * \param[in] talloc_ctx talloc context.
 | 
				
			||||||
 * \param[in] config  Parameters for setting up the GSUP client.
 | 
					 * \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
 | 
				
			||||||
 * \return a GSUP client connection, or NULL on failure.
 | 
					 *                    in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
 | 
				
			||||||
 | 
					 * \param[in] ip_addr GSUP server IP address.
 | 
				
			||||||
 | 
					 * \param[in] tcp_port GSUP server TCP port.
 | 
				
			||||||
 | 
					 * \param[in] read_cb callback for reading from the GSUP connection.
 | 
				
			||||||
 | 
					 * \param[in] oapc_config OPA client configuration.
 | 
				
			||||||
 | 
					 *  \returns a GSUP client connection or NULL on failure.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_gsup_client_config *config)
 | 
					struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
 | 
				
			||||||
 | 
											  struct ipaccess_unit *ipa_dev,
 | 
				
			||||||
 | 
											  const char *ip_addr,
 | 
				
			||||||
 | 
											  unsigned int tcp_port,
 | 
				
			||||||
 | 
											  osmo_gsup_client_read_cb_t read_cb,
 | 
				
			||||||
 | 
											  struct osmo_oap_client_config *oapc_config)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_client *gsupc;
 | 
						struct osmo_gsup_client *gsupc;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(config->ipa_dev->unit_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
 | 
						gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
 | 
				
			||||||
	OSMO_ASSERT(gsupc);
 | 
						OSMO_ASSERT(gsupc);
 | 
				
			||||||
	*gsupc = (struct osmo_gsup_client){
 | 
						gsupc->unit_name = (const char *)ipa_dev->unit_name; /* API backwards compat */
 | 
				
			||||||
		.unit_name = (const char *)config->ipa_dev->unit_name, /* API backwards compat */
 | 
						gsupc->ipa_dev = ipa_dev;
 | 
				
			||||||
		.ipa_dev = config->ipa_dev,
 | 
					 | 
				
			||||||
		.read_cb = config->read_cb,
 | 
					 | 
				
			||||||
		.up_down_cb = config->up_down_cb,
 | 
					 | 
				
			||||||
		.data = config->data,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* a NULL oapc_config will mark oap_state disabled. */
 | 
						/* a NULL oapc_config will mark oap_state disabled. */
 | 
				
			||||||
	rc = osmo_oap_client_init(config->oapc_config, &gsupc->oap_state);
 | 
						rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
 | 
				
			||||||
	if (rc != 0)
 | 
						if (rc != 0)
 | 
				
			||||||
		goto failed;
 | 
							goto failed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gsupc->link = ipa_client_conn_create2(gsupc,
 | 
						gsupc->link = ipa_client_conn_create(gsupc,
 | 
				
			||||||
					      /* no e1inp */ NULL,
 | 
										     /* no e1inp */ NULL,
 | 
				
			||||||
					      0,
 | 
										     0,
 | 
				
			||||||
					      /* no specific local IP:port */ NULL, 0,
 | 
										     ip_addr, tcp_port,
 | 
				
			||||||
					      config->ip_addr, config->tcp_port,
 | 
										     gsup_client_updown_cb,
 | 
				
			||||||
					      gsup_client_updown_cb,
 | 
										     gsup_client_read_cb,
 | 
				
			||||||
					      gsup_client_read_cb,
 | 
										     /* default write_cb */ NULL,
 | 
				
			||||||
					      /* default write_cb */ NULL,
 | 
										     gsupc);
 | 
				
			||||||
					      gsupc);
 | 
					 | 
				
			||||||
	if (!gsupc->link)
 | 
						if (!gsupc->link)
 | 
				
			||||||
		goto failed;
 | 
							goto failed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -337,6 +308,9 @@ struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
		goto failed;
 | 
							goto failed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gsupc->read_cb = read_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return gsupc;
 | 
						return gsupc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
failed:
 | 
					failed:
 | 
				
			||||||
@@ -344,26 +318,6 @@ failed:
 | 
				
			|||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Like osmo_gsup_client_create3() but without the up_down_cb and data arguments, and with the oapc_config argument in
 | 
					 | 
				
			||||||
 * a different position.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
 | 
					 | 
				
			||||||
						  struct ipaccess_unit *ipa_dev,
 | 
					 | 
				
			||||||
						  const char *ip_addr,
 | 
					 | 
				
			||||||
						  unsigned int tcp_port,
 | 
					 | 
				
			||||||
						  osmo_gsup_client_read_cb_t read_cb,
 | 
					 | 
				
			||||||
						  struct osmo_oap_client_config *oapc_config)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_client_config cfg = {
 | 
					 | 
				
			||||||
		.ipa_dev = ipa_dev,
 | 
					 | 
				
			||||||
		.ip_addr = ip_addr,
 | 
					 | 
				
			||||||
		.tcp_port = tcp_port,
 | 
					 | 
				
			||||||
		.oapc_config = oapc_config,
 | 
					 | 
				
			||||||
		.read_cb = read_cb,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	return osmo_gsup_client_create3(talloc_ctx, &cfg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Like osmo_gsup_client_create2() except it expects a unit name instead
 | 
					 * Like osmo_gsup_client_create2() except it expects a unit name instead
 | 
				
			||||||
 * of a full-blown ipacess_unit as the client-side identifier.
 | 
					 * of a full-blown ipacess_unit as the client-side identifier.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,416 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * All Rights Reserved
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU General Public License along
 | 
					 | 
				
			||||||
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <inttypes.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Create a new osmo_gsup_req record, decode GSUP and add to a provided list of requests.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Rationales:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * - osmo_gsup_req makes it easy to handle GSUP requests asynchronously. Before this, a GSUP message struct would be
 | 
					 | 
				
			||||||
 *   valid only within a read callback function, and would not survive asynchronous handling, because the struct often
 | 
					 | 
				
			||||||
 *   points directly into the received msgb. An osmo_gsup_req takes ownership of the msgb and ensures that the data
 | 
					 | 
				
			||||||
 *   remains valid, so that it can easily be queued for later handling.
 | 
					 | 
				
			||||||
 * - osmo_gsup_req unifies the composition of response messages to ensure that all IEs that identify it to belong to
 | 
					 | 
				
			||||||
 *   the initial request are preserved / derived, like the source_name, destination_name, session_id, etc (see
 | 
					 | 
				
			||||||
 *   osmo_gsup_make_response() for details).
 | 
					 | 
				
			||||||
 * - Deallocation of an osmo_gsup_req is implicit upon sending a response. The idea is that msgb memory leaks are a
 | 
					 | 
				
			||||||
 *   recurring source of bugs. By enforcing a request-response relation with implicit deallocation, osmo_gsup_req aims
 | 
					 | 
				
			||||||
 *   to help avoid most such memory leaks implicitly.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The typical GSUP message sequence is:
 | 
					 | 
				
			||||||
 *   -> rx request,
 | 
					 | 
				
			||||||
 *   <- tx response.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * With osmo_gsup_req we can easily expand to:
 | 
					 | 
				
			||||||
 *   -> rx request,
 | 
					 | 
				
			||||||
 *   ... wait asynchronously,
 | 
					 | 
				
			||||||
 *   <- tx response.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Only few GSUP conversations go beyond a 1:1 request-response match. But some have a session (e.g. USSD) or more
 | 
					 | 
				
			||||||
 * negotiation may happen before the initial request is completed (e.g. Update Location with interleaved Insert
 | 
					 | 
				
			||||||
 * Subscriber Data), so osmo_gsup_req also allows passing non-final responses.
 | 
					 | 
				
			||||||
 * The final_response flag allows for:
 | 
					 | 
				
			||||||
 *    -> rx request,
 | 
					 | 
				
			||||||
 *    ... wait async,
 | 
					 | 
				
			||||||
 *    <- tx intermediate message to same peer (final_response = false, req remains open),
 | 
					 | 
				
			||||||
 *    ... wait async,
 | 
					 | 
				
			||||||
 *    -> rx intermediate response,
 | 
					 | 
				
			||||||
 *    ... wait async,
 | 
					 | 
				
			||||||
 *    <- tx final response (final_response = true, req is deallocated).
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This function takes ownership of the msgb, which will, on success, be owned by the returned osmo_gsup_req instance
 | 
					 | 
				
			||||||
 * until osmo_gsup_req_free(). If a decoding error occurs, send an error response immediately, and return NULL.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The original CNI entity that sent the message is found in req->source_name. If the message was passed on by an
 | 
					 | 
				
			||||||
 * intermediate CNI peer, then req->via_proxy is set to the immediate peer, and it is the responsibility of the caller
 | 
					 | 
				
			||||||
 * to add req->source_name to the GSUP routes that are serviced by req->via_proxy (usually not relevant for clients with
 | 
					 | 
				
			||||||
 * a single GSUP conn).
 | 
					 | 
				
			||||||
 * Examples:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *   "msc" ---> here
 | 
					 | 
				
			||||||
 *   source_name = "msc"
 | 
					 | 
				
			||||||
 *   via_proxy = <empty>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *   "msc" ---> "proxy-HLR" ---> here (e.g. home HLR)
 | 
					 | 
				
			||||||
 *   source_name = "msc"
 | 
					 | 
				
			||||||
 *   via_proxy = "proxy-HLR"
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *   "msc" ---> "proxy-HLR" ---> "home-HLR" ---> here (e.g. EUSE)
 | 
					 | 
				
			||||||
 *   source_name = "msc"
 | 
					 | 
				
			||||||
 *   via_proxy = "home-HLR"
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * An osmo_gsup_req must be concluded (and deallocated) by calling one of the osmo_gsup_req_respond* functions.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Note: osmo_gsup_req API makes use of OTC_SELECT to allocate volatile buffers for logging. Use of
 | 
					 | 
				
			||||||
 * osmo_select_main_ctx() is mandatory when using osmo_gsup_req.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param[in] ctx  Talloc context for allocation of the new request.
 | 
					 | 
				
			||||||
 * \param[in] from_peer  The IPA unit name of the immediate GSUP peer from which this msgb was received.
 | 
					 | 
				
			||||||
 * \param[in] msg  The message buffer containing the received GSUP message, where msgb_l2() shall point to the GSUP
 | 
					 | 
				
			||||||
 *                 message start. The caller no longer owns the msgb when it is passed to this function: on error, the
 | 
					 | 
				
			||||||
 *                 msgb is freed immediately, and on success, the msgb is owned by the returned osmo_gsup_req.
 | 
					 | 
				
			||||||
 * \param[in] send_response_cb  User specific method to send a GSUP response message, invoked upon
 | 
					 | 
				
			||||||
 *				osmo_gsup_req_respond*() functions. Typically this invokes encoding and transmitting the
 | 
					 | 
				
			||||||
 *				GSUP message over a network socket. See for example gsup_server_send_req_response().
 | 
					 | 
				
			||||||
 * \param[inout] cb_data  Context data to be used freely by the caller.
 | 
					 | 
				
			||||||
 * \param[inout] add_to_list  List to which to append this request, or NULL for no list.
 | 
					 | 
				
			||||||
 * \return a newly allocated osmo_gsup_req, or NULL on error. If NULL is returned, an error response has already been
 | 
					 | 
				
			||||||
 *         dispatched to the send_response_cb.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
 | 
					 | 
				
			||||||
					osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
 | 
					 | 
				
			||||||
					struct llist_head *add_to_list)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	static unsigned int next_req_nr = 1;
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *req;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!from_peer) {
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from NULL peer is not allowed\n");
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msgb_l2(msg) || !msgb_l2len(msg)) {
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n",
 | 
					 | 
				
			||||||
		     osmo_cni_peer_id_to_str(from_peer));
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req = talloc_zero(ctx, struct osmo_gsup_req);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(req);
 | 
					 | 
				
			||||||
	/* Note: req->gsup is declared const, so that the incoming message cannot be modified by handlers. */
 | 
					 | 
				
			||||||
	req->nr = next_req_nr++;
 | 
					 | 
				
			||||||
	req->msg = msg;
 | 
					 | 
				
			||||||
	req->send_response_cb = send_response_cb;
 | 
					 | 
				
			||||||
	req->cb_data = cb_data;
 | 
					 | 
				
			||||||
	req->source_name = *from_peer;
 | 
					 | 
				
			||||||
	rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);
 | 
					 | 
				
			||||||
	if (rc < 0) {
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_cni_peer_id_to_str(from_peer), rc);
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ(req, LOGL_DEBUG, "new request: {%s}\n", osmo_gsup_message_to_str_c(OTC_SELECT, &req->gsup));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req->gsup.source_name_len) {
 | 
					 | 
				
			||||||
		if (osmo_cni_peer_id_set(&req->source_name, OSMO_CNI_PEER_ID_IPA_NAME,
 | 
					 | 
				
			||||||
					  req->gsup.source_name, req->gsup.source_name_len)) {
 | 
					 | 
				
			||||||
			LOGP(DLGSUP, LOGL_ERROR,
 | 
					 | 
				
			||||||
			     "Rx GSUP from %s: failed to decode source_name, message is not routable\n",
 | 
					 | 
				
			||||||
			     osmo_cni_peer_id_to_str(from_peer));
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
 | 
					 | 
				
			||||||
			return NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* The source of the GSUP message is not the immediate GSUP peer; the peer is our proxy for that source.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		if (osmo_cni_peer_id_cmp(&req->source_name, from_peer))
 | 
					 | 
				
			||||||
			req->via_proxy = *from_peer;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_imsi_str_valid(req->gsup.imsi)) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid IMSI: %s",
 | 
					 | 
				
			||||||
					  osmo_quote_str(req->gsup.imsi, -1));
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (add_to_list)
 | 
					 | 
				
			||||||
		llist_add_tail(&req->entry, add_to_list);
 | 
					 | 
				
			||||||
	return req;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Free an osmo_gsup_req and its msgb -- this is usually implicit in osmo_gsup_req_resond_*(), it should not be
 | 
					 | 
				
			||||||
 * necessary to call this directly. */
 | 
					 | 
				
			||||||
void osmo_gsup_req_free(struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ(req, LOGL_DEBUG, "free\n");
 | 
					 | 
				
			||||||
	if (req->msg)
 | 
					 | 
				
			||||||
		msgb_free(req->msg);
 | 
					 | 
				
			||||||
	if (req->entry.prev)
 | 
					 | 
				
			||||||
		llist_del(&req->entry);
 | 
					 | 
				
			||||||
	talloc_free(req);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Send a response to a GSUP request.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Ensure that the response message contains all GSUP IEs that identify it as a response for the request req, by calling
 | 
					 | 
				
			||||||
 * osmo_gsup_make_response().
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The final complete response message is passed to req->send_response_cb() to take care of the transmission.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param req  Request as previously initialized by osmo_gsup_req_new().
 | 
					 | 
				
			||||||
 * \param response  Buffer to compose the response, possibly with some pre-configured IEs.
 | 
					 | 
				
			||||||
 *                  Any missing IEs are added via osmo_gsup_make_response().
 | 
					 | 
				
			||||||
 *                  Must not be NULL. Does not need to remain valid memory beyond the function call,
 | 
					 | 
				
			||||||
 *                  i.e. this can just be a local variable in the calling function.
 | 
					 | 
				
			||||||
 * \param error  True when the response message indicates an error response (error message type).
 | 
					 | 
				
			||||||
 * \param final_response  True when the request is concluded by this response, which deallocates the req.
 | 
					 | 
				
			||||||
 *                        False when the request should remain open after this response.
 | 
					 | 
				
			||||||
 *                        For most plain request->response GSUP messages, this should be True.
 | 
					 | 
				
			||||||
 * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond() macro.
 | 
					 | 
				
			||||||
 * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond() macro.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
 | 
					 | 
				
			||||||
			   bool error, bool final_response, const char *file, int line)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = osmo_gsup_make_response(response, &req->gsup, error, final_response);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "Invalid response (rc=%d): {%s}\n",
 | 
					 | 
				
			||||||
				 rc, osmo_gsup_message_to_str_c(OTC_SELECT, response));
 | 
					 | 
				
			||||||
		rc = -EINVAL;
 | 
					 | 
				
			||||||
		goto exit_cleanup;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!req->send_response_cb) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "No send_response_cb set, cannot send: {%s}\n",
 | 
					 | 
				
			||||||
				 osmo_gsup_message_to_str_c(OTC_SELECT, response));
 | 
					 | 
				
			||||||
		rc = -EINVAL;
 | 
					 | 
				
			||||||
		goto exit_cleanup;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG_GSUP_REQ_SRC(req, LOGL_DEBUG, file, line, "Tx response: {%s}\n",
 | 
					 | 
				
			||||||
			 osmo_gsup_message_to_str_c(OTC_SELECT, response));
 | 
					 | 
				
			||||||
	req->send_response_cb(req, response);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exit_cleanup:
 | 
					 | 
				
			||||||
	if (final_response)
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Shorthand for _osmo_gsup_req_respond() with no additional IEs and a fixed message type.
 | 
					 | 
				
			||||||
 * Set the message type in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
 | 
					 | 
				
			||||||
 * That will ensure to add all IEs that identify it as a response to req.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param req  Request as previously initialized by osmo_gsup_req_new().
 | 
					 | 
				
			||||||
 * \param message_type  The GSUP message type discriminator to respond with.
 | 
					 | 
				
			||||||
 * \param final_response  True when the request is concluded by this response, which deallocates the req.
 | 
					 | 
				
			||||||
 *                        False when the request should remain open after this response.
 | 
					 | 
				
			||||||
 *                        For most plain request->response GSUP messages, this should be True.
 | 
					 | 
				
			||||||
 * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond_msgt() macro.
 | 
					 | 
				
			||||||
 * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond_msgt() macro.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
 | 
					 | 
				
			||||||
				bool final_response, const char *file, int line)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_message response = {
 | 
					 | 
				
			||||||
		.message_type = message_type,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	return _osmo_gsup_req_respond(req, &response, OSMO_GSUP_IS_MSGT_ERROR(message_type), final_response,
 | 
					 | 
				
			||||||
				      file, line);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Shorthand for _osmo_gsup_req_respond() with an error cause IEs and using the req's matched error message type.
 | 
					 | 
				
			||||||
 * Set the error cause in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
 | 
					 | 
				
			||||||
 * That will ensure to add all IEs that identify it as a response to req.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Responding with an error always implies a final response: req is implicitly deallocated.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param req  Request as previously initialized by osmo_gsup_req_new().
 | 
					 | 
				
			||||||
 * \param cause  The error cause to include in a OSMO_GSUP_CAUSE_IE.
 | 
					 | 
				
			||||||
 * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond_err() macro.
 | 
					 | 
				
			||||||
 * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond_err() macro.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
 | 
					 | 
				
			||||||
				const char *file, int line)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_message response = {
 | 
					 | 
				
			||||||
		.cause = cause,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
 | 
					 | 
				
			||||||
	if (!OSMO_GSUP_IS_MSGT_REQUEST(req->gsup.message_type)) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_gsup_req_respond(req, &response, true, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! This function is implicitly called by the osmo_gsup_req API, if at all possible rather use osmo_gsup_req_respond().
 | 
					 | 
				
			||||||
 * This function is non-static mostly to allow unit testing.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Set fields, if still unset, that need to be copied from a received message over to its response message, to ensure
 | 
					 | 
				
			||||||
 * the response can be routed back to the requesting peer even via GSUP proxies.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Note: after calling this function, fields in the reply may reference the same memory as rx and are not deep-copied,
 | 
					 | 
				
			||||||
 * as is the usual way we are handling decoded GSUP messages.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * These fields are set in the reply message, iff they are still unset:
 | 
					 | 
				
			||||||
 * - Set reply->message_type to the rx's matching RESULT code (or ERROR code if error == true).
 | 
					 | 
				
			||||||
 * - IMSI,
 | 
					 | 
				
			||||||
 * - Set reply->destination_name to rx->source_name (for proxy routing),
 | 
					 | 
				
			||||||
 * - sm_rp_mr (for SMS),
 | 
					 | 
				
			||||||
 * - session_id (for SS/USSD),
 | 
					 | 
				
			||||||
 * - if rx->session_state is not NONE, set tx->session_state depending on the final_response argument:
 | 
					 | 
				
			||||||
 *   If false, set to OSMO_GSUP_SESSION_STATE_CONTINUE, else OSMO_GSUP_SESSION_STATE_END.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * If values in reply are already set, they will not be overwritten. The return code is an optional way of finding out
 | 
					 | 
				
			||||||
 * whether all values that were already set in 'reply' are indeed matching the 'rx' values that would have been set.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param[in] rx  Received GSUP message that is being replied to.
 | 
					 | 
				
			||||||
 * \param[inout] reply  The message that should be the response to rx, either empty or with some values already set up.
 | 
					 | 
				
			||||||
 * \return 0 if the resulting message is a valid response for rx, nonzero otherwise. A nonzero rc has no effect on the
 | 
					 | 
				
			||||||
 *         values set in the reply message: all unset fields are first updated, and then the rc is determined.
 | 
					 | 
				
			||||||
 *         The rc is intended to merely warn if the reply message already contained data that is incompatible with rx,
 | 
					 | 
				
			||||||
 *         e.g. a mismatching IMSI.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int osmo_gsup_make_response(struct osmo_gsup_message *reply,
 | 
					 | 
				
			||||||
			    const struct osmo_gsup_message *rx, bool error, bool final_response)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!reply->message_type) {
 | 
					 | 
				
			||||||
		if (error)
 | 
					 | 
				
			||||||
			reply->message_type = OSMO_GSUP_TO_MSGT_ERROR(rx->message_type);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			reply->message_type = OSMO_GSUP_TO_MSGT_RESULT(rx->message_type);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (*reply->imsi == '\0')
 | 
					 | 
				
			||||||
		OSMO_STRLCPY_ARRAY(reply->imsi, rx->imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (reply->message_class == OSMO_GSUP_MESSAGE_CLASS_UNSET)
 | 
					 | 
				
			||||||
		reply->message_class = rx->message_class;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!reply->destination_name || !reply->destination_name_len) {
 | 
					 | 
				
			||||||
		reply->destination_name = rx->source_name;
 | 
					 | 
				
			||||||
		reply->destination_name_len = rx->source_name_len;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* RP-Message-Reference is mandatory for SM Service */
 | 
					 | 
				
			||||||
	if (!reply->sm_rp_mr)
 | 
					 | 
				
			||||||
		reply->sm_rp_mr = rx->sm_rp_mr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* For SS/USSD, it's important to keep both session state and ID IEs */
 | 
					 | 
				
			||||||
	if (!reply->session_id)
 | 
					 | 
				
			||||||
		reply->session_id = rx->session_id;
 | 
					 | 
				
			||||||
	if (rx->session_state != OSMO_GSUP_SESSION_STATE_NONE
 | 
					 | 
				
			||||||
	    && reply->session_state == OSMO_GSUP_SESSION_STATE_NONE) {
 | 
					 | 
				
			||||||
		if (final_response || rx->session_state == OSMO_GSUP_SESSION_STATE_END)
 | 
					 | 
				
			||||||
			reply->session_state = OSMO_GSUP_SESSION_STATE_END;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			reply->session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(reply->imsi, rx->imsi))
 | 
					 | 
				
			||||||
		rc |= 1 << 0;
 | 
					 | 
				
			||||||
	if (reply->message_class != rx->message_class)
 | 
					 | 
				
			||||||
		rc |= 1 << 1;
 | 
					 | 
				
			||||||
	if (rx->sm_rp_mr && (!reply->sm_rp_mr || *rx->sm_rp_mr != *reply->sm_rp_mr))
 | 
					 | 
				
			||||||
		rc |= 1 << 2;
 | 
					 | 
				
			||||||
	if (reply->session_id != rx->session_id)
 | 
					 | 
				
			||||||
		rc |= 1 << 3;
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Print the most important value of a GSUP message to a string buffer in human readable form.
 | 
					 | 
				
			||||||
 * \param[out] buf  The buffer to write to.
 | 
					 | 
				
			||||||
 * \param[out] buflen  sizeof(buf).
 | 
					 | 
				
			||||||
 * \param[in] msg  GSUP message to print.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
size_t osmo_gsup_message_to_str_buf(char *buf, size_t buflen, const struct osmo_gsup_message *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
					 | 
				
			||||||
	if (!msg) {
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, "NULL");
 | 
					 | 
				
			||||||
		return sb.chars_needed;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->message_class)
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, "%s ", osmo_gsup_message_class_name(msg->message_class));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OSMO_STRBUF_PRINTF(sb, "%s:", osmo_gsup_message_type_name(msg->message_type));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OSMO_STRBUF_PRINTF(sb, " imsi=");
 | 
					 | 
				
			||||||
	OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, msg->imsi, strnlen(msg->imsi, sizeof(msg->imsi)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->cause)
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " cause=%s", get_value_string(gsm48_gmm_cause_names, msg->cause));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (msg->cn_domain) {
 | 
					 | 
				
			||||||
	case OSMO_GSUP_CN_DOMAIN_CS:
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " cn_domain=CS");
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case OSMO_GSUP_CN_DOMAIN_PS:
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " cn_domain=PS");
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		if (msg->cn_domain)
 | 
					 | 
				
			||||||
			OSMO_STRBUF_PRINTF(sb, " cn_domain=?(%d)", msg->cn_domain);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->source_name_len) {
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " source_name=");
 | 
					 | 
				
			||||||
		OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->source_name, msg->source_name_len);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->destination_name_len) {
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " destination_name=");
 | 
					 | 
				
			||||||
		OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->destination_name, msg->destination_name_len);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->session_id)
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " session_id=%" PRIu32, msg->session_id);
 | 
					 | 
				
			||||||
	if (msg->session_state)
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " session_state=%s", osmo_gsup_session_state_name(msg->session_state));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (msg->sm_rp_mr)
 | 
					 | 
				
			||||||
		OSMO_STRBUF_PRINTF(sb, " sm_rp_mr=%" PRIu8, *msg->sm_rp_mr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return sb.chars_needed;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Same as  osmo_gsup_message_to_str_buf() but returns a talloc allocated string. */
 | 
					 | 
				
			||||||
char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gsup_message_to_str_buf, msg)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										582
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										582
									
								
								src/hlr.c
									
									
									
									
									
								
							@@ -31,15 +31,12 @@
 | 
				
			|||||||
#include <osmocom/vty/command.h>
 | 
					#include <osmocom/vty/command.h>
 | 
				
			||||||
#include <osmocom/vty/telnet_interface.h>
 | 
					#include <osmocom/vty/telnet_interface.h>
 | 
				
			||||||
#include <osmocom/vty/ports.h>
 | 
					#include <osmocom/vty/ports.h>
 | 
				
			||||||
#include <osmocom/vty/cpu_sched_vty.h>
 | 
					 | 
				
			||||||
#include <osmocom/ctrl/control_vty.h>
 | 
					#include <osmocom/ctrl/control_vty.h>
 | 
				
			||||||
#include <osmocom/gsm/apn.h>
 | 
					#include <osmocom/gsm/apn.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
					#include <osmocom/gsm/gsm48_ie.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm_utils.h>
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
#include <osmocom/mslookup/mslookup_client.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					#include <osmocom/hlr/hlr.h>
 | 
				
			||||||
#include <osmocom/hlr/ctrl.h>
 | 
					#include <osmocom/hlr/ctrl.h>
 | 
				
			||||||
@@ -47,23 +44,14 @@
 | 
				
			|||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
#include <osmocom/hlr/gsup_router.h>
 | 
					#include <osmocom/hlr/gsup_router.h>
 | 
				
			||||||
#include <osmocom/hlr/rand.h>
 | 
					#include <osmocom/hlr/rand.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/luop.h>
 | 
				
			||||||
#include <osmocom/hlr/hlr_vty.h>
 | 
					#include <osmocom/hlr/hlr_vty.h>
 | 
				
			||||||
#include <osmocom/hlr/hlr_ussd.h>
 | 
					#include <osmocom/hlr/hlr_ussd.h>
 | 
				
			||||||
#include <osmocom/hlr/dgsm.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/proxy.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/lu_fsm.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mdns.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr *g_hlr;
 | 
					struct hlr *g_hlr;
 | 
				
			||||||
static void *hlr_ctx = NULL;
 | 
					static void *hlr_ctx = NULL;
 | 
				
			||||||
static int quit = 0;
 | 
					static int quit = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_tdef g_hlr_tdefs[] = {
 | 
					 | 
				
			||||||
	/* 4222 is also the OSMO_GSUP_PORT */
 | 
					 | 
				
			||||||
	{ .T = -4222, .default_val = 30, .desc = "GSUP Update Location timeout" },
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
					/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \param[in] subscr  A subscriber we have new data to send for.
 | 
					 * \param[in] subscr  A subscriber we have new data to send for.
 | 
				
			||||||
@@ -81,8 +69,6 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
 | 
				
			|||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: send only to current vlr_number and sgsn_number */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(co, &g_hlr->gs->clients, list) {
 | 
						llist_for_each_entry(co, &g_hlr->gs->clients, list) {
 | 
				
			||||||
		struct osmo_gsup_message gsup = { };
 | 
							struct osmo_gsup_message gsup = { };
 | 
				
			||||||
		uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
 | 
							uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
 | 
				
			||||||
@@ -236,111 +222,145 @@ static int subscr_create_on_demand(const char *imsi)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
 | 
					 | 
				
			||||||
 * \param[in,out] hlr  Global hlr context.
 | 
					 | 
				
			||||||
 * \param[in] subscr   Subscriber from a fresh db_subscr_get_by_*() call.
 | 
					 | 
				
			||||||
 * \param[in] nam_val  True to enable CS/PS, false to disable.
 | 
					 | 
				
			||||||
 * \param[in] is_ps    True to enable/disable PS, false for CS.
 | 
					 | 
				
			||||||
 * \returns 0 on success, ENOEXEC if there is no need to change, a negative
 | 
					 | 
				
			||||||
 *          value on error.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name vlr_name;
 | 
					 | 
				
			||||||
	struct osmo_gsup_message gsup_del_data = {
 | 
					 | 
				
			||||||
		.message_type = OSMO_GSUP_MSGT_DELETE_DATA_REQUEST,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(gsup_del_data.imsi, subscr->imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (is_val == nam_val) {
 | 
					 | 
				
			||||||
		LOGP(DAUC, LOGL_DEBUG, "IMSI-%s: Already has the requested value when asked to %s %s\n",
 | 
					 | 
				
			||||||
		     subscr->imsi, nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
 | 
					 | 
				
			||||||
		return ENOEXEC;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
 | 
					 | 
				
			||||||
	if (rc)
 | 
					 | 
				
			||||||
		return rc > 0? -rc : rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If we're disabling, send a notice out to the GSUP client that is
 | 
					 | 
				
			||||||
	 * responsible. Otherwise no need. */
 | 
					 | 
				
			||||||
	if (nam_val)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (subscr->vlr_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
 | 
					 | 
				
			||||||
		osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
 | 
					 | 
				
			||||||
	if (subscr->sgsn_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))
 | 
					 | 
				
			||||||
		osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
 * Send Auth Info handling
 | 
					 * Send Auth Info handling
 | 
				
			||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* process an incoming SAI request */
 | 
					/* process an incoming SAI request */
 | 
				
			||||||
static int rx_send_auth_info(struct osmo_gsup_req *req)
 | 
					static int rx_send_auth_info(struct osmo_gsup_conn *conn,
 | 
				
			||||||
 | 
								     const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								     struct db_context *dbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_message gsup_out = {
 | 
						struct osmo_gsup_message gsup_out;
 | 
				
			||||||
		.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
 | 
						struct msgb *msg_out;
 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	bool separation_bit = false;
 | 
						bool separation_bit = false;
 | 
				
			||||||
	int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
 | 
						int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
 | 
				
			||||||
	unsigned int auc_3g_ind;
 | 
					 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subscr_create_on_demand(req->gsup.imsi);
 | 
						subscr_create_on_demand(gsup->imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS)
 | 
						/* initialize return message structure */
 | 
				
			||||||
 | 
						memset(&gsup_out, 0, sizeof(gsup_out));
 | 
				
			||||||
 | 
						memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup->current_rat_type == OSMO_RAT_EUTRAN_SGS)
 | 
				
			||||||
		separation_bit = true;
 | 
							separation_bit = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (req->gsup.num_auth_vectors > 0 &&
 | 
						if (gsup->num_auth_vectors > 0 &&
 | 
				
			||||||
			req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
 | 
								gsup->num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
 | 
				
			||||||
		num_auth_vectors = req->gsup.num_auth_vectors;
 | 
							num_auth_vectors = gsup->num_auth_vectors;
 | 
				
			||||||
	rc = db_ind(g_hlr->dbc, &req->source_name, &auc_3g_ind);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_ERROR,
 | 
					 | 
				
			||||||
			     "Unable to determine 3G auth IND for source %s (rc=%d),"
 | 
					 | 
				
			||||||
			     " generating tuples with IND = 0\n",
 | 
					 | 
				
			||||||
			     osmo_cni_peer_id_to_str(&req->source_name), rc);
 | 
					 | 
				
			||||||
		auc_3g_ind = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,
 | 
						rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
 | 
				
			||||||
			gsup_out.auth_vectors,
 | 
								gsup_out.auth_vectors,
 | 
				
			||||||
			num_auth_vectors,
 | 
								num_auth_vectors,
 | 
				
			||||||
			req->gsup.rand, req->gsup.auts, separation_bit);
 | 
								gsup->rand, gsup->auts, separation_bit);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc <= 0) {
 | 
						if (rc <= 0) {
 | 
				
			||||||
 | 
							gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
 | 
				
			||||||
		switch (rc) {
 | 
							switch (rc) {
 | 
				
			||||||
		case 0:
 | 
							case 0:
 | 
				
			||||||
			/* 0 means "0 tuples generated", which shouldn't happen.
 | 
								/* 0 means "0 tuples generated", which shouldn't happen.
 | 
				
			||||||
			 * Treat the same as "no auth data". */
 | 
								 * Treat the same as "no auth data". */
 | 
				
			||||||
		case -ENOKEY:
 | 
							case -ENOKEY:
 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN,
 | 
								LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"
 | 
				
			||||||
						  "IMSI known, but has no auth data;"
 | 
								     " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",
 | 
				
			||||||
						  " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
 | 
								     gsup->imsi);
 | 
				
			||||||
			return rc;
 | 
								gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		case -ENOENT:
 | 
							case -ENOENT:
 | 
				
			||||||
			osmo_gsup_req_respond_err(req, g_hlr->reject_cause, "IMSI unknown");
 | 
								LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
 | 
				
			||||||
			return rc;
 | 
								gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
 | 
								LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
 | 
				
			||||||
			return rc;
 | 
								gsup_out.cause = GMM_CAUSE_NET_FAIL;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
 | 
				
			||||||
 | 
							gsup_out.num_auth_vectors = rc;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gsup_out.num_auth_vectors = rc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_gsup_req_respond(req, &gsup_out, false, true);
 | 
						msg_out = osmo_gsup_msgb_alloc("GSUP AUC response");
 | 
				
			||||||
	return 0;
 | 
						osmo_gsup_encode(msg_out, &gsup_out);
 | 
				
			||||||
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Receive Update Location Request, creates new lu_operation */
 | 
					/***********************************************************************
 | 
				
			||||||
static int rx_upd_loc_req(struct osmo_gsup_conn *conn, struct osmo_gsup_req *req)
 | 
					 * LU Operation State / Structure
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static LLIST_HEAD(g_lu_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Receive Cancel Location Result from old VLR/SGSN */
 | 
				
			||||||
 | 
					void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
 | 
				
			||||||
 | 
								     const struct osmo_gsup_message *gsup)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (req->gsup.cn_domain) {
 | 
						OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
 | 
				
			||||||
 | 
						/* FIXME: Check for spoofing */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_timer_del(&luop->timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* FIXME */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_tx_insert_subscr_data(luop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
 | 
				
			||||||
 | 
					static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
 | 
				
			||||||
 | 
									    const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
 | 
				
			||||||
 | 
						/* FIXME: Check for spoofing */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_timer_del(&luop->timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Subscriber_Present_HLR */
 | 
				
			||||||
 | 
						/* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Send final ACK towards inquiring VLR/SGSN */
 | 
				
			||||||
 | 
						lu_op_tx_ack(luop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Receive GSUP message for given \ref lu_operation */
 | 
				
			||||||
 | 
					void lu_op_rx_gsup(struct lu_operation *luop,
 | 
				
			||||||
 | 
							  const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (gsup->message_type) {
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
				
			||||||
 | 
							/* FIXME */
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
				
			||||||
 | 
							lu_op_rx_insert_subscr_data_ack(luop, gsup);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
 | 
				
			||||||
 | 
							/* FIXME */
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
 | 
				
			||||||
 | 
							lu_op_rx_cancel_old_ack(luop, gsup);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
 | 
				
			||||||
 | 
								gsup->message_type);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Receive Update Location Request, creates new \ref lu_operation */
 | 
				
			||||||
 | 
					static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			||||||
 | 
								  const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber *subscr;
 | 
				
			||||||
 | 
						struct lu_operation *luop = lu_op_alloc_conn(conn);
 | 
				
			||||||
 | 
						if (!luop) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						subscr = &luop->subscr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_statechg(luop, LU_S_LU_RECEIVED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (gsup->cn_domain) {
 | 
				
			||||||
	case OSMO_GSUP_CN_DOMAIN_CS:
 | 
						case OSMO_GSUP_CN_DOMAIN_CS:
 | 
				
			||||||
		conn->supports_cs = true;
 | 
							conn->supports_cs = true;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
@@ -351,64 +371,143 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn, struct osmo_gsup_req *req
 | 
				
			|||||||
		 * a request, the PS Domain is assumed." */
 | 
							 * a request, the PS Domain is assumed." */
 | 
				
			||||||
	case OSMO_GSUP_CN_DOMAIN_PS:
 | 
						case OSMO_GSUP_CN_DOMAIN_PS:
 | 
				
			||||||
		conn->supports_ps = true;
 | 
							conn->supports_ps = true;
 | 
				
			||||||
 | 
							luop->is_ps = true;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						llist_add(&luop->list, &g_lu_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subscr_create_on_demand(req->gsup.imsi);
 | 
						subscr_create_on_demand(gsup->imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* check if subscriber is known at all */
 | 
				
			||||||
 | 
						if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
 | 
				
			||||||
 | 
							/* Send Error back: Subscriber Unknown in HLR */
 | 
				
			||||||
 | 
							osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
 | 
				
			||||||
 | 
							lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check if subscriber is generally permitted on CS or PS
 | 
				
			||||||
 | 
						 * service (as requested) */
 | 
				
			||||||
 | 
						if (!luop->is_ps && !luop->subscr.nam_cs) {
 | 
				
			||||||
 | 
							lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						} else if (luop->is_ps && !luop->subscr.nam_ps) {
 | 
				
			||||||
 | 
							lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
 | 
				
			||||||
 | 
						if (luop->is_ps == false &&
 | 
				
			||||||
 | 
						    strcmp(subscr->vlr_number, vlr_number)) {
 | 
				
			||||||
 | 
							lu_op_tx_cancel_old(luop);
 | 
				
			||||||
 | 
						} else if (luop->is_ps == true &&
 | 
				
			||||||
 | 
							   strcmp(subscr->sgsn_number, sgsn_number)) {
 | 
				
			||||||
 | 
							lu_op_tx_cancel_old(luop);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
 | 
				
			||||||
 | 
						LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
 | 
				
			||||||
 | 
						     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
 | 
				
			||||||
 | 
						     osmo_quote_str((const char*)luop->peer, -1));
 | 
				
			||||||
 | 
						if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
 | 
				
			||||||
 | 
							     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* TODO: Subscriber allowed to roam in PLMN? */
 | 
				
			||||||
 | 
						/* TODO: Update RoutingInfo */
 | 
				
			||||||
 | 
						/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
				
			||||||
 | 
						/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
				
			||||||
 | 
						lu_op_tx_insert_subscr_data(luop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lu_rx_gsup(req);
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rx_purge_ms_req(struct osmo_gsup_req *req)
 | 
					static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
 | 
				
			||||||
 | 
								   const struct osmo_gsup_message *gsup)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool is_ps = (req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS);
 | 
						struct osmo_gsup_message gsup_reply = {0};
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
						bool is_ps = false;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOG_GSUP_REQ_CAT(req, DAUC, LOGL_INFO, "Purge MS (%s)\n", is_ps ? "PS" : "CS");
 | 
						LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
 | 
				
			||||||
 | 
							is_ps ? "PS" : "CS");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
 | 
				
			||||||
 | 
							is_ps = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: check if the VLR that sends the purge is the same that
 | 
						/* FIXME: check if the VLR that sends the purge is the same that
 | 
				
			||||||
	 * we have on record. Only update if yes */
 | 
						 * we have on record. Only update if yes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Perform the actual update of the DB */
 | 
						/* Perform the actual update of the DB */
 | 
				
			||||||
	rc = db_subscr_purge(g_hlr->dbc, req->gsup.imsi, true, is_ps);
 | 
						rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rc == 0)
 | 
						if (rc == 0)
 | 
				
			||||||
		osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);
 | 
							gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
 | 
				
			||||||
	else if (rc == -ENOENT)
 | 
						else if (rc == -ENOENT) {
 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
 | 
							gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
 | 
				
			||||||
	else
 | 
							gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");
 | 
						} else {
 | 
				
			||||||
	return rc;
 | 
							gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
 | 
				
			||||||
 | 
							gsup_reply.cause = GMM_CAUSE_NET_FAIL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg_out = osmo_gsup_msgb_alloc("GSUP AUC response");
 | 
				
			||||||
 | 
						osmo_gsup_encode(msg_out, &gsup_reply);
 | 
				
			||||||
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rx_check_imei_req(struct osmo_gsup_req *req)
 | 
					static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
 | 
				
			||||||
 | 
									enum osmo_gsup_message_type type_in, uint8_t err_cause)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_message gsup_reply;
 | 
						int type_err = OSMO_GSUP_TO_MSGT_ERROR(type_in);
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup_reply = {0};
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi);
 | 
				
			||||||
 | 
						gsup_reply.message_type = type_err;
 | 
				
			||||||
 | 
						gsup_reply.cause = err_cause;
 | 
				
			||||||
 | 
						msg_out = osmo_gsup_msgb_alloc("GSUP ERR response");
 | 
				
			||||||
 | 
						osmo_gsup_encode(msg_out, &gsup_reply);
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(type_err));
 | 
				
			||||||
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup_reply = {0};
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
	char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};
 | 
						char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};
 | 
				
			||||||
	const struct osmo_gsup_message *gsup = &req->gsup;
 | 
					 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Require IMEI */
 | 
						/* Require IMEI */
 | 
				
			||||||
	if (!gsup->imei_enc) {
 | 
						if (!gsup->imei_enc) {
 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "missing IMEI");
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: missing IMEI\n", gsup->imsi);
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Decode IMEI (fails if IMEI is too long) */
 | 
						/* Decode IMEI (fails if IMEI is too long) */
 | 
				
			||||||
	rc = gsm48_decode_bcd_number2(imei, sizeof(imei), gsup->imei_enc, gsup->imei_enc_len, 0);
 | 
						rc = gsm48_decode_bcd_number2(imei, sizeof(imei), gsup->imei_enc, gsup->imei_enc_len, 0);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: failed to decode IMEI (rc: %i)\n", gsup->imsi, rc);
 | 
				
			||||||
					  "failed to decode IMEI %s (rc: %d)",
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
					  osmo_hexdump_c(OTC_SELECT, gsup->imei_enc, gsup->imei_enc_len),
 | 
					 | 
				
			||||||
					  rc);
 | 
					 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check if IMEI is too short */
 | 
						/* Check if IMEI is too short */
 | 
				
			||||||
	if (!osmo_imei_str_valid(imei, false)) {
 | 
						if (strlen(imei) != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {
 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: wrong encoded IMEI length (IMEI: '%s', %lu, %i)\n", gsup->imsi, imei,
 | 
				
			||||||
					  "invalid IMEI: %s", osmo_quote_str_c(OTC_SELECT, imei, -1));
 | 
							     strlen(imei), GSM23003_IMEI_NUM_DIGITS_NO_CHK);
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -418,7 +517,7 @@ static int rx_check_imei_req(struct osmo_gsup_req *req)
 | 
				
			|||||||
	if (g_hlr->store_imei) {
 | 
						if (g_hlr->store_imei) {
 | 
				
			||||||
		LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);
 | 
							LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);
 | 
				
			||||||
		if (db_subscr_update_imei_by_imsi(g_hlr->dbc, gsup->imsi, imei) < 0) {
 | 
							if (db_subscr_update_imei_by_imsi(g_hlr->dbc, gsup->imsi, imei) < 0) {
 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Failed to store IMEI in HLR db");
 | 
								gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -426,154 +525,184 @@ static int rx_check_imei_req(struct osmo_gsup_req *req)
 | 
				
			|||||||
		LOGP(DMAIN, LOGL_INFO, "IMSI='%s': has IMEI = %s (consider setting 'store-imei')\n", gsup->imsi, imei);
 | 
							LOGP(DMAIN, LOGL_INFO, "IMSI='%s': has IMEI = %s (consider setting 'store-imei')\n", gsup->imsi, imei);
 | 
				
			||||||
		struct hlr_subscriber subscr;
 | 
							struct hlr_subscriber subscr;
 | 
				
			||||||
		if (db_subscr_get_by_imsi(g_hlr->dbc, gsup->imsi, &subscr) < 0) {
 | 
							if (db_subscr_get_by_imsi(g_hlr->dbc, gsup->imsi, &subscr) < 0) {
 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "IMSI unknown");
 | 
								gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Accept all IMEIs */
 | 
						/* Accept all IMEIs */
 | 
				
			||||||
	gsup_reply = (struct osmo_gsup_message){
 | 
						gsup_reply.imei_result = OSMO_GSUP_IMEI_RESULT_ACK;
 | 
				
			||||||
		.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
 | 
						gsup_reply.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT;
 | 
				
			||||||
		.imei_result = OSMO_GSUP_IMEI_RESULT_ACK,
 | 
						msg_out = osmo_gsup_msgb_alloc("GSUP Check_IMEI response");
 | 
				
			||||||
	};
 | 
						memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
 | 
				
			||||||
	return osmo_gsup_req_respond(req, &gsup_reply, false, true);
 | 
						osmo_gsup_encode(msg_out, &gsup_reply);
 | 
				
			||||||
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char namebuf[255];
 | 
					static char namebuf[255];
 | 
				
			||||||
#define LOGP_GSUP_FWD(gsup, level, fmt, args ...) \
 | 
					#define LOGP_GSUP_FWD(gsup, level, fmt, args ...) \
 | 
				
			||||||
	LOGP(DMAIN, level, "Forward %s (class=%s, IMSI=%s, %s->%s): " fmt, \
 | 
						LOGP(DMAIN, level, "Forward %s (class=%s, IMSI=%s, %s->%s): " fmt, \
 | 
				
			||||||
	     osmo_gsup_message_type_name((gsup)->message_type), \
 | 
						     osmo_gsup_message_type_name(gsup->message_type), \
 | 
				
			||||||
	     osmo_gsup_message_class_name((gsup)->message_class), \
 | 
						     osmo_gsup_message_class_name(gsup->message_class), \
 | 
				
			||||||
	     (gsup)->imsi, \
 | 
						     gsup->imsi, \
 | 
				
			||||||
	     osmo_quote_str((const char *)(gsup)->source_name, (gsup)->source_name_len), \
 | 
						     osmo_quote_str((const char *)gsup->source_name, gsup->source_name_len), \
 | 
				
			||||||
	     osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)(gsup)->destination_name, (gsup)->destination_name_len), \
 | 
						     osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)gsup->destination_name, gsup->destination_name_len), \
 | 
				
			||||||
	     ## args)
 | 
						     ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int read_cb_forward(struct osmo_gsup_req *req)
 | 
					static int read_cb_forward(struct osmo_gsup_conn *conn, struct msgb *msg, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = -EINVAL;
 | 
						int ret = -EINVAL;
 | 
				
			||||||
	const struct osmo_gsup_message *gsup = &req->gsup;
 | 
						struct osmo_gsup_message *gsup_err;
 | 
				
			||||||
	struct osmo_gsup_message gsup_err;
 | 
					
 | 
				
			||||||
	struct msgb *forward_msg;
 | 
						/* FIXME: it would be better if the msgb never were deallocated immediately by osmo_gsup_addr_send(), which a
 | 
				
			||||||
	struct osmo_ipa_name destination_name;
 | 
						 * select-loop volatile talloc context could facilitate. Then we would still be able to access gsup-> members
 | 
				
			||||||
 | 
						 * (pointing into the msgb) even after sending failed, and we wouldn't need to copy this data before sending: */
 | 
				
			||||||
 | 
						/* Prepare error message (before IEs get deallocated) */
 | 
				
			||||||
 | 
						gsup_err = talloc_zero(hlr_ctx, struct osmo_gsup_message);
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(gsup_err->imsi, gsup->imsi);
 | 
				
			||||||
 | 
						gsup_err->message_class = gsup->message_class;
 | 
				
			||||||
 | 
						gsup_err->destination_name = talloc_memdup(gsup_err, gsup->destination_name, gsup->destination_name_len);
 | 
				
			||||||
 | 
						gsup_err->destination_name_len = gsup->destination_name_len;
 | 
				
			||||||
 | 
						gsup_err->message_type = gsup->message_type;
 | 
				
			||||||
 | 
						gsup_err->session_state = gsup->session_state;
 | 
				
			||||||
 | 
						gsup_err->session_id = gsup->session_id;
 | 
				
			||||||
 | 
						gsup_err->source_name = talloc_memdup(gsup_err, gsup->source_name, gsup->source_name_len);
 | 
				
			||||||
 | 
						gsup_err->source_name_len = gsup->source_name_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check for routing IEs */
 | 
						/* Check for routing IEs */
 | 
				
			||||||
	if (!req->gsup.source_name || !req->gsup.source_name_len
 | 
						if (!gsup->source_name || !gsup->source_name_len || !gsup->destination_name || !gsup->destination_name_len) {
 | 
				
			||||||
	    || !req->gsup.destination_name || !req->gsup.destination_name_len) {
 | 
							LOGP_GSUP_FWD(gsup, LOGL_ERROR, "missing routing IEs\n");
 | 
				
			||||||
		LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");
 | 
							goto end;
 | 
				
			||||||
		goto routing_error;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)) {
 | 
						/* Verify source name (e.g. "MSC-00-00-00-00-00-00") */
 | 
				
			||||||
		LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "invalid destination name\n");
 | 
						if (gsup_route_find(conn->server, gsup->source_name, gsup->source_name_len) != conn) {
 | 
				
			||||||
		goto routing_error;
 | 
							LOGP_GSUP_FWD(gsup, LOGL_ERROR, "mismatching source name\n");
 | 
				
			||||||
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_ipa_name_to_str(&destination_name));
 | 
						/* Forward message without re-encoding (so we don't remove unknown IEs) */
 | 
				
			||||||
 | 
						LOGP_GSUP_FWD(gsup, LOGL_INFO, "checks passed, forwarding\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Forward message without re-encoding (so we don't remove unknown IEs).
 | 
						/* Remove incoming IPA header to be able to prepend an outgoing IPA header */
 | 
				
			||||||
	 * Copy GSUP part to forward, removing incoming IPA header to be able to prepend an outgoing IPA header */
 | 
						msgb_pull_to_l2(msg);
 | 
				
			||||||
	forward_msg = osmo_gsup_msgb_alloc("GSUP forward");
 | 
						ret = osmo_gsup_addr_send(g_hlr->gs, gsup->destination_name, gsup->destination_name_len, msg);
 | 
				
			||||||
	forward_msg->l2h = msgb_put(forward_msg, msgb_l2len(req->msg));
 | 
						/* AT THIS POINT, THE msg MAY BE DEALLOCATED and the data like gsup->imsi, gsup->source_name etc may all be
 | 
				
			||||||
	memcpy(forward_msg->l2h, msgb_l2(req->msg), msgb_l2len(req->msg));
 | 
						 * invalid and cause segfaults. */
 | 
				
			||||||
	ret = osmo_gsup_send_to_ipa_name(g_hlr->gs, &destination_name, forward_msg);
 | 
						msg = NULL;
 | 
				
			||||||
 | 
						gsup = NULL;
 | 
				
			||||||
 | 
						if (ret == -ENODEV)
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "destination not connected\n");
 | 
				
			||||||
 | 
						else if (ret)
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "unknown error %i\n", ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end:
 | 
				
			||||||
 | 
						/* Send error back to source */
 | 
				
			||||||
	if (ret) {
 | 
						if (ret) {
 | 
				
			||||||
		LOGP_GSUP_FWD(gsup, LOGL_ERROR, "%s (rc=%d)\n",
 | 
							struct msgb *msg_err = osmo_gsup_msgb_alloc("GSUP forward ERR response");
 | 
				
			||||||
			      ret == -ENODEV ? "destination not connected" : "unknown error",
 | 
							gsup_err->message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR;
 | 
				
			||||||
			      ret);
 | 
							osmo_gsup_encode(msg_err, gsup_err);
 | 
				
			||||||
		goto routing_error;
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(gsup_err->message_type));
 | 
				
			||||||
 | 
							osmo_gsup_conn_send(conn, msg_err);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	osmo_gsup_req_free(req);
 | 
						talloc_free(gsup_err);
 | 
				
			||||||
	return 0;
 | 
						if (msg)
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
routing_error:
 | 
						return ret;
 | 
				
			||||||
	gsup_err = (struct osmo_gsup_message){
 | 
					 | 
				
			||||||
		.message_type = OSMO_GSUP_MSGT_ROUTING_ERROR,
 | 
					 | 
				
			||||||
		.source_name = gsup->destination_name,
 | 
					 | 
				
			||||||
		.source_name_len = gsup->destination_name_len,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	osmo_gsup_req_respond(req, &gsup_err, true, true);
 | 
					 | 
				
			||||||
	return -1;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
					static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_req *req = osmo_gsup_conn_rx(conn, msg);
 | 
						static struct osmo_gsup_message gsup;
 | 
				
			||||||
	if (!req)
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!msgb_l2(msg) || !msgb_l2len(msg)) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "missing or empty L2 data\n");
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If the GSUP recipient is other than this HLR, forward. */
 | 
					 | 
				
			||||||
	if (req->gsup.destination_name_len) {
 | 
					 | 
				
			||||||
		struct osmo_ipa_name destination_name;
 | 
					 | 
				
			||||||
		struct osmo_ipa_name my_name;
 | 
					 | 
				
			||||||
		osmo_ipa_name_set_str(&my_name, g_hlr->gsup_unit_name.serno);
 | 
					 | 
				
			||||||
		if (!osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)
 | 
					 | 
				
			||||||
		    && osmo_ipa_name_cmp(&destination_name, &my_name)) {
 | 
					 | 
				
			||||||
			return read_cb_forward(req);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Distributed GSM: check whether to proxy for / lookup a remote HLR.
 | 
						rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
 | 
				
			||||||
	 * It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
 | 
						if (rc < 0) {
 | 
				
			||||||
	 * it becomes semantically easier if we do this once-off ahead of time. */
 | 
							LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
 | 
				
			||||||
	if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)
 | 
							msgb_free(msg);
 | 
				
			||||||
	    || osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) {
 | 
							return rc;
 | 
				
			||||||
		if (dgsm_check_forward_gsup_msg(req))
 | 
					 | 
				
			||||||
			return 0;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* HLR related messages that are handled at this HLR instance */
 | 
						/* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5
 | 
				
			||||||
	switch (req->gsup.message_type) {
 | 
						 * digits is impossible.  Even 5 digits is a highly theoretical case */
 | 
				
			||||||
 | 
						if (strlen(gsup.imsi) < 5) { /* TODO: move this check to libosmogsm/gsup.c? */
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "IMSI too short: %s\n", osmo_quote_str(gsup.imsi, -1));
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup.destination_name_len)
 | 
				
			||||||
 | 
							return read_cb_forward(conn, msg, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (gsup.message_type) {
 | 
				
			||||||
	/* requests sent to us */
 | 
						/* requests sent to us */
 | 
				
			||||||
	case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
 | 
						case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
 | 
				
			||||||
		rx_send_auth_info(req);
 | 
							rx_send_auth_info(conn, &gsup, g_hlr->dbc);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
 | 
						case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
 | 
				
			||||||
		rx_upd_loc_req(conn, req);
 | 
							rx_upd_loc_req(conn, &gsup);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
 | 
						case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
 | 
				
			||||||
		rx_purge_ms_req(req);
 | 
							rx_purge_ms_req(conn, &gsup);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	/* responses to requests sent by us */
 | 
						/* responses to requests sent by us */
 | 
				
			||||||
	case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:
 | 
						case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:
 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_ERROR, "Peer responds with: Error while deleting subscriber data\n");
 | 
							LOGP(DMAIN, LOGL_ERROR, "Error while deleting subscriber data "
 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
							     "for IMSI %s\n", gsup.imsi);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:
 | 
						case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:
 | 
				
			||||||
		LOG_GSUP_REQ(req, LOGL_DEBUG, "Peer responds with: Subscriber data deleted\n");
 | 
							LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
							     gsup.imsi);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
 | 
						case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_PROC_SS_RESULT:
 | 
						case OSMO_GSUP_MSGT_PROC_SS_RESULT:
 | 
				
			||||||
		rx_proc_ss_req(req);
 | 
							rx_proc_ss_req(conn, &gsup);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_PROC_SS_ERROR:
 | 
						case OSMO_GSUP_MSGT_PROC_SS_ERROR:
 | 
				
			||||||
		rx_proc_ss_error(req);
 | 
							rx_proc_ss_error(conn, &gsup);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
 | 
						case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
 | 
						case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
 | 
				
			||||||
		lu_rx_gsup(req);
 | 
							{
 | 
				
			||||||
 | 
								struct lu_operation *luop = lu_op_by_imsi(gsup.imsi,
 | 
				
			||||||
 | 
													  &g_lu_ops);
 | 
				
			||||||
 | 
								if (!luop) {
 | 
				
			||||||
 | 
									LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "
 | 
				
			||||||
 | 
									     "unknown IMSI %s\n",
 | 
				
			||||||
 | 
									     osmo_gsup_message_type_name(gsup.message_type),
 | 
				
			||||||
 | 
										gsup.imsi);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								lu_op_rx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
 | 
						case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
 | 
				
			||||||
		rx_check_imei_req(req);
 | 
							rx_check_imei_req(conn, &gsup);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
 | 
							LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
 | 
				
			||||||
		     osmo_gsup_message_type_name(req->gsup.message_type));
 | 
							     osmo_gsup_message_type_name(gsup.message_type));
 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						msgb_free(msg);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_usage(void)
 | 
					static void print_usage()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	printf("Usage: osmo-hlr\n");
 | 
						printf("Usage: osmo-hlr\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_help(void)
 | 
					static void print_help()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	printf("  -h --help                  This text.\n");
 | 
						printf("  -h --help                  This text.\n");
 | 
				
			||||||
	printf("  -c --config-file filename  The config file to use.\n");
 | 
						printf("  -c --config-file filename  The config file to use.\n");
 | 
				
			||||||
@@ -586,10 +715,6 @@ static void print_help(void)
 | 
				
			|||||||
	printf("  -U --db-upgrade            Allow HLR database schema upgrades.\n");
 | 
						printf("  -U --db-upgrade            Allow HLR database schema upgrades.\n");
 | 
				
			||||||
	printf("  -C --db-check              Quit after opening (and upgrading) the database.\n");
 | 
						printf("  -C --db-check              Quit after opening (and upgrading) the database.\n");
 | 
				
			||||||
	printf("  -V --version               Print the version of OsmoHLR.\n");
 | 
						printf("  -V --version               Print the version of OsmoHLR.\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 struct {
 | 
					static struct {
 | 
				
			||||||
@@ -605,37 +730,10 @@ static struct {
 | 
				
			|||||||
	.db_upgrade = false,
 | 
						.db_upgrade = false,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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)
 | 
					static void handle_options(int argc, char **argv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		int option_index = 0, c;
 | 
							int option_index = 0, c;
 | 
				
			||||||
		static int long_option = 0;
 | 
					 | 
				
			||||||
		static struct option long_options[] = {
 | 
							static struct option long_options[] = {
 | 
				
			||||||
			{"help", 0, 0, 'h'},
 | 
								{"help", 0, 0, 'h'},
 | 
				
			||||||
			{"config-file", 1, 0, 'c'},
 | 
								{"config-file", 1, 0, 'c'},
 | 
				
			||||||
@@ -648,8 +746,6 @@ static void handle_options(int argc, char **argv)
 | 
				
			|||||||
			{"db-upgrade", 0, 0, 'U' },
 | 
								{"db-upgrade", 0, 0, 'U' },
 | 
				
			||||||
			{"db-check", 0, 0, 'C' },
 | 
								{"db-check", 0, 0, 'C' },
 | 
				
			||||||
			{"version", 0, 0, 'V' },
 | 
								{"version", 0, 0, 'V' },
 | 
				
			||||||
			{"vty-ref-mode", 1, &long_option, 1},
 | 
					 | 
				
			||||||
			{"vty-ref-xml", 0, &long_option, 2},
 | 
					 | 
				
			||||||
			{0, 0, 0, 0}
 | 
								{0, 0, 0, 0}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -659,9 +755,6 @@ static void handle_options(int argc, char **argv)
 | 
				
			|||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (c) {
 | 
							switch (c) {
 | 
				
			||||||
		case 0:
 | 
					 | 
				
			||||||
			handle_long_options(argv[0], long_option);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 'h':
 | 
							case 'h':
 | 
				
			||||||
			print_usage();
 | 
								print_usage();
 | 
				
			||||||
			print_help();
 | 
								print_help();
 | 
				
			||||||
@@ -755,12 +848,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	INIT_LLIST_HEAD(&g_hlr->euse_list);
 | 
						INIT_LLIST_HEAD(&g_hlr->euse_list);
 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
						INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
						INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
 | 
					 | 
				
			||||||
	g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
 | 
						g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
 | 
				
			||||||
	g_hlr->mslookup.server.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
 | 
					 | 
				
			||||||
	g_hlr->mslookup.client.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
 | 
					 | 
				
			||||||
	g_hlr->reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
					 | 
				
			||||||
	g_hlr->no_proxy_reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Init default (call independent) SS session guard timeout value */
 | 
						/* Init default (call independent) SS session guard timeout value */
 | 
				
			||||||
	g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
 | 
						g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
 | 
				
			||||||
@@ -771,16 +859,11 @@ int main(int argc, char **argv)
 | 
				
			|||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set up llists and objects, startup is happening from VTY commands. */
 | 
					 | 
				
			||||||
	dgsm_init(hlr_ctx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_stats_init(hlr_ctx);
 | 
						osmo_stats_init(hlr_ctx);
 | 
				
			||||||
	vty_init(&vty_info);
 | 
						vty_init(&vty_info);
 | 
				
			||||||
	ctrl_vty_init(hlr_ctx);
 | 
						ctrl_vty_init(hlr_ctx);
 | 
				
			||||||
	hlr_vty_init(hlr_ctx);
 | 
					 | 
				
			||||||
	dgsm_vty_init();
 | 
					 | 
				
			||||||
	osmo_cpu_sched_vty_init(hlr_ctx);
 | 
					 | 
				
			||||||
	handle_options(argc, argv);
 | 
						handle_options(argc, argv);
 | 
				
			||||||
 | 
						hlr_vty_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = vty_read_config_file(cmdline_opts.config_file, NULL);
 | 
						rc = vty_read_config_file(cmdline_opts.config_file, NULL);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
@@ -818,23 +901,22 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
						/* start telnet after reading config for vty_get_bind_addr() */
 | 
				
			||||||
	rc = telnet_init_default(hlr_ctx, NULL, OSMO_VTY_PORT_HLR);
 | 
						rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
 | 
				
			||||||
 | 
								       OSMO_VTY_PORT_HLR);
 | 
				
			||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
		return rc;
 | 
							return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,
 | 
						g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,
 | 
				
			||||||
					    read_cb, g_hlr);
 | 
										    read_cb, &g_lu_ops, g_hlr);
 | 
				
			||||||
	if (!g_hlr->gs) {
 | 
						if (!g_hlr->gs) {
 | 
				
			||||||
		LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
 | 
							LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
 | 
				
			||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	proxy_init(g_hlr->gs);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
 | 
				
			||||||
	g_hlr->ctrl = hlr_controlif_setup(g_hlr);
 | 
						g_hlr->ctrl = hlr_controlif_setup(g_hlr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dgsm_start(hlr_ctx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_init_ignore_signals();
 | 
						osmo_init_ignore_signals();
 | 
				
			||||||
	signal(SIGINT, &signal_hdlr);
 | 
						signal(SIGINT, &signal_hdlr);
 | 
				
			||||||
	signal(SIGTERM, &signal_hdlr);
 | 
						signal(SIGTERM, &signal_hdlr);
 | 
				
			||||||
@@ -849,9 +931,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (!quit)
 | 
						while (!quit)
 | 
				
			||||||
		osmo_select_main_ctx(0);
 | 
							osmo_select_main(0);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	dgsm_stop();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_gsup_server_destroy(g_hlr->gs);
 | 
						osmo_gsup_server_destroy(g_hlr->gs);
 | 
				
			||||||
	db_close(g_hlr->dbc);
 | 
						db_close(g_hlr->dbc);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,6 @@
 | 
				
			|||||||
#include <getopt.h>
 | 
					#include <getopt.h>
 | 
				
			||||||
#include <inttypes.h>
 | 
					#include <inttypes.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
#include <osmocom/core/application.h>
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
@@ -51,7 +50,7 @@ static struct {
 | 
				
			|||||||
	.db_upgrade = false,
 | 
						.db_upgrade = false,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_help(void)
 | 
					static void print_help()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	printf("\n");
 | 
						printf("\n");
 | 
				
			||||||
	printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
 | 
						printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
 | 
				
			||||||
@@ -71,9 +70,8 @@ static void print_help(void)
 | 
				
			|||||||
	printf("                             (All commands imply this if none exists yet.)\n");
 | 
						printf("                             (All commands imply this if none exists yet.)\n");
 | 
				
			||||||
	printf("\n");
 | 
						printf("\n");
 | 
				
			||||||
	printf("  import-nitb-db <nitb.db>   Add OsmoNITB db's subscribers to OsmoHLR db.\n");
 | 
						printf("  import-nitb-db <nitb.db>   Add OsmoNITB db's subscribers to OsmoHLR db.\n");
 | 
				
			||||||
	printf("                             Be aware that the import is somewhat lossy, only the IMSI,\n");
 | 
						printf("                             Be aware that the import is lossy, only the\n");
 | 
				
			||||||
	printf("                             MSISDN, IMEI, nam_cs/ps, 2G auth data and last seen LU are set.\n");
 | 
						printf("                             IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
 | 
				
			||||||
	printf("                             The most recently associated IMEI from the Equipment table is used.\n");
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_version(int print_copyright)
 | 
					static void print_version(int print_copyright)
 | 
				
			||||||
@@ -214,15 +212,9 @@ enum nitb_stmt {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const char *nitb_stmt_sql[] = {
 | 
					static const char *nitb_stmt_sql[] = {
 | 
				
			||||||
	[NITB_SELECT_SUBSCR] =
 | 
						[NITB_SELECT_SUBSCR] =
 | 
				
			||||||
		"SELECT s.imsi, s.id, s.extension, s.authorized,"
 | 
							"SELECT imsi, id, extension, authorized"
 | 
				
			||||||
		" SUBSTR(e.imei,0,15), STRFTIME('%s', s.expire_lu)"
 | 
							" FROM Subscriber"
 | 
				
			||||||
		" FROM Subscriber s LEFT JOIN"
 | 
							" ORDER BY id",
 | 
				
			||||||
		" (SELECT imei, subscriber_id, MAX(Equipment.updated) AS updated"
 | 
					 | 
				
			||||||
		" FROM Equipment,EquipmentWatch"
 | 
					 | 
				
			||||||
		" WHERE Equipment.id = EquipmentWatch.equipment_id"
 | 
					 | 
				
			||||||
		" GROUP BY EquipmentWatch.subscriber_id) e"
 | 
					 | 
				
			||||||
		" ON e.subscriber_id = s.id"
 | 
					 | 
				
			||||||
		" ORDER by s.id",
 | 
					 | 
				
			||||||
	[NITB_SELECT_AUTH_KEYS] =
 | 
						[NITB_SELECT_AUTH_KEYS] =
 | 
				
			||||||
		"SELECT algorithm_id, a3a8_ki from authkeys"
 | 
							"SELECT algorithm_id, a3a8_ki from authkeys"
 | 
				
			||||||
		" WHERE subscriber_id = $subscr_id",
 | 
							" WHERE subscriber_id = $subscr_id",
 | 
				
			||||||
@@ -230,65 +222,8 @@ static const char *nitb_stmt_sql[] = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
 | 
					sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum hlr_db_stmt {
 | 
					 | 
				
			||||||
	HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const char *hlr_db_stmt_sql[] = {
 | 
					 | 
				
			||||||
	[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI] =
 | 
					 | 
				
			||||||
		"UPDATE subscriber SET last_lu_seen = datetime($last_lu, 'unixepoch') WHERE imsi = $imsi",
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sqlite3_stmt *hlr_db_stmt[ARRAY_SIZE(hlr_db_stmt_sql)] = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
 | 
					size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Set a subscriber's LU timestamp in the HLR database.
 | 
					 | 
				
			||||||
 * In normal operations there is never any need to explicitly
 | 
					 | 
				
			||||||
 * update the value of last_lu_seen, so this function can live here.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 | 
				
			||||||
 * \param[in] imsi  ASCII string of IMSI digits
 | 
					 | 
				
			||||||
 * \param[in] imei  ASCII string of identifier digits, or NULL to remove the IMEI.
 | 
					 | 
				
			||||||
 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
 | 
					 | 
				
			||||||
 *         -EIO on database errors.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int db_subscr_update_lu_by_imsi(struct db_context *dbc, const char* imsi, const int last_lu)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc, ret = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sqlite3_stmt *stmt = hlr_db_stmt[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!db_bind_text(stmt, "$imsi", imsi))
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
	if (last_lu && !db_bind_int(stmt, "$last_lu", last_lu))
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* execute the statement */
 | 
					 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
					 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
					 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL Error: %s\n", imsi,
 | 
					 | 
				
			||||||
		     sqlite3_errmsg(dbc->db));
 | 
					 | 
				
			||||||
		ret = -EIO;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* verify execution result */
 | 
					 | 
				
			||||||
	rc = sqlite3_changes(dbc->db);
 | 
					 | 
				
			||||||
	if (!rc) {
 | 
					 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Cannot update last_lu_seen for subscriber IMSI='%s': no such subscriber\n", imsi);
 | 
					 | 
				
			||||||
		ret = -ENOENT;
 | 
					 | 
				
			||||||
	} else if (rc != 1) {
 | 
					 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
 | 
					 | 
				
			||||||
		     imsi, rc);
 | 
					 | 
				
			||||||
		ret = -EIO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	db_remove_reset(stmt);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
 | 
					void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
@@ -362,7 +297,6 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
 | 
				
			|||||||
	int64_t imsi;
 | 
						int64_t imsi;
 | 
				
			||||||
	char imsi_str[32];
 | 
						char imsi_str[32];
 | 
				
			||||||
	bool authorized;
 | 
						bool authorized;
 | 
				
			||||||
	int last_lu_int;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	imsi = sqlite3_column_int64(stmt, 0);
 | 
						imsi = sqlite3_column_int64(stmt, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -381,18 +315,8 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
 | 
				
			|||||||
	nitb_id = sqlite3_column_int64(stmt, 1);
 | 
						nitb_id = sqlite3_column_int64(stmt, 1);
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
 | 
						copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
 | 
				
			||||||
	authorized = sqlite3_column_int(stmt, 3) ? true : false;
 | 
						authorized = sqlite3_column_int(stmt, 3) ? true : false;
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr.imei, stmt, 4);
 | 
					 | 
				
			||||||
	/* Default periodic LU was 30 mins and the expire_lu
 | 
					 | 
				
			||||||
	 * was twice that + 1 min
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	last_lu_int = sqlite3_column_int(stmt, 5) - 3660;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
 | 
						db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
 | 
				
			||||||
	/* In case the subscriber was somehow never seen, invent an IMEI */
 | 
					 | 
				
			||||||
	if (strlen(subscr.imei) == 14)
 | 
					 | 
				
			||||||
		db_subscr_update_imei_by_imsi(dbc, imsi_str, subscr.imei);
 | 
					 | 
				
			||||||
	db_subscr_update_lu_by_imsi(dbc, imsi_str, last_lu_int);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db_subscr_nam(dbc, imsi_str, authorized, true);
 | 
						db_subscr_nam(dbc, imsi_str, authorized, true);
 | 
				
			||||||
	db_subscr_nam(dbc, imsi_str, authorized, false);
 | 
						db_subscr_nam(dbc, imsi_str, authorized, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -437,17 +361,6 @@ int import_nitb_db(void)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(hlr_db_stmt_sql); i++) {
 | 
					 | 
				
			||||||
		sql = hlr_db_stmt_sql[i];
 | 
					 | 
				
			||||||
		rc = sqlite3_prepare_v2(g_hlr_db_tool_ctx->dbc->db, hlr_db_stmt_sql[i], -1,
 | 
					 | 
				
			||||||
					&hlr_db_stmt[i], NULL);
 | 
					 | 
				
			||||||
		if (rc != SQLITE_OK) {
 | 
					 | 
				
			||||||
			LOGP(DDB, LOGL_ERROR, "OsmoHLR DB: Unable to prepare SQL statement '%s'\n", sql);
 | 
					 | 
				
			||||||
			ret = -1;
 | 
					 | 
				
			||||||
			goto out_free;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stmt = nitb_stmt[NITB_SELECT_SUBSCR];
 | 
						stmt = nitb_stmt[NITB_SELECT_SUBSCR];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
 | 
						while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
 | 
				
			||||||
@@ -474,7 +387,6 @@ int main(int argc, char **argv)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	int (*main_action)(void);
 | 
						int (*main_action)(void);
 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	main_action = NULL;
 | 
						main_action = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
 | 
						g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
 | 
				
			||||||
@@ -518,11 +430,6 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	if (main_action)
 | 
						if (main_action)
 | 
				
			||||||
		rc = (*main_action)();
 | 
							rc = (*main_action)();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* db_close will only finalize statments in g_hlr_db_tool_ctx->dbc->stmt
 | 
					 | 
				
			||||||
	 * it is ok to call finalize on NULL */
 | 
					 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(hlr_db_stmt); i++) {
 | 
					 | 
				
			||||||
		sqlite3_finalize(hlr_db_stmt[i]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	db_close(g_hlr_db_tool_ctx->dbc);
 | 
						db_close(g_hlr_db_tool_ctx->dbc);
 | 
				
			||||||
	log_fini();
 | 
						log_fini();
 | 
				
			||||||
	exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
 | 
						exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										264
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							
							
						
						
									
										264
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							@@ -170,14 +170,12 @@ struct ss_session {
 | 
				
			|||||||
	/* subscriber's vlr_number
 | 
						/* subscriber's vlr_number
 | 
				
			||||||
	 * MO USSD: originating MSC's vlr_number
 | 
						 * MO USSD: originating MSC's vlr_number
 | 
				
			||||||
	 * MT USSD: looked up once per session and cached here */
 | 
						 * MT USSD: looked up once per session and cached here */
 | 
				
			||||||
	struct osmo_ipa_name vlr_name;
 | 
						uint8_t *vlr_number;
 | 
				
			||||||
 | 
						size_t vlr_number_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
 | 
						/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
 | 
				
			||||||
	 * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
 | 
						 * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
 | 
				
			||||||
	 * every time we receive an USSD component from the EUSE */
 | 
						 * every time we receive an USSD component from the EUSE */
 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *initial_req_from_ms;
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *initial_req_from_euse;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)
 | 
					struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)
 | 
				
			||||||
@@ -193,10 +191,6 @@ struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t s
 | 
				
			|||||||
void ss_session_free(struct ss_session *ss)
 | 
					void ss_session_free(struct ss_session *ss)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	osmo_timer_del(&ss->timeout);
 | 
						osmo_timer_del(&ss->timeout);
 | 
				
			||||||
	if (ss->initial_req_from_ms)
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(ss->initial_req_from_ms);
 | 
					 | 
				
			||||||
	if (ss->initial_req_from_euse)
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(ss->initial_req_from_euse);
 | 
					 | 
				
			||||||
	llist_del(&ss->list);
 | 
						llist_del(&ss->list);
 | 
				
			||||||
	talloc_free(ss);
 | 
						talloc_free(ss);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -236,72 +230,59 @@ struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t
 | 
				
			|||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Resolve the target MSC by ss->imsi and send GSUP message. */
 | 
					/* Resolve the target MSC by ss->imsi and send GSUP message. */
 | 
				
			||||||
static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs, struct osmo_gsup_message *gsup)
 | 
					static int ss_gsup_send(struct ss_session *ss, struct osmo_gsup_server *gs, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber subscr = {};
 | 
						struct hlr_subscriber subscr = {};
 | 
				
			||||||
	struct msgb *msg;
 | 
					 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ss->initial_req_from_ms) {
 | 
					 | 
				
			||||||
		/* Use non-final osmo_gsup_req_respond() to not deallocate the ss->initial_req_from_ms */
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond(ss->initial_req_from_ms, gsup, false, false);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg = osmo_gsup_msgb_alloc("GSUP USSD FW");
 | 
					 | 
				
			||||||
	rc = osmo_gsup_encode(msg, gsup);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");
 | 
					 | 
				
			||||||
		msgb_free(msg);
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Use vlr_number as looked up by the caller, or look up now. */
 | 
						/* Use vlr_number as looked up by the caller, or look up now. */
 | 
				
			||||||
	if (!ss->vlr_name.len) {
 | 
						if (!ss->vlr_number) {
 | 
				
			||||||
		rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
							rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
		if (rc < 0) {
 | 
							if (rc < 0) {
 | 
				
			||||||
			LOGPSS(ss, LOGL_ERROR, "Cannot find subscriber, cannot route GSUP message\n");
 | 
								LOGPSS(ss, LOGL_ERROR, "Cannot find subscriber, cannot route GSUP message\n");
 | 
				
			||||||
			msgb_free(msg);
 | 
								msgb_free(msg);
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		osmo_ipa_name_set_str(&ss->vlr_name, subscr.vlr_number);
 | 
							ss->vlr_number = (uint8_t *)talloc_strdup(ss, subscr.vlr_number);
 | 
				
			||||||
 | 
							ss->vlr_number_len = strlen(subscr.vlr_number) + 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */
 | 
						/* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */
 | 
				
			||||||
	if (ss->vlr_name.len <= 1) {
 | 
						if (ss->vlr_number_len == 1) {
 | 
				
			||||||
		LOGPSS(ss, LOGL_ERROR, "Cannot send GSUP message, no VLR number stored for subscriber\n");
 | 
							LOGPSS(ss, LOGL_ERROR, "Cannot send GSUP message, no VLR number stored for subscriber\n");
 | 
				
			||||||
		msgb_free(msg);
 | 
							msgb_free(msg);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_ipa_name_to_str(&ss->vlr_name));
 | 
						LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_quote_str((char *)ss->vlr_number, ss->vlr_number_len));
 | 
				
			||||||
	return osmo_gsup_send_to_ipa_name(gs, &ss->vlr_name, msg);
 | 
						return osmo_gsup_addr_send(gs, ss->vlr_number, ss->vlr_number_len, msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
 | 
					static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
 | 
				
			||||||
		       struct msgb *ss_msg)
 | 
								bool final, struct msgb *ss_msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_message resp;
 | 
						struct osmo_gsup_message resp = {0};
 | 
				
			||||||
	int rc;
 | 
						struct msgb *resp_msg;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	resp = (struct osmo_gsup_message) {
 | 
					 | 
				
			||||||
		.message_type = gsup_msg_type,
 | 
					 | 
				
			||||||
		.session_id = ss->session_id,
 | 
					 | 
				
			||||||
		.session_state = ss->state,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp.message_type = gsup_msg_type;
 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
 | 
						OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
 | 
				
			||||||
 | 
						if (final)
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_END;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
 | 
				
			||||||
 | 
						resp.session_id = ss->session_id;
 | 
				
			||||||
	if (ss_msg) {
 | 
						if (ss_msg) {
 | 
				
			||||||
		resp.ss_info = msgb_data(ss_msg);
 | 
							resp.ss_info = msgb_data(ss_msg);
 | 
				
			||||||
		resp.ss_info_len = msgb_length(ss_msg);
 | 
							resp.ss_info_len = msgb_length(ss_msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = ss_gsup_send_to_ms(ss, g_hlr->gs, &resp);
 | 
						resp_msg = msgb_alloc_headroom(4000, 64, __func__);
 | 
				
			||||||
 | 
						OSMO_ASSERT(resp_msg);
 | 
				
			||||||
 | 
						osmo_gsup_encode(resp_msg, &resp);
 | 
				
			||||||
	msgb_free(ss_msg);
 | 
						msgb_free(ss_msg);
 | 
				
			||||||
	return rc;
 | 
					
 | 
				
			||||||
 | 
						return ss_gsup_send(ss, g_hlr->gs, resp_msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
@@ -312,26 +293,24 @@ static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_ta
 | 
				
			|||||||
	LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
 | 
						LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
 | 
				
			||||||
		problem_tag, problem_code);
 | 
							problem_tag, problem_code);
 | 
				
			||||||
	OSMO_ASSERT(msg);
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
 | 
				
			||||||
	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
 | 
					static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
 | 
						struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
 | 
				
			||||||
	LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
 | 
						LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
 | 
				
			||||||
	OSMO_ASSERT(msg);
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
 | 
				
			||||||
	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const char *text)
 | 
					static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
 | 
						struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
 | 
				
			||||||
	LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
 | 
						LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
 | 
				
			||||||
	OSMO_ASSERT(msg);
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
@@ -340,15 +319,13 @@ static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handle_ussd_own_msisdn(struct ss_session *ss,
 | 
					static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
				  const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
									  const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
	char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
						char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
	switch (rc) {
 | 
						switch (rc) {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
@@ -356,39 +333,25 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
 | 
				
			|||||||
			snprintf(buf, sizeof(buf), "You have no MSISDN!");
 | 
								snprintf(buf, sizeof(buf), "You have no MSISDN!");
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
 | 
								snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
 | 
				
			||||||
		ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
 | 
							ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case -ENOENT:
 | 
						case -ENOENT:
 | 
				
			||||||
		ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case -EIO:
 | 
						case -EIO:
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handle_ussd_own_imsi(struct ss_session *ss,
 | 
					static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
				const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
									const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
						char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
				
			||||||
	snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
 | 
						snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
 | 
				
			||||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
						ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
				
			||||||
	ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* This handler just keeps the session idle unless the guard timer expires. */
 | 
					 | 
				
			||||||
static int handle_ussd_test_idle(struct ss_session *ss,
 | 
					 | 
				
			||||||
				 const struct osmo_gsup_message *gsup,
 | 
					 | 
				
			||||||
				 const struct ss_request *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char buf[GSM0480_USSD_7BIT_STRING_LEN + 1];
 | 
					 | 
				
			||||||
	snprintf(buf, sizeof(buf), "Keeping your session idle, it will expire "
 | 
					 | 
				
			||||||
		 "at most in %u seconds.", g_hlr->ncss_guard_timeout);
 | 
					 | 
				
			||||||
	ss->state = OSMO_GSUP_SESSION_STATE_CONTINUE;
 | 
					 | 
				
			||||||
	ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -402,10 +365,6 @@ static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			|||||||
		.name = "own-imsi",
 | 
							.name = "own-imsi",
 | 
				
			||||||
		.handle_ussd = handle_ussd_own_imsi,
 | 
							.handle_ussd = handle_ussd_own_imsi,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.name = "test-idle",
 | 
					 | 
				
			||||||
		.handle_ussd = handle_ussd_test_idle,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct hlr_iuse *iuse_find(const char *name)
 | 
					const struct hlr_iuse *iuse_find(const char *name)
 | 
				
			||||||
@@ -439,28 +398,37 @@ static bool ss_op_is_ussd(uint8_t opcode)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* is this GSUP connection an EUSE (true) or not (false)? */
 | 
					/* is this GSUP connection an EUSE (true) or not (false)? */
 | 
				
			||||||
static bool peer_name_is_euse(const struct osmo_cni_peer_id *peer_name)
 | 
					static bool conn_is_euse(struct osmo_gsup_conn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (peer_name->type != OSMO_CNI_PEER_ID_IPA_NAME)
 | 
						int rc;
 | 
				
			||||||
 | 
						uint8_t *addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
						if (rc <= 5)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	if (peer_name->ipa_name.len <= 5)
 | 
						if (!strncmp((char *)addr, "EUSE-", 5))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	return strncmp((char *)(peer_name->ipa_name.val), "EUSE-", 5) == 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct hlr_euse *euse_by_name(const struct osmo_cni_peer_id *peer_name)
 | 
					static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!peer_name_is_euse(peer_name))
 | 
						int rc;
 | 
				
			||||||
 | 
						char *addr;
 | 
				
			||||||
 | 
						struct hlr *hlr = conn->server->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
						if (rc <= 5)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						if (strncmp(addr, "EUSE-", 5))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* above peer_name_is_euse() ensures this: */
 | 
						return euse_find(hlr, addr+5);
 | 
				
			||||||
	OSMO_ASSERT(peer_name->type == OSMO_CNI_PEER_ID_IPA_NAME);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handle_ss(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,
 | 
					static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup,
 | 
				
			||||||
		     const struct ss_request *req)
 | 
								const struct ss_request *req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint8_t comp_type = gsup->ss_info[0];
 | 
						uint8_t comp_type = gsup->ss_info[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -473,16 +441,17 @@ static int handle_ss(struct ss_session *ss, bool is_euse_originated, const struc
 | 
				
			|||||||
	 * we don't handle "structured" SS requests at all.
 | 
						 * we don't handle "structured" SS requests at all.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");
 | 
						LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");
 | 
				
			||||||
	ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
 | 
						ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
 | 
				
			||||||
	return -ENOTSUP;
 | 
						return -ENOTSUP;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
 | 
					/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
 | 
				
			||||||
static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,
 | 
					static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
		       const struct ss_request *req)
 | 
								const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint8_t comp_type = gsup->ss_info[0];
 | 
						uint8_t comp_type = gsup->ss_info[0];
 | 
				
			||||||
	struct msgb *msg_out;
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
						bool is_euse_originated = conn_is_euse(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",
 | 
						LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",
 | 
				
			||||||
		gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),
 | 
							gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),
 | 
				
			||||||
@@ -490,27 +459,26 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {
 | 
						if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {
 | 
				
			||||||
		LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);
 | 
							LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);
 | 
				
			||||||
		ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (is_euse_originated) {
 | 
						if (is_euse_originated) {
 | 
				
			||||||
 | 
							msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
 | 
				
			||||||
		/* Received from EUSE, Forward to VLR */
 | 
							/* Received from EUSE, Forward to VLR */
 | 
				
			||||||
		/* Need a non-const osmo_gsup_message, because sending might modify some (routing related?) parts. */
 | 
							osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
		struct osmo_gsup_message forward = *gsup;
 | 
							ss_gsup_send(ss, conn->server, msg_out);
 | 
				
			||||||
		ss_gsup_send_to_ms(ss, g_hlr->gs, &forward);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* Received from VLR (MS) */
 | 
							/* Received from VLR (MS) */
 | 
				
			||||||
		if (ss->is_external) {
 | 
							if (ss->is_external) {
 | 
				
			||||||
			/* Forward to EUSE */
 | 
								/* Forward to EUSE */
 | 
				
			||||||
			struct osmo_ipa_name euse_name;
 | 
								char addr[128];
 | 
				
			||||||
			struct osmo_gsup_conn *conn;
 | 
								strcpy(addr, "EUSE-");
 | 
				
			||||||
			osmo_ipa_name_set_str(&euse_name, "EUSE-%s", ss->u.euse->name);
 | 
								osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5);
 | 
				
			||||||
			conn = gsup_route_find_by_ipa_name(g_hlr->gs, &euse_name);
 | 
								conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1);
 | 
				
			||||||
			if (!conn) {
 | 
								if (!conn) {
 | 
				
			||||||
				LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n",
 | 
									LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr);
 | 
				
			||||||
				       osmo_ipa_name_to_str(&euse_name));
 | 
									ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
				ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
 | 
									msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
 | 
				
			||||||
				osmo_gsup_encode(msg_out, gsup);
 | 
									osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
@@ -518,10 +486,9 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			/* Handle internally */
 | 
								/* Handle internally */
 | 
				
			||||||
			ss->u.iuse->handle_ussd(ss, gsup, req);
 | 
								ss->u.iuse->handle_ussd(conn, ss, gsup, req);
 | 
				
			||||||
			/* Release session if the handler has changed its state to END */
 | 
								/* Release session immediately */
 | 
				
			||||||
			if (ss->state == OSMO_GSUP_SESSION_STATE_END)
 | 
								ss_session_free(ss);
 | 
				
			||||||
				ss_session_free(ss);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -531,43 +498,30 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well
 | 
					/* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well
 | 
				
			||||||
 * as from the EUSE side */
 | 
					 * as from the EUSE side */
 | 
				
			||||||
void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
					int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr *hlr = g_hlr;
 | 
						struct hlr *hlr = conn->server->priv;
 | 
				
			||||||
	struct ss_session *ss;
 | 
						struct ss_session *ss;
 | 
				
			||||||
	struct ss_request req = {0};
 | 
						struct ss_request req = {0};
 | 
				
			||||||
	const struct osmo_gsup_message *gsup = &gsup_req->gsup;
 | 
						struct gsup_route *gsup_rt;
 | 
				
			||||||
	/* Remember whether this function should free the incoming gsup_req: if it is placed as ss->initial_req_from_*,
 | 
					 | 
				
			||||||
	 * do not free it here. If not, free it here. */
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *free_gsup_req = gsup_req;
 | 
					 | 
				
			||||||
	bool is_euse_originated = peer_name_is_euse(&gsup_req->source_name);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
						LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
				
			||||||
		osmo_gsup_session_state_name(gsup->session_state));
 | 
							osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gsup_req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
					 | 
				
			||||||
		LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to process SS request: Unsupported GSUP peer id type%s\n",
 | 
					 | 
				
			||||||
		     gsup->imsi, gsup->session_id,
 | 
					 | 
				
			||||||
		     osmo_cni_peer_id_type_name(gsup_req->source_name.type));
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_PROTO_ERR_UNSPEC, "error processing SS request");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* decode and find out what kind of SS message it is */
 | 
						/* decode and find out what kind of SS message it is */
 | 
				
			||||||
	if (gsup->ss_info && gsup->ss_info_len) {
 | 
						if (gsup->ss_info && gsup->ss_info_len) {
 | 
				
			||||||
		if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
 | 
							if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
 | 
				
			||||||
			LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",
 | 
				
			||||||
				gsup->imsi, gsup->session_id,
 | 
									gsup->imsi, gsup->session_id,
 | 
				
			||||||
				osmo_hexdump(gsup->ss_info, gsup->ss_info_len));
 | 
									osmo_hexdump(gsup->ss_info, gsup->ss_info_len));
 | 
				
			||||||
			osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "error parsing SS request");
 | 
								/* FIXME: Send a Reject component? */
 | 
				
			||||||
			return;
 | 
								goto out_err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if (gsup->session_state != OSMO_GSUP_SESSION_STATE_END) {
 | 
						} else if (gsup->session_state != OSMO_GSUP_SESSION_STATE_END) {
 | 
				
			||||||
		LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Missing SS payload for '%s'\n",
 | 
							LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Missing SS payload for '%s'\n",
 | 
				
			||||||
		     gsup->imsi, gsup->session_id,
 | 
							     gsup->imsi, gsup->session_id,
 | 
				
			||||||
		     osmo_gsup_session_state_name(gsup->session_state));
 | 
							     osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
		osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "missing SS payload");
 | 
							goto out_err;
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (gsup->session_state) {
 | 
						switch (gsup->session_state) {
 | 
				
			||||||
@@ -576,30 +530,32 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
		if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {
 | 
							if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {
 | 
				
			||||||
			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",
 | 
				
			||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "BEGIN with non-unique session ID");
 | 
								goto out_err;
 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);
 | 
							ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);
 | 
				
			||||||
		if (!ss) {
 | 
							if (!ss) {
 | 
				
			||||||
			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",
 | 
				
			||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_NET_FAIL, "Unable to allocate SS session");
 | 
								goto out_err;
 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* Get IPA name from VLR conn and save as ss->vlr_number */
 | 
							/* Get IPA name from VLR conn and save as ss->vlr_number */
 | 
				
			||||||
		if (!is_euse_originated) {
 | 
							if (!conn_is_euse(conn)) {
 | 
				
			||||||
			ss->initial_req_from_ms = gsup_req;
 | 
								gsup_rt = gsup_route_find_by_conn(conn);
 | 
				
			||||||
			free_gsup_req = NULL;
 | 
								if (gsup_rt) {
 | 
				
			||||||
			OSMO_ASSERT(gsup_req->source_name.type == OSMO_CNI_PEER_ID_IPA_NAME); /* checked above */
 | 
									ss->vlr_number = (uint8_t *)talloc_strdup(ss, (const char *)gsup_rt->addr);
 | 
				
			||||||
			ss->vlr_name = gsup_req->source_name.ipa_name;
 | 
									ss->vlr_number_len = strlen((const char *)gsup_rt->addr) + 1;
 | 
				
			||||||
		} else {
 | 
									LOGPSS(ss, LOGL_DEBUG, "Destination IPA name retrieved from GSUP route: %s\n",
 | 
				
			||||||
			ss->initial_req_from_euse = gsup_req;
 | 
									       osmo_quote_str((const char *)ss->vlr_number, ss->vlr_number_len));
 | 
				
			||||||
			free_gsup_req = NULL;
 | 
								} else {
 | 
				
			||||||
 | 
									LOGPSS(ss, LOGL_NOTICE, "Could not find GSUP route, therefore can't set the destination"
 | 
				
			||||||
 | 
												" IPA name. We'll try to look it up later, but this should not"
 | 
				
			||||||
 | 
												" have happened.\n");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (ss_op_is_ussd(req.opcode)) {
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
			if (is_euse_originated) {
 | 
								if (conn_is_euse(conn)) {
 | 
				
			||||||
				/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
 | 
									/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
 | 
				
			||||||
				ss->u.euse = euse_by_name(&gsup_req->source_name);
 | 
									ss->u.euse = euse_by_conn(conn);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
 | 
									/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
 | 
				
			||||||
				struct hlr_ussd_route *rt;
 | 
									struct hlr_ussd_route *rt;
 | 
				
			||||||
@@ -620,10 +576,10 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			/* dispatch unstructured SS to routing */
 | 
								/* dispatch unstructured SS to routing */
 | 
				
			||||||
			handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
								handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			/* dispatch non-call SS to internal code */
 | 
								/* dispatch non-call SS to internal code */
 | 
				
			||||||
			handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
								handle_ss(ss, gsup, &req);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_SESSION_STATE_CONTINUE:
 | 
						case OSMO_GSUP_SESSION_STATE_CONTINUE:
 | 
				
			||||||
@@ -631,8 +587,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
		if (!ss) {
 | 
							if (!ss) {
 | 
				
			||||||
			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
 | 
				
			||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "CONTINUE for unknown SS session");
 | 
								goto out_err;
 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Reschedule self-destruction timer */
 | 
							/* Reschedule self-destruction timer */
 | 
				
			||||||
@@ -641,10 +596,10 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (ss_op_is_ussd(req.opcode)) {
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
			/* dispatch unstructured SS to routing */
 | 
								/* dispatch unstructured SS to routing */
 | 
				
			||||||
			handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
								handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			/* dispatch non-call SS to internal code */
 | 
								/* dispatch non-call SS to internal code */
 | 
				
			||||||
			handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
								handle_ss(ss, gsup, &req);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_SESSION_STATE_END:
 | 
						case OSMO_GSUP_SESSION_STATE_END:
 | 
				
			||||||
@@ -652,17 +607,17 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
		if (!ss) {
 | 
							if (!ss) {
 | 
				
			||||||
			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
 | 
				
			||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			return;
 | 
								goto out_err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* SS payload is optional for END */
 | 
							/* SS payload is optional for END */
 | 
				
			||||||
		if (gsup->ss_info && gsup->ss_info_len) {
 | 
							if (gsup->ss_info && gsup->ss_info_len) {
 | 
				
			||||||
			if (ss_op_is_ussd(req.opcode)) {
 | 
								if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
				/* dispatch unstructured SS to routing */
 | 
									/* dispatch unstructured SS to routing */
 | 
				
			||||||
				handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
									handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				/* dispatch non-call SS to internal code */
 | 
									/* dispatch non-call SS to internal code */
 | 
				
			||||||
				handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);
 | 
									handle_ss(ss, gsup, &req);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -671,15 +626,18 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
				
			|||||||
	default:
 | 
						default:
 | 
				
			||||||
		LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,
 | 
							LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,
 | 
				
			||||||
			gsup->session_id, gsup->session_state);
 | 
								gsup->session_id, gsup->session_state);
 | 
				
			||||||
		break;
 | 
							goto out_err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (free_gsup_req)
 | 
						return 0;
 | 
				
			||||||
		osmo_gsup_req_free(free_gsup_req);
 | 
					
 | 
				
			||||||
 | 
					out_err:
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void rx_proc_ss_error(struct osmo_gsup_req *req)
 | 
					int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", req->gsup.imsi, req->gsup.session_id,
 | 
						LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id,
 | 
				
			||||||
		osmo_gsup_session_state_name(req->gsup.session_state));
 | 
							osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										106
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							@@ -25,10 +25,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/core/talloc.h>
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 | 
					 | 
				
			||||||
#include <osmocom/vty/vty.h>
 | 
					#include <osmocom/vty/vty.h>
 | 
				
			||||||
#include <osmocom/vty/stats.h>
 | 
					#include <osmocom/vty/stats.h>
 | 
				
			||||||
#include <osmocom/vty/command.h>
 | 
					#include <osmocom/vty/command.h>
 | 
				
			||||||
@@ -43,36 +40,6 @@
 | 
				
			|||||||
#include <osmocom/hlr/hlr_ussd.h>
 | 
					#include <osmocom/hlr/hlr_ussd.h>
 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct value_string gsm48_gmm_cause_vty_names[] = {
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_IMSI_UNKNOWN,	"imsi-unknown" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_ILLEGAL_MS,		"illegal-ms" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_PLMN_NOTALLOWED,	"plmn-not-allowed" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_LA_NOTALLOWED,	"la-not-allowed" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_ROAMING_NOTALLOWED,	"roaming-not-allowed" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_NO_SUIT_CELL_IN_LA,	"no-suitable-cell-in-la" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_NET_FAIL,		"net-fail" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_CONGESTION,		"congestion" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_GSM_AUTH_UNACCEPT,	"auth-unacceptable" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_PROTO_ERR_UNSPEC,	"proto-error-unspec" },
 | 
					 | 
				
			||||||
	{ 0, NULL },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* TS 24.008 4.4.4.7 */
 | 
					 | 
				
			||||||
static const struct value_string gsm48_gmm_cause_vty_descs[] = {
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_IMSI_UNKNOWN,	" #02: (IMSI unknown in HLR)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_ILLEGAL_MS,		" #03  (Illegal MS)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_PLMN_NOTALLOWED,	" #11: (PLMN not allowed)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_LA_NOTALLOWED,	" #12: (Location Area not allowed)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_ROAMING_NOTALLOWED,	" #13: (Roaming not allowed in this location area)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_NO_SUIT_CELL_IN_LA,	" #15: (No Suitable Cells In Location Area [continue search in PLMN])." },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_NET_FAIL,		" #17: (Network Failure)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_CONGESTION,		" #22: (Congestion)" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_GSM_AUTH_UNACCEPT,	" #23: (GSM authentication unacceptable [UMTS])" },
 | 
					 | 
				
			||||||
	{ GMM_CAUSE_PROTO_ERR_UNSPEC,	"#111: (Protocol error, unspecified)" },
 | 
					 | 
				
			||||||
	{ 0, NULL },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node hlr_node = {
 | 
					struct cmd_node hlr_node = {
 | 
				
			||||||
	HLR_NODE,
 | 
						HLR_NODE,
 | 
				
			||||||
	"%s(config-hlr)# ",
 | 
						"%s(config-hlr)# ",
 | 
				
			||||||
@@ -106,15 +73,6 @@ DEFUN(cfg_gsup,
 | 
				
			|||||||
static int config_write_hlr(struct vty *vty)
 | 
					static int config_write_hlr(struct vty *vty)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	vty_out(vty, "hlr%s", VTY_NEWLINE);
 | 
						vty_out(vty, "hlr%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (g_hlr->reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
 | 
					 | 
				
			||||||
		vty_out(vty, " reject-cause not-found %s%s",
 | 
					 | 
				
			||||||
			get_value_string_or_null(gsm48_gmm_cause_vty_names,
 | 
					 | 
				
			||||||
						 (uint32_t) g_hlr->reject_cause), VTY_NEWLINE);
 | 
					 | 
				
			||||||
	if (g_hlr->no_proxy_reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
 | 
					 | 
				
			||||||
		vty_out(vty, " reject-cause no-proxy %s%s",
 | 
					 | 
				
			||||||
			get_value_string_or_null(gsm48_gmm_cause_vty_names,
 | 
					 | 
				
			||||||
						 (uint32_t) g_hlr->no_proxy_reject_cause), VTY_NEWLINE);
 | 
					 | 
				
			||||||
	if (g_hlr->store_imei)
 | 
						if (g_hlr->store_imei)
 | 
				
			||||||
		vty_out(vty, " store-imei%s", VTY_NEWLINE);
 | 
							vty_out(vty, " store-imei%s", VTY_NEWLINE);
 | 
				
			||||||
	if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH))
 | 
						if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH))
 | 
				
			||||||
@@ -144,8 +102,6 @@ static int config_write_hlr_gsup(struct vty *vty)
 | 
				
			|||||||
	vty_out(vty, " gsup%s", VTY_NEWLINE);
 | 
						vty_out(vty, " gsup%s", VTY_NEWLINE);
 | 
				
			||||||
	if (g_hlr->gsup_bind_addr)
 | 
						if (g_hlr->gsup_bind_addr)
 | 
				
			||||||
		vty_out(vty, "  bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
 | 
							vty_out(vty, "  bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
 | 
				
			||||||
	if (g_hlr->gsup_unit_name.serno)
 | 
					 | 
				
			||||||
		vty_out(vty, "  ipa-name %s%s", g_hlr->gsup_unit_name.serno, VTY_NEWLINE);
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -158,8 +114,8 @@ static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
 | 
				
			|||||||
	rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
 | 
						rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
 | 
				
			||||||
	OSMO_ASSERT(rc);
 | 
						OSMO_ASSERT(rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s",
 | 
						vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u, 3G_IND=%u%s",
 | 
				
			||||||
		name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps,
 | 
							name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind,
 | 
				
			||||||
		VTY_NEWLINE);
 | 
							VTY_NEWLINE);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,24 +146,6 @@ DEFUN(cfg_hlr_gsup_bind_ip,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFUN(cfg_hlr_gsup_ipa_name,
 | 
					 | 
				
			||||||
      cfg_hlr_gsup_ipa_name_cmd,
 | 
					 | 
				
			||||||
      "ipa-name NAME",
 | 
					 | 
				
			||||||
      "Set the IPA name of this HLR, for proxying to remote HLRs\n"
 | 
					 | 
				
			||||||
      "A globally unique name for this HLR. For example: PLMN + redundancy server number: HLR-901-70-0. "
 | 
					 | 
				
			||||||
      "This name is used for GSUP routing and must be set if multiple HLRs interconnect (e.g. mslookup "
 | 
					 | 
				
			||||||
      "for Distributed GSM).\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (vty->type != VTY_FILE) {
 | 
					 | 
				
			||||||
		vty_out(vty, "gsup/ipa-name: The GSUP IPA name cannot be changed at run-time; "
 | 
					 | 
				
			||||||
			"It can only be set in the configuration file.%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	g_hlr->gsup_unit_name.serno = talloc_strdup(g_hlr, argv[0]);
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
 * USSD Entity
 | 
					 * USSD Entity
 | 
				
			||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
@@ -218,11 +156,10 @@ DEFUN(cfg_hlr_gsup_ipa_name,
 | 
				
			|||||||
#define UROUTE_STR "Routing Configuration\n"
 | 
					#define UROUTE_STR "Routing Configuration\n"
 | 
				
			||||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
					#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define INT_CHOICE "(own-msisdn|own-imsi|test-idle)"
 | 
					#define INT_CHOICE "(own-msisdn|own-imsi)"
 | 
				
			||||||
#define INT_STR "Internal USSD Handler\n" \
 | 
					#define INT_STR "Internal USSD Handler\n" \
 | 
				
			||||||
		"Respond with subscribers' own MSISDN\n" \
 | 
							"Respond with subscribers' own MSISDN\n" \
 | 
				
			||||||
		"Respond with subscribers' own IMSI\n" \
 | 
							"Respond with subscribers' own IMSI\n"
 | 
				
			||||||
		"Keep the session idle (useful for testing)\n"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define EXT_STR "External USSD Handler\n" \
 | 
					#define EXT_STR "External USSD Handler\n" \
 | 
				
			||||||
		"Name of External USSD Handler (IPA CCM ID)\n"
 | 
							"Name of External USSD Handler (IPA CCM ID)\n"
 | 
				
			||||||
@@ -350,7 +287,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
 | 
						struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
 | 
				
			||||||
	if (!euse) {
 | 
						if (!euse) {
 | 
				
			||||||
		vty_out(vty, "%% Cannot remove non-existent EUSE %s%s", argv[0], VTY_NEWLINE);
 | 
							vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
		return CMD_WARNING;
 | 
							return CMD_WARNING;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (g_hlr->euse_default == euse) {
 | 
						if (g_hlr->euse_default == euse) {
 | 
				
			||||||
@@ -400,21 +337,6 @@ DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_reject_cause, cfg_reject_cause_cmd,
 | 
					 | 
				
			||||||
      "reject-cause TYPE CAUSE", "") /* Dynamically Generated */
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int cause_code = get_string_value(gsm48_gmm_cause_vty_names, argv[1]);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(cause_code >= 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(argv[0], "not-found") == 0)
 | 
					 | 
				
			||||||
		g_hlr->reject_cause = (enum gsm48_gmm_cause) cause_code;
 | 
					 | 
				
			||||||
	if (strcmp(argv[0], "no-proxy") == 0)
 | 
					 | 
				
			||||||
		g_hlr->no_proxy_reject_cause = (enum gsm48_gmm_cause) cause_code;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(cfg_store_imei, cfg_store_imei_cmd,
 | 
					DEFUN(cfg_store_imei, cfg_store_imei_cmd,
 | 
				
			||||||
	"store-imei",
 | 
						"store-imei",
 | 
				
			||||||
	"Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
 | 
						"Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
 | 
				
			||||||
@@ -507,22 +429,8 @@ int hlr_vty_is_config_node(struct vty *vty, int node)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void hlr_vty_init(void *hlr_ctx)
 | 
					void hlr_vty_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	cfg_reject_cause_cmd.string =
 | 
					 | 
				
			||||||
		vty_cmd_string_from_valstr(hlr_ctx,
 | 
					 | 
				
			||||||
					   gsm48_gmm_cause_vty_names,
 | 
					 | 
				
			||||||
					   "reject-cause (not-found|no-proxy) (", "|", ")",
 | 
					 | 
				
			||||||
					   VTY_DO_LOWER);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cfg_reject_cause_cmd.doc =
 | 
					 | 
				
			||||||
		vty_cmd_string_from_valstr(hlr_ctx,
 | 
					 | 
				
			||||||
					   gsm48_gmm_cause_vty_descs,
 | 
					 | 
				
			||||||
					   "GSUP/GMM cause to be sent\n"
 | 
					 | 
				
			||||||
					   "in the case the IMSI could not be found in the database\n"
 | 
					 | 
				
			||||||
					   "in the case no remote HLR reponded to mslookup GSUP request\n",
 | 
					 | 
				
			||||||
					   "\n", "", 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logging_vty_add_cmds();
 | 
						logging_vty_add_cmds();
 | 
				
			||||||
	osmo_talloc_vty_add_cmds();
 | 
						osmo_talloc_vty_add_cmds();
 | 
				
			||||||
	osmo_stats_vty_add_cmds();
 | 
						osmo_stats_vty_add_cmds();
 | 
				
			||||||
@@ -536,7 +444,6 @@ void hlr_vty_init(void *hlr_ctx)
 | 
				
			|||||||
	install_node(&gsup_node, config_write_hlr_gsup);
 | 
						install_node(&gsup_node, config_write_hlr_gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
						install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
				
			||||||
	install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_database_cmd);
 | 
						install_element(HLR_NODE, &cfg_database_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -549,7 +456,6 @@ void hlr_vty_init(void *hlr_ctx)
 | 
				
			|||||||
	install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
 | 
						install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
 | 
						install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
 | 
						install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_reject_cause_cmd);
 | 
					 | 
				
			||||||
	install_element(HLR_NODE, &cfg_store_imei_cmd);
 | 
						install_element(HLR_NODE, &cfg_store_imei_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_no_store_imei_cmd);
 | 
						install_element(HLR_NODE, &cfg_no_store_imei_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd);
 | 
						install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,48 +30,30 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					#include <osmocom/hlr/hlr.h>
 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
#include <osmocom/hlr/timestamp.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr_vty.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct vty;
 | 
					struct vty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
					#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
 | 
					static char *
 | 
				
			||||||
 | 
					get_datestr(const time_t *t, char *datebuf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tm tm;
 | 
						char *p, *s = ctime_r(t, datebuf);
 | 
				
			||||||
	gmtime_r(t, &tm);
 | 
					
 | 
				
			||||||
	strftime(buf, bufsize, "%FT%T+00:00", &tm);
 | 
						/* Strip trailing newline. */
 | 
				
			||||||
	return buf;
 | 
						p = strchr(s, '\n');
 | 
				
			||||||
 | 
						if (p)
 | 
				
			||||||
 | 
							*p = '\0';
 | 
				
			||||||
 | 
						return s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)
 | 
					static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32_t age;
 | 
						char datebuf[26]; /* for ctime_r(3) */
 | 
				
			||||||
	char datebuf[32];
 | 
					 | 
				
			||||||
	if (!last_lu_seen)
 | 
						if (!last_lu_seen)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	if (!only_age)
 | 
						vty_out(vty, "    last LU seen on %s: %s UTC%s", domain_label, get_datestr(&last_lu_seen, datebuf),
 | 
				
			||||||
		vty_out(vty, "    last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
 | 
							VTY_NEWLINE);
 | 
				
			||||||
	if (!timestamp_age(&last_lu_seen, &age))
 | 
					 | 
				
			||||||
		vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
	else {
 | 
					 | 
				
			||||||
		vty_out(vty, " (");
 | 
					 | 
				
			||||||
#define UNIT_AGO(UNITNAME, UNITVAL) \
 | 
					 | 
				
			||||||
		if (age >= (UNITVAL)) { \
 | 
					 | 
				
			||||||
			vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
 | 
					 | 
				
			||||||
			age = age % (UNITVAL); \
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		UNIT_AGO("d", 60*60*24);
 | 
					 | 
				
			||||||
		UNIT_AGO("h", 60*60);
 | 
					 | 
				
			||||||
		UNIT_AGO("m", 60);
 | 
					 | 
				
			||||||
		UNIT_AGO("s", 1);
 | 
					 | 
				
			||||||
		if (!only_age)
 | 
					 | 
				
			||||||
			vty_out(vty, " ago)%s", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			vty_out(vty, " ago)");
 | 
					 | 
				
			||||||
#undef UNIT_AGO
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
					static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			||||||
@@ -113,8 +95,8 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			|||||||
		vty_out(vty, "    PS disabled%s", VTY_NEWLINE);
 | 
							vty_out(vty, "    PS disabled%s", VTY_NEWLINE);
 | 
				
			||||||
	if (subscr->ms_purged_ps)
 | 
						if (subscr->ms_purged_ps)
 | 
				
			||||||
		vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
							vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
				
			||||||
	dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
 | 
						dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
 | 
				
			||||||
	dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
 | 
						dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*subscr->imsi)
 | 
						if (!*subscr->imsi)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
@@ -164,28 +146,6 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct vty *vty = data;
 | 
					 | 
				
			||||||
	vty_out(vty, "%-5"PRIu64"  %-12s  %-16s", subscr->id,
 | 
					 | 
				
			||||||
		*subscr->msisdn ? subscr->msisdn : "none",
 | 
					 | 
				
			||||||
		*subscr->imsi ? subscr->imsi : "none");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (*subscr->imei) {
 | 
					 | 
				
			||||||
		char checksum = osmo_luhn(subscr->imei, 14);
 | 
					 | 
				
			||||||
		if (checksum == -EINVAL)
 | 
					 | 
				
			||||||
			vty_out(vty, "  %-14s (INVALID LENGTH!)", subscr->imei);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			vty_out(vty, "  %-14s%c", subscr->imei, checksum);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		vty_out(vty,"   ------------- ");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	vty_out(vty, "   %-2s%-2s  ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
 | 
					 | 
				
			||||||
	if (subscr->last_lu_seen)
 | 
					 | 
				
			||||||
		dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
 | 
					 | 
				
			||||||
	vty_out_newline(vty);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
 | 
					static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
 | 
						char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
 | 
				
			||||||
@@ -213,52 +173,10 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *texts = "ID     MSISDN        IMSI              IMEI              NAM";
 | 
					 | 
				
			||||||
	const char *lines = "-----  ------------  ----------------  ----------------  -----";
 | 
					 | 
				
			||||||
	const char *ls_text = "    LAST SEEN";
 | 
					 | 
				
			||||||
	const char *ls_line = "  ------------";
 | 
					 | 
				
			||||||
	if (header) {
 | 
					 | 
				
			||||||
		if (!show_ls)
 | 
					 | 
				
			||||||
			vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (!show_ls)
 | 
					 | 
				
			||||||
			vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc = -1;
 | 
					 | 
				
			||||||
	int count = 0;
 | 
					 | 
				
			||||||
	const char *err;
 | 
					 | 
				
			||||||
	bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
 | 
					 | 
				
			||||||
	dump_summary_table_vty(vty, true, show_ls);
 | 
					 | 
				
			||||||
	rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
 | 
					 | 
				
			||||||
	if (count > 40) {
 | 
					 | 
				
			||||||
		dump_summary_table_vty(vty, false, show_ls);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (count > 0)
 | 
					 | 
				
			||||||
		vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
 | 
					 | 
				
			||||||
	if (rc)
 | 
					 | 
				
			||||||
		vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define SUBSCR_CMD "subscriber "
 | 
					#define SUBSCR_CMD "subscriber "
 | 
				
			||||||
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
 | 
					#define SUBSCR_CMD_HELP "Subscriber management commands\n"
 | 
				
			||||||
#define SUBSCR_SHOW_HELP "Show subscriber information\n"
 | 
					 | 
				
			||||||
#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
 | 
					#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
 | 
				
			||||||
#define SUBSCR_FILTER "(imei|imsi|msisdn) FILTER"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define SUBSCR_ID_HELP \
 | 
					#define SUBSCR_ID_HELP \
 | 
				
			||||||
	"Identify subscriber by IMSI\n" \
 | 
						"Identify subscriber by IMSI\n" \
 | 
				
			||||||
	"Identify subscriber by MSISDN (phone number)\n" \
 | 
						"Identify subscriber by MSISDN (phone number)\n" \
 | 
				
			||||||
@@ -276,7 +194,7 @@ static int get_subscrs(struct vty *vty, const char *filter_type, const char *fil
 | 
				
			|||||||
DEFUN(subscriber_show,
 | 
					DEFUN(subscriber_show,
 | 
				
			||||||
      subscriber_show_cmd,
 | 
					      subscriber_show_cmd,
 | 
				
			||||||
      SUBSCR "show",
 | 
					      SUBSCR "show",
 | 
				
			||||||
      SUBSCR_HELP SUBSCR_SHOW_HELP)
 | 
					      SUBSCR_HELP "Show subscriber information\n")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
	const char *id_type = argv[0];
 | 
						const char *id_type = argv[0];
 | 
				
			||||||
@@ -291,50 +209,7 @@ DEFUN(subscriber_show,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
ALIAS(subscriber_show, show_subscriber_cmd,
 | 
					ALIAS(subscriber_show, show_subscriber_cmd,
 | 
				
			||||||
      "show " SUBSCR_CMD SUBSCR_ID,
 | 
					      "show " SUBSCR_CMD SUBSCR_ID,
 | 
				
			||||||
      SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);
 | 
					      SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(show_subscriber_all,
 | 
					 | 
				
			||||||
      show_subscriber_all_cmd,
 | 
					 | 
				
			||||||
      "show subscribers all",
 | 
					 | 
				
			||||||
      SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (get_subscrs(vty, NULL, NULL))
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(show_subscriber_filtered,
 | 
					 | 
				
			||||||
      show_subscriber_filtered_cmd,
 | 
					 | 
				
			||||||
      "show subscribers " SUBSCR_FILTER,
 | 
					 | 
				
			||||||
      SHOW_STR SUBSCRS_SHOW_HELP
 | 
					 | 
				
			||||||
      "Filter Subscribers by IMEI\n" "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n"
 | 
					 | 
				
			||||||
      "String to match in imei, imsi or msisdn\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *filter_type = argv[0];
 | 
					 | 
				
			||||||
	const char *filter = argv[1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (get_subscrs(vty, filter_type, filter))
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
 | 
					 | 
				
			||||||
      "show subscribers (cs|ps) (on|off)",
 | 
					 | 
				
			||||||
      SHOW_STR SUBSCR_SHOW_HELP
 | 
					 | 
				
			||||||
      "Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
 | 
					 | 
				
			||||||
      "Authorised\n" "Not Authorised\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
 | 
					 | 
				
			||||||
      "show subscribers last-seen",
 | 
					 | 
				
			||||||
      SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (get_subscrs(vty, "last_lu_seen", NULL))
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFUN(subscriber_create,
 | 
					DEFUN(subscriber_create,
 | 
				
			||||||
      subscriber_create_cmd,
 | 
					      subscriber_create_cmd,
 | 
				
			||||||
@@ -347,7 +222,7 @@ DEFUN(subscriber_create,
 | 
				
			|||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
	const char *imsi = argv[0];
 | 
						const char *imsi = argv[0];
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	if (!osmo_imsi_str_valid(imsi)) {
 | 
						if (!osmo_imsi_str_valid(imsi)) {
 | 
				
			||||||
		vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
 | 
							vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
 | 
				
			||||||
		return CMD_WARNING;
 | 
							return CMD_WARNING;
 | 
				
			||||||
@@ -471,8 +346,14 @@ static bool is_hexkey_valid(struct vty *vty, const char *label,
 | 
				
			|||||||
#define AUTH_ALG_TYPES_3G_HELP \
 | 
					#define AUTH_ALG_TYPES_3G_HELP \
 | 
				
			||||||
	"Use Milenage algorithm\n"
 | 
						"Use Milenage algorithm\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
 | 
					#define A38_XOR_MIN_KEY_LEN	12
 | 
				
			||||||
		     int *minlen, int *maxlen)
 | 
					#define A38_XOR_MAX_KEY_LEN	16
 | 
				
			||||||
 | 
					#define A38_COMP128_KEY_LEN	16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MILENAGE_KEY_LEN 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
 | 
				
			||||||
 | 
								    int *minlen, int *maxlen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!strcasecmp(alg_str, "none")) {
 | 
						if (!strcasecmp(alg_str, "none")) {
 | 
				
			||||||
		*algo = OSMO_AUTH_ALG_NONE;
 | 
							*algo = OSMO_AUTH_ALG_NONE;
 | 
				
			||||||
@@ -631,7 +512,7 @@ DEFUN(subscriber_aud3g,
 | 
				
			|||||||
			.ind_bitlen = ind_bitlen,
 | 
								.ind_bitlen = ind_bitlen,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
 | 
						if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
 | 
				
			||||||
		vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
 | 
							vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
 | 
				
			||||||
		return CMD_WARNING;
 | 
							return CMD_WARNING;
 | 
				
			||||||
@@ -657,55 +538,6 @@ DEFUN(subscriber_aud3g,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFUN(subscriber_aud3g_xor,
 | 
					 | 
				
			||||||
      subscriber_aud3g_xor_cmd,
 | 
					 | 
				
			||||||
      SUBSCR_UPDATE "aud3g xor k K"
 | 
					 | 
				
			||||||
      " [ind-bitlen] [<0-28>]",
 | 
					 | 
				
			||||||
      SUBSCR_UPDATE_HELP
 | 
					 | 
				
			||||||
      "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
 | 
					 | 
				
			||||||
      "Use XOR algorithm\n"
 | 
					 | 
				
			||||||
      "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
 | 
					 | 
				
			||||||
      "Set IND bit length\n" "IND bit length value (default: 5)\n")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	int minlen = 0;
 | 
					 | 
				
			||||||
	int maxlen = 0;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	const char *id_type = argv[0];
 | 
					 | 
				
			||||||
	const char *id = argv[1];
 | 
					 | 
				
			||||||
	const char *k = argv[2];
 | 
					 | 
				
			||||||
	int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
 | 
					 | 
				
			||||||
	struct sub_auth_data_str aud3g = {
 | 
					 | 
				
			||||||
		.type = OSMO_AUTH_TYPE_UMTS,
 | 
					 | 
				
			||||||
		.u.umts = {
 | 
					 | 
				
			||||||
			.k = k,
 | 
					 | 
				
			||||||
			.opc_is_op = 0,
 | 
					 | 
				
			||||||
			.opc = "00000000000000000000000000000000",
 | 
					 | 
				
			||||||
			.ind_bitlen = ind_bitlen,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!auth_algo_parse("xor", &aud3g.algo, &minlen, &maxlen)) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (get_subscr_by_argv(vty, id_type, id, &subscr))
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
 | 
					 | 
				
			||||||
			subscr.imsi, VTY_NEWLINE);
 | 
					 | 
				
			||||||
		return CMD_WARNING;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return CMD_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEFUN(subscriber_imei,
 | 
					DEFUN(subscriber_imei,
 | 
				
			||||||
      subscriber_imei_cmd,
 | 
					      subscriber_imei_cmd,
 | 
				
			||||||
      SUBSCR_UPDATE "imei (none|IMEI)",
 | 
					      SUBSCR_UPDATE "imei (none|IMEI)",
 | 
				
			||||||
@@ -783,10 +615,6 @@ DEFUN(subscriber_nam,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void hlr_vty_subscriber_init(void)
 | 
					void hlr_vty_subscriber_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	install_element_ve(&show_subscriber_all_cmd);
 | 
					 | 
				
			||||||
	install_element_ve(&show_subscriber_filtered_cmd);
 | 
					 | 
				
			||||||
	install_element_ve(&show_subscriber_filtered_cmd2);
 | 
					 | 
				
			||||||
	install_element_ve(&show_subscriber_order_last_seen_cmd);
 | 
					 | 
				
			||||||
	install_element_ve(&subscriber_show_cmd);
 | 
						install_element_ve(&subscriber_show_cmd);
 | 
				
			||||||
	install_element_ve(&show_subscriber_cmd);
 | 
						install_element_ve(&show_subscriber_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_create_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_create_cmd);
 | 
				
			||||||
@@ -796,7 +624,6 @@ void hlr_vty_subscriber_init(void)
 | 
				
			|||||||
	install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
 | 
					 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_imei_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_imei_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_nam_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_nam_cmd);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,24 +31,6 @@ const struct log_info_cat hlr_log_info_cat[] = {
 | 
				
			|||||||
		.color = "\033[1;35m",
 | 
							.color = "\033[1;35m",
 | 
				
			||||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	[DLU] = {
 | 
					 | 
				
			||||||
		.name = "DLU",
 | 
					 | 
				
			||||||
		.description = "Location Updating",
 | 
					 | 
				
			||||||
		.color = "\033[1;33m",
 | 
					 | 
				
			||||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	[DDGSM] = {
 | 
					 | 
				
			||||||
		.name = "DDGSM",
 | 
					 | 
				
			||||||
		.description = "Distributed GSM: MS lookup and proxy",
 | 
					 | 
				
			||||||
		.color = "\033[1;35m",
 | 
					 | 
				
			||||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	[DCTRL] = {
 | 
					 | 
				
			||||||
		.name = "DCTRL",
 | 
					 | 
				
			||||||
		.description = "Osmocom CTRL interface",
 | 
					 | 
				
			||||||
		.color = "\033[1;30m",
 | 
					 | 
				
			||||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct log_info hlr_log_info = {
 | 
					const struct log_info hlr_log_info = {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										320
									
								
								src/lu_fsm.c
									
									
									
									
									
								
							
							
						
						
									
										320
									
								
								src/lu_fsm.c
									
									
									
									
									
								
							@@ -1,320 +0,0 @@
 | 
				
			|||||||
/* Roughly following "Process Update_Location_HLR" of TS 09.02 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/core/utils.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/tdef.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/fsm.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/apn.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_LU(lu, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGPFSML((lu)? (lu)->fi : NULL, level, fmt, ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_LU_REQ(lu, req, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGPFSML((lu)? (lu)->fi : NULL, level, "%s:" fmt, \
 | 
					 | 
				
			||||||
		 osmo_gsup_message_type_name((req)->gsup.message_type), ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct lu {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	struct osmo_fsm_inst *fi;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *update_location_req;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Subscriber state at time of initial Update Location Request */
 | 
					 | 
				
			||||||
	struct hlr_subscriber subscr;
 | 
					 | 
				
			||||||
	bool is_ps;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* VLR requesting the LU. */
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id vlr_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest
 | 
					 | 
				
			||||||
	 * peer that forwarded the GSUP message. */
 | 
					 | 
				
			||||||
	struct osmo_cni_peer_id via_proxy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
LLIST_HEAD(g_all_lu);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum lu_fsm_event {
 | 
					 | 
				
			||||||
	LU_EV_RX_GSUP,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum lu_fsm_state {
 | 
					 | 
				
			||||||
	LU_ST_UNVALIDATED,
 | 
					 | 
				
			||||||
	LU_ST_WAIT_INSERT_DATA_RESULT,
 | 
					 | 
				
			||||||
	LU_ST_WAIT_LOCATION_CANCEL_RESULT,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct value_string lu_fsm_event_names[] = {
 | 
					 | 
				
			||||||
	OSMO_VALUE_STRING(LU_EV_RX_GSUP),
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct osmo_tdef_state_timeout lu_fsm_timeouts[32] = {
 | 
					 | 
				
			||||||
	[LU_ST_WAIT_INSERT_DATA_RESULT] = { .T = -4222 },
 | 
					 | 
				
			||||||
	[LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { .T = -4222 },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define lu_state_chg(lu, state) \
 | 
					 | 
				
			||||||
	osmo_tdef_fsm_inst_state_chg((lu)->fi, state, lu_fsm_timeouts, g_hlr_tdefs, 5)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void lu_success(struct lu *lu)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!lu->update_location_req)
 | 
					 | 
				
			||||||
		LOG_LU(lu, LOGL_ERROR, "No request for this LU\n");
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT, true);
 | 
					 | 
				
			||||||
	lu->update_location_req = NULL;
 | 
					 | 
				
			||||||
	osmo_fsm_inst_term(lu->fi, OSMO_FSM_TERM_REGULAR, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define lu_failure(LU, CAUSE, log_msg, args...) do { \
 | 
					 | 
				
			||||||
		if (!(LU)->update_location_req) \
 | 
					 | 
				
			||||||
			LOG_LU(LU, LOGL_ERROR, "No request for this LU\n"); \
 | 
					 | 
				
			||||||
		else \
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_err((LU)->update_location_req, CAUSE, log_msg, ##args); \
 | 
					 | 
				
			||||||
		(LU)->update_location_req = NULL; \
 | 
					 | 
				
			||||||
		osmo_fsm_inst_term((LU)->fi, OSMO_FSM_TERM_REGULAR, NULL); \
 | 
					 | 
				
			||||||
	} while(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct osmo_fsm lu_fsm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void lu_start(struct osmo_gsup_req *update_location_req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_fsm_inst *fi;
 | 
					 | 
				
			||||||
	struct lu *lu;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OSMO_ASSERT(update_location_req);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fi = osmo_fsm_inst_alloc(&lu_fsm, g_hlr, NULL, LOGL_DEBUG, update_location_req->gsup.imsi);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(fi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lu = talloc(fi, struct lu);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(lu);
 | 
					 | 
				
			||||||
	fi->priv = lu;
 | 
					 | 
				
			||||||
	*lu = (struct lu){
 | 
					 | 
				
			||||||
		.fi = fi,
 | 
					 | 
				
			||||||
		.update_location_req = update_location_req,
 | 
					 | 
				
			||||||
		.vlr_name = update_location_req->source_name,
 | 
					 | 
				
			||||||
		.via_proxy = update_location_req->via_proxy,
 | 
					 | 
				
			||||||
		/* According to GSUP specs, OSMO_GSUP_CN_DOMAIN_PS is the default. */
 | 
					 | 
				
			||||||
		.is_ps = (update_location_req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	llist_add(&lu->entry, &g_all_lu);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_cni_peer_id_is_empty(&lu->vlr_name)) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {
 | 
					 | 
				
			||||||
		lu_failure(lu, g_hlr->reject_cause, "Subscriber does not exist");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Check if subscriber is generally permitted on CS or PS
 | 
					 | 
				
			||||||
	 * service (as requested) */
 | 
					 | 
				
			||||||
	if (!lu->is_ps && !lu->subscr.nam_cs) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_PLMN_NOTALLOWED, "nam_cs == false");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (lu->is_ps && !lu->subscr.nam_ps) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_GPRS_NOTALLOWED, "nam_ps == false");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
	/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old (FIXME: OS#4491) */
 | 
					 | 
				
			||||||
	if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {
 | 
					 | 
				
			||||||
		lu_op_tx_cancel_old(lu);
 | 
					 | 
				
			||||||
	} else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {
 | 
					 | 
				
			||||||
		lu_op_tx_cancel_old(lu);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
 | 
					 | 
				
			||||||
	if (!osmo_cni_peer_id_is_empty(&lu->via_proxy)) {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",
 | 
					 | 
				
			||||||
			     lu->is_ps ? "SGSN number" : "VLR number",
 | 
					 | 
				
			||||||
			     osmo_cni_peer_id_to_str(&lu->vlr_name),
 | 
					 | 
				
			||||||
			     osmo_cni_peer_id_to_str(&lu->via_proxy));
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",
 | 
					 | 
				
			||||||
		     lu->is_ps ? "SGSN number" : "VLR number",
 | 
					 | 
				
			||||||
		     osmo_cni_peer_id_to_str(&lu->vlr_name));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_cni_peer_id_is_empty(&lu->vlr_name)
 | 
					 | 
				
			||||||
	    || (lu->vlr_name.type != OSMO_CNI_PEER_ID_IPA_NAME)) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for vlr_name: %s",
 | 
					 | 
				
			||||||
			   osmo_cni_peer_id_type_name(lu->vlr_name.type));
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (!osmo_cni_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_CNI_PEER_ID_IPA_NAME)) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for via_proxy: %s",
 | 
					 | 
				
			||||||
			   osmo_cni_peer_id_type_name(lu->via_proxy.type));
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name.ipa_name, lu->is_ps,
 | 
					 | 
				
			||||||
			 osmo_cni_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name)) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
 | 
					 | 
				
			||||||
			   lu->is_ps ? "SGSN number" : "VLR number");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* TODO: Subscriber allowed to roam in PLMN? */
 | 
					 | 
				
			||||||
	/* TODO: Update RoutingInfo */
 | 
					 | 
				
			||||||
	/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
					 | 
				
			||||||
	/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lu_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void lu_rx_gsup(struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct lu *lu;
 | 
					 | 
				
			||||||
	if (req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST)
 | 
					 | 
				
			||||||
		return lu_start(req);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(lu, &g_all_lu, entry) {
 | 
					 | 
				
			||||||
		if (strcmp(lu->subscr.imsi, req->gsup.imsi))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		if (osmo_fsm_inst_dispatch(lu->fi, LU_EV_RX_GSUP, req)) {
 | 
					 | 
				
			||||||
			LOG_LU_REQ(lu, req, LOGL_ERROR, "Cannot receive GSUP messages in this state\n");
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE,
 | 
					 | 
				
			||||||
						  "LU does not accept GSUP rx");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "No Location Updating in progress for this IMSI");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int lu_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct lu *lu = fi->priv;
 | 
					 | 
				
			||||||
	lu_failure(lu, GSM_CAUSE_NET_FAIL, "Timeout");
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void lu_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct lu *lu = fi->priv;
 | 
					 | 
				
			||||||
	if (lu->update_location_req)
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(lu->update_location_req, GSM_CAUSE_NET_FAIL, "LU aborted");
 | 
					 | 
				
			||||||
	lu->update_location_req = NULL;
 | 
					 | 
				
			||||||
	llist_del(&lu->entry);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* Transmit Insert Data Request to the VLR */
 | 
					 | 
				
			||||||
	struct lu *lu = fi->priv;
 | 
					 | 
				
			||||||
	struct hlr_subscriber *subscr = &lu->subscr;
 | 
					 | 
				
			||||||
	struct osmo_gsup_message gsup;
 | 
					 | 
				
			||||||
	uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
 | 
					 | 
				
			||||||
	uint8_t apn[APN_MAXLEN];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi,
 | 
					 | 
				
			||||||
							subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
 | 
					 | 
				
			||||||
							apn, sizeof(apn),
 | 
					 | 
				
			||||||
							lu->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_gsup_req_respond(lu->update_location_req, &gsup, false, false))
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot send %s", osmo_gsup_message_type_name(gsup.message_type));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct lu *lu = fi->priv;
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *req;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (event) {
 | 
					 | 
				
			||||||
	case LU_EV_RX_GSUP:
 | 
					 | 
				
			||||||
		req = data;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		OSMO_ASSERT(false);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (req->gsup.message_type) {
 | 
					 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
					 | 
				
			||||||
		osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
		lu_success(lu);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
					 | 
				
			||||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type));
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "unexpected message type in this state");
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define S(x) (1 << (x))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct osmo_fsm_state lu_fsm_states[] = {
 | 
					 | 
				
			||||||
	[LU_ST_UNVALIDATED] = {
 | 
					 | 
				
			||||||
		.name = "UNVALIDATED",
 | 
					 | 
				
			||||||
		.out_state_mask = 0
 | 
					 | 
				
			||||||
			| S(LU_ST_WAIT_INSERT_DATA_RESULT)
 | 
					 | 
				
			||||||
			,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	[LU_ST_WAIT_INSERT_DATA_RESULT] = {
 | 
					 | 
				
			||||||
		.name = "WAIT_INSERT_DATA_RESULT",
 | 
					 | 
				
			||||||
		.in_event_mask = 0
 | 
					 | 
				
			||||||
			| S(LU_EV_RX_GSUP)
 | 
					 | 
				
			||||||
			,
 | 
					 | 
				
			||||||
		.onenter = lu_fsm_wait_insert_data_result_onenter,
 | 
					 | 
				
			||||||
		.action = lu_fsm_wait_insert_data_result,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct osmo_fsm lu_fsm = {
 | 
					 | 
				
			||||||
	.name = "lu",
 | 
					 | 
				
			||||||
	.states = lu_fsm_states,
 | 
					 | 
				
			||||||
	.num_states = ARRAY_SIZE(lu_fsm_states),
 | 
					 | 
				
			||||||
	.log_subsys = DLU,
 | 
					 | 
				
			||||||
	.event_names = lu_fsm_event_names,
 | 
					 | 
				
			||||||
	.timer_cb = lu_fsm_timer_cb,
 | 
					 | 
				
			||||||
	.cleanup = lu_fsm_cleanup,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static __attribute__((constructor)) void lu_fsm_init(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										258
									
								
								src/luop.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								src/luop.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,258 @@
 | 
				
			|||||||
 | 
					/* OsmoHLR TX/RX lu operations */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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 <stdbool.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/apn.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/hlr/gsup_server.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/gsup_router.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/hlr/luop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct value_string lu_state_names[] = {
 | 
				
			||||||
 | 
						{ LU_S_NULL,			"NULL" },
 | 
				
			||||||
 | 
						{ LU_S_LU_RECEIVED,		"LU RECEIVED" },
 | 
				
			||||||
 | 
						{ LU_S_CANCEL_SENT,		"CANCEL SENT" },
 | 
				
			||||||
 | 
						{ LU_S_CANCEL_ACK_RECEIVED,	"CANCEL-ACK RECEIVED" },
 | 
				
			||||||
 | 
						{ LU_S_ISD_SENT,		"ISD SENT" },
 | 
				
			||||||
 | 
						{ LU_S_ISD_ACK_RECEIVED,	"ISD-ACK RECEIVED" },
 | 
				
			||||||
 | 
						{ LU_S_COMPLETE,		"COMPLETE" },
 | 
				
			||||||
 | 
						{ 0, NULL }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Transmit a given GSUP message for the given LU operation */
 | 
				
			||||||
 | 
					static void _luop_tx_gsup(struct lu_operation *luop,
 | 
				
			||||||
 | 
								  const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg_out = osmo_gsup_msgb_alloc("GSUP LUOP");
 | 
				
			||||||
 | 
						osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_gsup_addr_send(luop->gsup_server, luop->peer,
 | 
				
			||||||
 | 
								    talloc_total_size(luop->peer),
 | 
				
			||||||
 | 
								    msg_out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void fill_gsup_msg(struct osmo_gsup_message *out,
 | 
				
			||||||
 | 
									 const struct lu_operation *lu,
 | 
				
			||||||
 | 
									 enum osmo_gsup_message_type mt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						memset(out, 0, sizeof(struct osmo_gsup_message));
 | 
				
			||||||
 | 
						if (lu)
 | 
				
			||||||
 | 
							osmo_strlcpy(out->imsi, lu->subscr.imsi,
 | 
				
			||||||
 | 
								     GSM23003_IMSI_MAX_DIGITS + 1);
 | 
				
			||||||
 | 
						out->message_type = mt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* timer call-back in case LU operation doesn't receive an response */
 | 
				
			||||||
 | 
					static void lu_op_timer_cb(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct lu_operation *luop = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
 | 
				
			||||||
 | 
							get_value_string(lu_state_names, luop->state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (luop->state) {
 | 
				
			||||||
 | 
						case LU_S_CANCEL_SENT:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case LU_S_ISD_SENT:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,
 | 
				
			||||||
 | 
							       const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber *subscr = &luop->subscr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (db_subscr_get_by_imsi(dbc, imsi, subscr) < 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct lu_operation *luop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						luop = talloc_zero(srv, struct lu_operation);
 | 
				
			||||||
 | 
						OSMO_ASSERT(luop);
 | 
				
			||||||
 | 
						luop->gsup_server = srv;
 | 
				
			||||||
 | 
						osmo_timer_setup(&luop->timer, lu_op_timer_cb, luop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return luop;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lu_op_free(struct lu_operation *luop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Only attempt to remove when it was ever added to a list. */
 | 
				
			||||||
 | 
						if (luop->list.next)
 | 
				
			||||||
 | 
							llist_del(&luop->list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Delete timer just in case it is still pending. */
 | 
				
			||||||
 | 
						osmo_timer_del(&luop->timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talloc_free(luop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t *peer_addr;
 | 
				
			||||||
 | 
						struct lu_operation *luop = lu_op_alloc(conn->server);
 | 
				
			||||||
 | 
						int rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							lu_op_free(luop);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						luop->peer = talloc_memdup(luop, peer_addr, rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return luop;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* FIXME: this doesn't seem to work at all */
 | 
				
			||||||
 | 
					struct lu_operation *lu_op_by_imsi(const char *imsi,
 | 
				
			||||||
 | 
									   const struct llist_head *lst)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct lu_operation *luop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(luop, lst, list) {
 | 
				
			||||||
 | 
							if (!strcmp(imsi, luop->subscr.imsi))
 | 
				
			||||||
 | 
								return luop;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum lu_state old_state = luop->state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DEBUGP(DMAIN, "LU OP state change: %s -> ",
 | 
				
			||||||
 | 
							get_value_string(lu_state_names, old_state));
 | 
				
			||||||
 | 
						DEBUGPC(DMAIN, "%s\n",
 | 
				
			||||||
 | 
							get_value_string(lu_state_names, new_state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						luop->state = new_state;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
 | 
				
			||||||
 | 
					void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DEBUGP(DMAIN, "%s: LU OP Tx Error (cause %s)\n",
 | 
				
			||||||
 | 
						       luop->subscr.imsi, get_value_string(gsm48_gmm_cause_names,
 | 
				
			||||||
 | 
											   cause));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR);
 | 
				
			||||||
 | 
						gsup.cause = cause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_luop_tx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_free(luop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
 | 
				
			||||||
 | 
					void lu_op_tx_ack(struct lu_operation *luop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT);
 | 
				
			||||||
 | 
						//FIXME gsup.hlr_enc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_luop_tx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_free(luop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Send Cancel Location to old VLR/SGSN */
 | 
				
			||||||
 | 
					void lu_op_tx_cancel_old(struct lu_operation *luop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fill_gsup_msg(&gsup, NULL, OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST);
 | 
				
			||||||
 | 
						//gsup.cause = FIXME;
 | 
				
			||||||
 | 
						//gsup.cancel_type = FIXME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_luop_tx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_statechg(luop, LU_S_CANCEL_SENT);
 | 
				
			||||||
 | 
						osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Transmit Insert Subscriber Data to new VLR/SGSN */
 | 
				
			||||||
 | 
					void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber *subscr = &luop->subscr;
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup = { };
 | 
				
			||||||
 | 
						uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
 | 
				
			||||||
 | 
						uint8_t apn[APN_MAXLEN];
 | 
				
			||||||
 | 
						enum osmo_gsup_cn_domain cn_domain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
 | 
				
			||||||
 | 
							    luop->state == LU_S_CANCEL_ACK_RECEIVED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (luop->is_ps)
 | 
				
			||||||
 | 
							cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
 | 
				
			||||||
 | 
												sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR,
 | 
				
			||||||
 | 
							       "IMSI='%s': Cannot notify GSUP client; could not create gsup message "
 | 
				
			||||||
 | 
							       "for %s\n", subscr->imsi, luop->peer);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Send ISD to new VLR/SGSN */
 | 
				
			||||||
 | 
						_luop_tx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu_op_statechg(luop, LU_S_ISD_SENT);
 | 
				
			||||||
 | 
						osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Transmit Delete Subscriber Data to new VLR/SGSN.
 | 
				
			||||||
 | 
					 * The luop is not freed. */
 | 
				
			||||||
 | 
					void lu_op_tx_del_subscr_data(struct lu_operation *luop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_DELETE_DATA_REQUEST);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Send ISD to new VLR/SGSN */
 | 
				
			||||||
 | 
						_luop_tx_gsup(luop, &gsup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
# This is _NOT_ the library release version, it's an API version.
 | 
					# This is _NOT_ the library release version, it's an API version.
 | 
				
			||||||
# Please read chapter "Library interface versions" of the libtool documentation
 | 
					# Please read chapter "Library interface versions" of the libtool documentation
 | 
				
			||||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
 | 
					# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
 | 
				
			||||||
LIBVERSION=1:0:0
 | 
					LIBVERSION=0:0:0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
 | 
					AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
 | 
				
			||||||
AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS)
 | 
					AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ int osmo_mdns_msg_request_encode(void *ctx, struct msgb *msg, const struct osmo_
 | 
				
			|||||||
	qst.domain = req->domain;
 | 
						qst.domain = req->domain;
 | 
				
			||||||
	qst.qtype = req->type;
 | 
						qst.qtype = req->type;
 | 
				
			||||||
	qst.qclass = OSMO_MDNS_RFC_CLASS_IN;
 | 
						qst.qclass = OSMO_MDNS_RFC_CLASS_IN;
 | 
				
			||||||
	if (osmo_mdns_rfc_question_encode(msg, &qst) != 0)
 | 
						if (osmo_mdns_rfc_question_encode(ctx, msg, &qst) != 0)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@@ -106,7 +106,7 @@ int osmo_mdns_msg_answer_encode(void *ctx, struct msgb *msg, const struct osmo_m
 | 
				
			|||||||
		rec.rdlength = ans_record->length;
 | 
							rec.rdlength = ans_record->length;
 | 
				
			||||||
		rec.rdata = ans_record->data;
 | 
							rec.rdata = ans_record->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (osmo_mdns_rfc_record_encode(msg, &rec) != 0)
 | 
							if (osmo_mdns_rfc_record_encode(ctx, msg, &rec) != 0)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,9 +213,9 @@ struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const cha
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	va_start(ap, value_fmt);
 | 
						va_start(ap, value_fmt);
 | 
				
			||||||
	value = talloc_vasprintf(ctx, value_fmt, ap);
 | 
						value = talloc_vasprintf(ctx, value_fmt, ap);
 | 
				
			||||||
	va_end(ap);
 | 
					 | 
				
			||||||
	if (!value)
 | 
						if (!value)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
	r = _osmo_mdns_record_txt_encode(ctx, key, value);
 | 
						r = _osmo_mdns_record_txt_encode(ctx, key, value);
 | 
				
			||||||
	talloc_free(value);
 | 
						talloc_free(value);
 | 
				
			||||||
	return r;
 | 
						return r;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,9 +27,86 @@
 | 
				
			|||||||
#include <osmocom/core/msgb.h>
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
#include <osmocom/core/bitvec.h>
 | 
					#include <osmocom/core/bitvec.h>
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
#include <osmocom/gsm/apn.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mdns_rfc.h>
 | 
					#include <osmocom/mslookup/mdns_rfc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Encode/decode IEs
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Encode a domain string as qname (RFC 1035 4.1.2).
 | 
				
			||||||
 | 
					 * \param[in] domain  multiple labels separated by dots, e.g. "sip.voice.1234.msisdn".
 | 
				
			||||||
 | 
					 * \returns allocated buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...), NULL on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *domain_dup;
 | 
				
			||||||
 | 
						char *domain_iter;
 | 
				
			||||||
 | 
						char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 2] = ""; /* len(qname) is len(domain) +1 */
 | 
				
			||||||
 | 
						struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
 | 
				
			||||||
 | 
						char *label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strlen(domain) > OSMO_MDNS_RFC_MAX_NAME_LEN)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						domain_iter = domain_dup = talloc_strdup(ctx, domain);
 | 
				
			||||||
 | 
						while ((label = strsep(&domain_iter, "."))) {
 | 
				
			||||||
 | 
							size_t len = strlen(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Empty domain, dot at start, two dots in a row, or ending with a dot */
 | 
				
			||||||
 | 
							if (!len)
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							OSMO_STRBUF_PRINTF(sb, "%c%s", (char)len, label);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talloc_free(domain_dup);
 | 
				
			||||||
 | 
						return talloc_strdup(ctx, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
						talloc_free(domain_dup);
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Decode a domain string from a qname (RFC 1035 4.1.2).
 | 
				
			||||||
 | 
					 * \param[in] qname  buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...)
 | 
				
			||||||
 | 
					 * \param[in] qname_max_len  amount of bytes that can be read at most from the memory location that qname points to.
 | 
				
			||||||
 | 
					 * \returns allocated buffer with domain string, multiple labels separated by dots (e.g. "sip.voice.1234.msisdn"),
 | 
				
			||||||
 | 
					 *	    NULL on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *next_label, *qname_end = qname + qname_max_len;
 | 
				
			||||||
 | 
						char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 1];
 | 
				
			||||||
 | 
						int i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (qname_max_len < 1)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (*qname) {
 | 
				
			||||||
 | 
							size_t len = *qname;
 | 
				
			||||||
 | 
							next_label = qname + len + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (i) {
 | 
				
			||||||
 | 
								/* Two dots in a row is not allowed */
 | 
				
			||||||
 | 
								if (buf[i - 1] == '.')
 | 
				
			||||||
 | 
									return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buf[i] = '.';
 | 
				
			||||||
 | 
								i++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							memcpy(buf + i, qname + 1, len);
 | 
				
			||||||
 | 
							i += len;
 | 
				
			||||||
 | 
							qname = next_label;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buf[i] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return talloc_strdup(ctx, buf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Encode/decode message sections
 | 
					 * Encode/decode message sections
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -69,17 +146,20 @@ int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osm
 | 
				
			|||||||
/*! Encode question section (RFC 1035 4.1.2).
 | 
					/*! Encode question section (RFC 1035 4.1.2).
 | 
				
			||||||
 * \param[in] msgb  mesage buffer to which the encoded data will be appended.
 | 
					 * \param[in] msgb  mesage buffer to which the encoded data will be appended.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int osmo_mdns_rfc_question_encode(struct msgb *msg, const struct osmo_mdns_rfc_question *qst)
 | 
					int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint8_t *buf;
 | 
						char *qname;
 | 
				
			||||||
	size_t buf_len;
 | 
						size_t qname_len;
 | 
				
			||||||
 | 
						uint8_t *qname_buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* qname */
 | 
						/* qname */
 | 
				
			||||||
	buf_len = strlen(qst->domain) + 1;
 | 
						qname = osmo_mdns_rfc_qname_encode(ctx, qst->domain);
 | 
				
			||||||
	buf = msgb_put(msg, buf_len);
 | 
						if (!qname)
 | 
				
			||||||
	if (osmo_apn_from_str(buf, buf_len, qst->domain) < 0)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	msgb_put_u8(msg, 0x00);
 | 
						qname_len = strlen(qname) + 1;
 | 
				
			||||||
 | 
						qname_buf = msgb_put(msg, qname_len);
 | 
				
			||||||
 | 
						memcpy(qname_buf, qname, qname_len);
 | 
				
			||||||
 | 
						talloc_free(qname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* qtype and qclass */
 | 
						/* qtype and qclass */
 | 
				
			||||||
	msgb_put_u16(msg, qst->qtype);
 | 
						msgb_put_u16(msg, qst->qtype);
 | 
				
			||||||
@@ -97,25 +177,21 @@ struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const ui
 | 
				
			|||||||
	if (data_len < 6)
 | 
						if (data_len < 6)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* qname */
 | 
				
			||||||
	ret = talloc_zero(ctx, struct osmo_mdns_rfc_question);
 | 
						ret = talloc_zero(ctx, struct osmo_mdns_rfc_question);
 | 
				
			||||||
	if (!ret)
 | 
						if (!ret)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
						ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, qname_len);
 | 
				
			||||||
	/* qname */
 | 
						if (!ret->domain) {
 | 
				
			||||||
	ret->domain = talloc_size(ret, qname_len - 1);
 | 
							talloc_free(ret);
 | 
				
			||||||
	if (!ret->domain)
 | 
							return NULL;
 | 
				
			||||||
		goto error;
 | 
						}
 | 
				
			||||||
	if (!osmo_apn_to_str(ret->domain, data, qname_len - 1))
 | 
					 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* qtype and qclass */
 | 
						/* qtype and qclass */
 | 
				
			||||||
	ret->qtype = osmo_load16be(data + qname_len);
 | 
						ret->qtype = osmo_load16be(data + qname_len);
 | 
				
			||||||
	ret->qclass = osmo_load16be(data + qname_len + 2);
 | 
						ret->qclass = osmo_load16be(data + qname_len + 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
error:
 | 
					 | 
				
			||||||
	talloc_free(ret);
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -125,17 +201,20 @@ error:
 | 
				
			|||||||
/*! Encode one resource record (RFC 1035 4.1.3).
 | 
					/*! Encode one resource record (RFC 1035 4.1.3).
 | 
				
			||||||
 * \param[in] msgb  mesage buffer to which the encoded data will be appended.
 | 
					 * \param[in] msgb  mesage buffer to which the encoded data will be appended.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_record *rec)
 | 
					int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						char *name;
 | 
				
			||||||
 | 
						size_t name_len;
 | 
				
			||||||
	uint8_t *buf;
 | 
						uint8_t *buf;
 | 
				
			||||||
	size_t buf_len;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* name */
 | 
						/* name */
 | 
				
			||||||
	buf_len = strlen(rec->domain) + 1;
 | 
						name = osmo_mdns_rfc_qname_encode(ctx, rec->domain);
 | 
				
			||||||
	buf = msgb_put(msg, buf_len);
 | 
						if (!name)
 | 
				
			||||||
	if (osmo_apn_from_str(buf, buf_len, rec->domain) < 0)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	msgb_put_u8(msg, 0x00);
 | 
						name_len = strlen(name) + 1;
 | 
				
			||||||
 | 
						buf = msgb_put(msg, name_len);
 | 
				
			||||||
 | 
						memcpy(buf, name, name_len);
 | 
				
			||||||
 | 
						talloc_free(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* type, class, ttl, rdlength */
 | 
						/* type, class, ttl, rdlength */
 | 
				
			||||||
	msgb_put_u16(msg, rec->type);
 | 
						msgb_put_u16(msg, rec->type);
 | 
				
			||||||
@@ -153,26 +232,15 @@ int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_rec
 | 
				
			|||||||
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
 | 
					struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
 | 
				
			||||||
						       size_t *record_len)
 | 
											       size_t *record_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_mdns_rfc_record *ret;
 | 
						struct osmo_mdns_rfc_record *ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
 | 
				
			||||||
	size_t name_len;
 | 
						size_t name_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* name length: represented as a series of labels, and terminated by a
 | 
						/* name */
 | 
				
			||||||
	 * label with zero length (RFC 1035 3.3). A label with zero length is a
 | 
						ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, data_len - 10);
 | 
				
			||||||
	 * NUL byte. */
 | 
					 | 
				
			||||||
	name_len = strnlen((const char *)data, data_len - 10) + 1;
 | 
					 | 
				
			||||||
	if (data[name_len])
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* allocate ret + ret->domain */
 | 
					 | 
				
			||||||
	ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
 | 
					 | 
				
			||||||
	if (!ret)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	ret->domain = talloc_size(ctx, name_len - 1);
 | 
					 | 
				
			||||||
	if (!ret->domain)
 | 
						if (!ret->domain)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						name_len = strlen(ret->domain) + 2;
 | 
				
			||||||
	/* name */
 | 
						if (name_len + 10 > data_len)
 | 
				
			||||||
	if (!osmo_apn_to_str(ret->domain, data, name_len - 1))
 | 
					 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* type, class, ttl, rdlength */
 | 
						/* type, class, ttl, rdlength */
 | 
				
			||||||
@@ -186,7 +254,7 @@ struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_
 | 
				
			|||||||
	/* rdata */
 | 
						/* rdata */
 | 
				
			||||||
	ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength);
 | 
						ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength);
 | 
				
			||||||
	if (!ret->rdata)
 | 
						if (!ret->rdata)
 | 
				
			||||||
		goto error;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*record_len = name_len + 10 + ret->rdlength;
 | 
						*record_len = name_len + 10 + ret->rdlength;
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
 | 
				
			|||||||
	rc = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface));
 | 
						rc = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface));
 | 
				
			||||||
	if (rc == -1) {
 | 
						if (rc == -1) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
				
			||||||
		goto error_sock;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr,
 | 
						memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr,
 | 
				
			||||||
	       sizeof(multicast_req.imr_multiaddr));
 | 
						       sizeof(multicast_req.imr_multiaddr));
 | 
				
			||||||
@@ -92,7 +92,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
 | 
				
			|||||||
	rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req));
 | 
						rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req));
 | 
				
			||||||
	if (rc == -1) {
 | 
						if (rc == -1) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
				
			||||||
		goto error_sock;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by
 | 
						/* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by
 | 
				
			||||||
@@ -102,22 +102,20 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
 | 
				
			|||||||
	rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y));
 | 
						rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y));
 | 
				
			||||||
	if (rc == -1) {
 | 
						if (rc == -1) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
				
			||||||
		goto error_sock;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Bind and register osmo_fd callback */
 | 
						/* Bind and register osmo_fd callback */
 | 
				
			||||||
	rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen);
 | 
						rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen);
 | 
				
			||||||
	if (rc == -1) {
 | 
						if (rc == -1) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: bind: %s\n", strerror(errno));
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: bind: %s\n", strerror(errno));
 | 
				
			||||||
		goto error_sock;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	osmo_fd_setup(&ret->osmo_fd, sock, OSMO_FD_READ, cb, data, priv_nr);
 | 
						osmo_fd_setup(&ret->osmo_fd, sock, OSMO_FD_READ, cb, data, priv_nr);
 | 
				
			||||||
	if (osmo_fd_register(&ret->osmo_fd) != 0)
 | 
						if (osmo_fd_register(&ret->osmo_fd) != 0)
 | 
				
			||||||
		goto error_sock;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
error_sock:
 | 
					 | 
				
			||||||
	close(sock);
 | 
					 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	if (ret->ai)
 | 
						if (ret->ai)
 | 
				
			||||||
		freeaddrinfo(ret->ai);
 | 
							freeaddrinfo(ret->ai);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -146,7 +146,7 @@ void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_
 | 
				
			|||||||
	if (!req) {
 | 
						if (!req) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
				
			||||||
		     "Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
 | 
							     "Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
 | 
				
			||||||
		     request_handle);
 | 
							     req->request_handle);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,7 +114,6 @@ static void mdns_method_request(struct osmo_mslookup_client_method *method, cons
 | 
				
			|||||||
	if (!msg) {
 | 
						if (!msg) {
 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n",
 | 
							LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n",
 | 
				
			||||||
		     osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
 | 
							     osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Send over the wire */
 | 
						/* Send over the wire */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,7 +53,7 @@ static void print_version(void)
 | 
				
			|||||||
	"\n");
 | 
						"\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_help(void)
 | 
					static void print_help()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	print_version();
 | 
						print_version();
 | 
				
			||||||
	printf(
 | 
						printf(
 | 
				
			||||||
@@ -315,18 +315,17 @@ int do_send(int argc, char ** argv)
 | 
				
			|||||||
	struct msgb *msg = osmo_mdns_result_encode(ctx, 0, &q, &r, cmdline_opts.mdns_domain_suffix);
 | 
						struct msgb *msg = osmo_mdns_result_encode(ctx, 0, &q, &r, cmdline_opts.mdns_domain_suffix);
 | 
				
			||||||
	if (!msg) {
 | 
						if (!msg) {
 | 
				
			||||||
		print_error("unable to encode mDNS response\n");
 | 
							print_error("unable to encode mDNS response\n");
 | 
				
			||||||
		goto exit_cleanup_sock;
 | 
							goto exit_cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (osmo_mdns_sock_send(sock, msg)) {
 | 
						if (osmo_mdns_sock_send(sock, msg)) {
 | 
				
			||||||
		print_error("unable to send mDNS message\n");
 | 
							print_error("unable to send mDNS message\n");
 | 
				
			||||||
		goto exit_cleanup_sock;
 | 
							goto exit_cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = 0;
 | 
						rc = 0;
 | 
				
			||||||
exit_cleanup_sock:
 | 
					 | 
				
			||||||
	osmo_mdns_sock_cleanup(sock);
 | 
					 | 
				
			||||||
exit_cleanup:
 | 
					exit_cleanup:
 | 
				
			||||||
 | 
						osmo_mdns_sock_cleanup(sock);
 | 
				
			||||||
	talloc_free(ctx);
 | 
						talloc_free(ctx);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -456,11 +455,6 @@ static int socket_read_cb(struct osmo_fd *ofd)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	rxbuf[rc] = '\0';
 | 
						rxbuf[rc] = '\0';
 | 
				
			||||||
	query_with_timeout = strtok(rxbuf, "\r\n");
 | 
						query_with_timeout = strtok(rxbuf, "\r\n");
 | 
				
			||||||
	if (!query_with_timeout) {
 | 
					 | 
				
			||||||
		print_error("ERROR: failed to read line from socket\n");
 | 
					 | 
				
			||||||
		goto close;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	at = strchr(query_with_timeout, '@');
 | 
						at = strchr(query_with_timeout, '@');
 | 
				
			||||||
	query_str = at ? at + 1 : query_with_timeout;
 | 
						query_str = at ? at + 1 : query_with_timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -487,7 +481,7 @@ static int socket_cb(struct osmo_fd *ofd, unsigned int flags)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	int rc = 0;
 | 
						int rc = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & OSMO_FD_READ)
 | 
						if (flags & BSC_FD_READ)
 | 
				
			||||||
		rc = socket_read_cb(ofd);
 | 
							rc = socket_read_cb(ofd);
 | 
				
			||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
		return rc;
 | 
							return rc;
 | 
				
			||||||
@@ -512,7 +506,7 @@ int socket_accept(struct osmo_fd *ofd, unsigned int flags)
 | 
				
			|||||||
	c = talloc_zero(globals.ctx, struct socket_client);
 | 
						c = talloc_zero(globals.ctx, struct socket_client);
 | 
				
			||||||
	OSMO_ASSERT(c);
 | 
						OSMO_ASSERT(c);
 | 
				
			||||||
	c->ofd.fd = rc;
 | 
						c->ofd.fd = rc;
 | 
				
			||||||
	c->ofd.when = OSMO_FD_READ;
 | 
						c->ofd.when = BSC_FD_READ;
 | 
				
			||||||
	c->ofd.cb = socket_cb;
 | 
						c->ofd.cb = socket_cb;
 | 
				
			||||||
	c->ofd.data = c;
 | 
						c->ofd.data = c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -543,7 +537,7 @@ int socket_init(const char *sock_path)
 | 
				
			|||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ofd->when = OSMO_FD_READ;
 | 
						ofd->when = BSC_FD_READ;
 | 
				
			||||||
	ofd->cb = socket_accept;
 | 
						ofd->cb = socket_accept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = osmo_fd_register(ofd);
 | 
						rc = osmo_fd_register(ofd);
 | 
				
			||||||
@@ -555,7 +549,7 @@ int socket_init(const char *sock_path)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void socket_close(void)
 | 
					void socket_close()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct socket_client *c, *n;
 | 
						struct socket_client *c, *n;
 | 
				
			||||||
	llist_for_each_entry_safe(c, n, &globals.socket_clients, entry)
 | 
						llist_for_each_entry_safe(c, n, &globals.socket_clients, entry)
 | 
				
			||||||
@@ -584,11 +578,11 @@ void respond_result(const char *query_str, const struct osmo_mslookup_result *r)
 | 
				
			|||||||
	llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) {
 | 
						llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) {
 | 
				
			||||||
		if (!strcmp(query_str, c->query_str)) {
 | 
							if (!strcmp(query_str, c->query_str)) {
 | 
				
			||||||
			socket_client_respond_result(c, g_buf);
 | 
								socket_client_respond_result(c, g_buf);
 | 
				
			||||||
			if (!r || r->last)
 | 
								if (r->last)
 | 
				
			||||||
				socket_client_close(c);
 | 
									socket_client_close(c);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!r || r->last)
 | 
						if (r->last)
 | 
				
			||||||
		globals.requests_handled++;
 | 
							globals.requests_handled++;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,459 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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 <string.h>
 | 
					 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <osmocom/core/sockaddr_str.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/timestamp.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/proxy.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct osmo_mslookup_result not_found = {
 | 
					 | 
				
			||||||
		.rc = OSMO_MSLOOKUP_RC_NOT_FOUND,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
const struct osmo_ipa_name mslookup_server_msc_wildcard = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void set_result(struct osmo_mslookup_result *result,
 | 
					 | 
				
			||||||
		       const struct mslookup_service_host *service_host,
 | 
					 | 
				
			||||||
		       uint32_t age)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!osmo_sockaddr_str_is_nonzero(&service_host->host_v4)
 | 
					 | 
				
			||||||
	    && !osmo_sockaddr_str_is_nonzero(&service_host->host_v6)) {
 | 
					 | 
				
			||||||
		*result = not_found;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	result->rc = OSMO_MSLOOKUP_RC_RESULT;
 | 
					 | 
				
			||||||
	result->host_v4 = service_host->host_v4;
 | 
					 | 
				
			||||||
	result->host_v6 = service_host->host_v6;
 | 
					 | 
				
			||||||
	result->age = age;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	static struct mslookup_service_host gsup_bind = {};
 | 
					 | 
				
			||||||
	struct mslookup_service_host *host;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Find a HLR/GSUP service set for the server (no VLR unit name) */
 | 
					 | 
				
			||||||
	host = mslookup_server_service_get(&mslookup_server_msc_wildcard, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
 | 
					 | 
				
			||||||
	if (host)
 | 
					 | 
				
			||||||
		return host;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Try to use the locally configured GSUP bind address */
 | 
					 | 
				
			||||||
	osmo_sockaddr_str_from_str(&gsup_bind.host_v4, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT);
 | 
					 | 
				
			||||||
	if (gsup_bind.host_v4.af == AF_INET6) {
 | 
					 | 
				
			||||||
		gsup_bind.host_v6 = gsup_bind.host_v4;
 | 
					 | 
				
			||||||
		gsup_bind.host_v4 = (struct osmo_sockaddr_str){};
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &gsup_bind;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct llist_head *c = &g_hlr->mslookup.server.local_site_services;
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msc_name)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(msc, c, entry) {
 | 
					 | 
				
			||||||
		if (osmo_ipa_name_cmp(&msc->name, msc_name))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		return msc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (!create)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msc = talloc_zero(g_hlr, struct mslookup_server_msc_cfg);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(msc);
 | 
					 | 
				
			||||||
	INIT_LLIST_HEAD(&msc->service_hosts);
 | 
					 | 
				
			||||||
	msc->name = *msc_name;
 | 
					 | 
				
			||||||
	llist_add_tail(&msc->entry, c);
 | 
					 | 
				
			||||||
	return msc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
							      bool create)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_service_host *e;
 | 
					 | 
				
			||||||
	if (!msc)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(e, &msc->service_hosts, entry) {
 | 
					 | 
				
			||||||
		if (!strcmp(e->service, service))
 | 
					 | 
				
			||||||
			return e;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!create)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	e = talloc_zero(msc, struct mslookup_service_host);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(e);
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(e->service, service);
 | 
					 | 
				
			||||||
	llist_add_tail(&e->entry, &msc->service_hosts);
 | 
					 | 
				
			||||||
	return e;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_server_msc_cfg *msc = mslookup_server_msc_get(msc_name, false);
 | 
					 | 
				
			||||||
	if (!msc)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	return mslookup_server_msc_service_get(msc, service, false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
				    const struct osmo_sockaddr_str *addr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_service_host *e;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!service || !service[0]
 | 
					 | 
				
			||||||
	    || strlen(service) > OSMO_MSLOOKUP_SERVICE_MAXLEN)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	if (!addr || !osmo_sockaddr_str_is_nonzero(addr))
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	e = mslookup_server_msc_service_get(msc, service, true);
 | 
					 | 
				
			||||||
	if (!e)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (addr->af) {
 | 
					 | 
				
			||||||
	case AF_INET:
 | 
					 | 
				
			||||||
		e->host_v4 = *addr;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case AF_INET6:
 | 
					 | 
				
			||||||
		e->host_v6 = *addr;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service,
 | 
					 | 
				
			||||||
				    const struct osmo_sockaddr_str *addr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mslookup_service_host *e, *n;
 | 
					 | 
				
			||||||
	int deleted = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!msc)
 | 
					 | 
				
			||||||
		return -ENOENT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry_safe(e, n, &msc->service_hosts, entry) {
 | 
					 | 
				
			||||||
		if (service && strcmp(service, e->service))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (addr) {
 | 
					 | 
				
			||||||
			if (!osmo_sockaddr_str_cmp(addr, &e->host_v4)) {
 | 
					 | 
				
			||||||
				e->host_v4 = (struct osmo_sockaddr_str){};
 | 
					 | 
				
			||||||
				/* Removed one addr. If the other is still there, keep the entry. */
 | 
					 | 
				
			||||||
				if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
 | 
					 | 
				
			||||||
					continue;
 | 
					 | 
				
			||||||
			} else if (!osmo_sockaddr_str_cmp(addr, &e->host_v6)) {
 | 
					 | 
				
			||||||
				e->host_v6 = (struct osmo_sockaddr_str){};
 | 
					 | 
				
			||||||
				/* Removed one addr. If the other is still there, keep the entry. */
 | 
					 | 
				
			||||||
				if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
 | 
					 | 
				
			||||||
					continue;
 | 
					 | 
				
			||||||
			} else
 | 
					 | 
				
			||||||
				/* No addr match, keep the entry. */
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			/* Addr matched and none is left. Delete. */
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		llist_del(&e->entry);
 | 
					 | 
				
			||||||
		talloc_free(e);
 | 
					 | 
				
			||||||
		deleted++;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return deleted;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* A remote entity is asking us whether we are the home HLR of the given subscriber. */
 | 
					 | 
				
			||||||
static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
					struct osmo_mslookup_result *result)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const struct mslookup_service_host *host;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	switch (query->id.type) {
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_IMSI:
 | 
					 | 
				
			||||||
		rc = db_subscr_exists_by_imsi(g_hlr->dbc, query->id.imsi);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_MSISDN:
 | 
					 | 
				
			||||||
		rc = db_subscr_exists_by_msisdn(g_hlr->dbc, query->id.msisdn);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
 | 
					 | 
				
			||||||
		*result = not_found;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		*result = not_found;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: found in local HLR\n",
 | 
					 | 
				
			||||||
	     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host = mslookup_server_get_local_gsup_addr();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	set_result(result, host, 0);
 | 
					 | 
				
			||||||
	if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
					 | 
				
			||||||
		     "Subscriber found, but error in service '" OSMO_MSLOOKUP_SERVICE_HLR_GSUP "' config:"
 | 
					 | 
				
			||||||
		     " v4: " OSMO_SOCKADDR_STR_FMT "  v6: " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v4),
 | 
					 | 
				
			||||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v6));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Look in the local HLR record: If the subscriber is "at home" in this HLR and is also currently located at a local
 | 
					 | 
				
			||||||
 * VLR, we will find a valid location updating with vlr_number, and no vlr_via_proxy entry. */
 | 
					 | 
				
			||||||
static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
					    uint32_t *lu_age,
 | 
					 | 
				
			||||||
					    struct osmo_ipa_name *local_msc_name,
 | 
					 | 
				
			||||||
					    struct hlr_subscriber *ret_subscr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct hlr_subscriber _subscr;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	uint32_t age;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct hlr_subscriber *subscr = ret_subscr ? : &_subscr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (query->id.type) {
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_IMSI:
 | 
					 | 
				
			||||||
		rc = db_subscr_get_by_imsi(g_hlr->dbc, query->id.imsi, subscr);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_MSISDN:
 | 
					 | 
				
			||||||
		rc = db_subscr_get_by_msisdn(g_hlr->dbc, query->id.msisdn, subscr);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!subscr->vlr_number[0]) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (subscr->vlr_via_proxy.len) {
 | 
					 | 
				
			||||||
		/* The VLR is behind a proxy, the subscriber is not attached to a local VLR but a remote one. That
 | 
					 | 
				
			||||||
		 * remote proxy should instead respond to the service lookup request. */
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy %s\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
		     subscr->vlr_number,
 | 
					 | 
				
			||||||
		     osmo_ipa_name_to_str(&subscr->vlr_via_proxy));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!timestamp_age(&subscr->last_lu_seen, &age)) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: Invalid last_lu_seen timestamp for subscriber\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (age > g_hlr->mslookup.server.local_attach_max_age) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach was here, but too long ago: %us > %us\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
		     age, g_hlr->mslookup.server.local_attach_max_age);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*lu_age = age;
 | 
					 | 
				
			||||||
	osmo_ipa_name_set_str(local_msc_name, subscr->vlr_number);
 | 
					 | 
				
			||||||
	LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s\n",
 | 
					 | 
				
			||||||
	     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
	     age, osmo_ipa_name_to_str(local_msc_name));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Determine whether the subscriber with the given ID has routed a Location Updating via this HLR as first hop. Return
 | 
					 | 
				
			||||||
 * true if it is attached at a local VLR, and we are serving as proxy for a remote home HLR.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
					      uint32_t *lu_age,
 | 
					 | 
				
			||||||
					      struct osmo_ipa_name *local_msc_name,
 | 
					 | 
				
			||||||
					      struct proxy_subscr *ret_proxy_subscr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr;
 | 
					 | 
				
			||||||
	uint32_t age;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* See the local HLR record. If the subscriber is "at home" in this HLR and is also currently located here, we
 | 
					 | 
				
			||||||
	 * will find a valid location updating and no vlr_via_proxy entry. */
 | 
					 | 
				
			||||||
	switch (query->id.type) {
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_IMSI:
 | 
					 | 
				
			||||||
		rc = proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, query->id.imsi);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case OSMO_MSLOOKUP_ID_MSISDN:
 | 
					 | 
				
			||||||
		rc = proxy_subscr_get_by_msisdn(&proxy_subscr, g_hlr->gs->proxy, query->id.msisdn);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_ERROR, "%s: unknown ID type\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_DEBUG, "%s: does not exist in GSUP proxy\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* We only need to care about CS LU, since only CS services need D-GSM routing. */
 | 
					 | 
				
			||||||
	if (!timestamp_age(&proxy_subscr.cs.last_lu, &age)
 | 
					 | 
				
			||||||
	    || age > g_hlr->mslookup.server.local_attach_max_age) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_ERROR,
 | 
					 | 
				
			||||||
		     "%s: last attach was at local VLR (proxying for remote HLR), but too long ago: %us > %us\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
		     age, g_hlr->mslookup.server.local_attach_max_age);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (proxy_subscr.cs.vlr_via_proxy.len) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy '%s'\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
		     osmo_ipa_name_to_str(&proxy_subscr.cs.vlr_name),
 | 
					 | 
				
			||||||
		     osmo_ipa_name_to_str(&proxy_subscr.cs.vlr_via_proxy));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*lu_age = age;
 | 
					 | 
				
			||||||
	*local_msc_name = proxy_subscr.cs.vlr_name;
 | 
					 | 
				
			||||||
	LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s; proxying for remote HLR "
 | 
					 | 
				
			||||||
	     OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
	     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
	     age, osmo_ipa_name_to_str(local_msc_name),
 | 
					 | 
				
			||||||
	     OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr.remote_hlr_addr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret_proxy_subscr)
 | 
					 | 
				
			||||||
		*ret_proxy_subscr = proxy_subscr;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
				 uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name,
 | 
					 | 
				
			||||||
				 char *ret_imsi, size_t ret_imsi_len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	bool attached_here;
 | 
					 | 
				
			||||||
	uint32_t lu_age = 0;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name msc_name = {};
 | 
					 | 
				
			||||||
	bool attached_here_proxy;
 | 
					 | 
				
			||||||
	uint32_t proxy_lu_age = 0;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name proxy_msc_name = {};
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr;
 | 
					 | 
				
			||||||
	struct hlr_subscriber db_subscr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* First ask the local HLR db, but if the local proxy record indicates a more recent LU, use that instead.
 | 
					 | 
				
			||||||
	 * For all usual cases, only one of these will reflect a LU, even if a subscriber had more than one home HLR:
 | 
					 | 
				
			||||||
	 *   - if the subscriber is known here, we will never proxy.
 | 
					 | 
				
			||||||
	 *   - if the subscriber is not known here, this local HLR db will never record a LU.
 | 
					 | 
				
			||||||
	 * However, if a subscriber was being proxied to a remote home HLR, and if then the subscriber was also added to
 | 
					 | 
				
			||||||
	 * the local HLR database, there might occur a situation where both reflect a LU. So, to be safe against all
 | 
					 | 
				
			||||||
	 * situations, compare the two entries.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	attached_here = subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name, &db_subscr);
 | 
					 | 
				
			||||||
	attached_here_proxy = subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name, &proxy_subscr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If proxy has a younger lu, replace. */
 | 
					 | 
				
			||||||
	if (attached_here_proxy && (!attached_here || (proxy_lu_age < lu_age))) {
 | 
					 | 
				
			||||||
		attached_here = true;
 | 
					 | 
				
			||||||
		lu_age = proxy_lu_age;
 | 
					 | 
				
			||||||
		msc_name = proxy_msc_name;
 | 
					 | 
				
			||||||
		if (ret_imsi)
 | 
					 | 
				
			||||||
			osmo_strlcpy(ret_imsi, proxy_subscr.imsi, ret_imsi_len);
 | 
					 | 
				
			||||||
	} else if (attached_here) {
 | 
					 | 
				
			||||||
		if (ret_imsi)
 | 
					 | 
				
			||||||
			osmo_strlcpy(ret_imsi, db_subscr.imsi, ret_imsi_len);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attached_here && !msc_name.len) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "%s: attached here, but no VLR name known\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!attached_here) {
 | 
					 | 
				
			||||||
		/* Already logged "not attached" for both local-db and proxy attach */
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGP(DMSLOOKUP, LOGL_INFO, "%s: attached here, at VLR %s\n",
 | 
					 | 
				
			||||||
	     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
	     osmo_ipa_name_to_str(&msc_name));
 | 
					 | 
				
			||||||
	*lu_age_p = lu_age;
 | 
					 | 
				
			||||||
	*local_msc_name = msc_name;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* A remote entity is asking us whether we are providing the given service for the given subscriber. */
 | 
					 | 
				
			||||||
void mslookup_server_rx(const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
			struct osmo_mslookup_result *result)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const struct mslookup_service_host *service_host;
 | 
					 | 
				
			||||||
	uint32_t age;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name msc_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* A request for a home HLR: answer exactly if this is the subscriber's home HLR, i.e. the IMSI is listed in the
 | 
					 | 
				
			||||||
	 * HLR database. */
 | 
					 | 
				
			||||||
	if (strcmp(query->service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP) == 0)
 | 
					 | 
				
			||||||
		return mslookup_server_rx_hlr_gsup(query, result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* All other service types: answer when the subscriber has done a LU that is either listed in the local HLR or
 | 
					 | 
				
			||||||
	 * in the GSUP proxy database: i.e. if the subscriber has done a Location Updating at an VLR belonging to this
 | 
					 | 
				
			||||||
	 * HLR. Respond with whichever services are configured in the osmo-hlr.cfg. */
 | 
					 | 
				
			||||||
	if (!subscriber_has_done_lu_here(query, &age, &msc_name, NULL, 0)) {
 | 
					 | 
				
			||||||
		*result = not_found;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* We've detected a LU here. The VLR where the LU happened is stored in msc_unit_name, and the LU age is stored
 | 
					 | 
				
			||||||
	 * in 'age'. Figure out the address configured for that VLR and service name. */
 | 
					 | 
				
			||||||
	service_host = mslookup_server_service_get(&msc_name, query->service);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!service_host) {
 | 
					 | 
				
			||||||
		/* Find such service set globally (no VLR unit name) */
 | 
					 | 
				
			||||||
		service_host = mslookup_server_service_get(&mslookup_server_msc_wildcard, query->service);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!service_host) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
					 | 
				
			||||||
		     "%s: subscriber found, but no service %s configured, cannot service lookup request\n",
 | 
					 | 
				
			||||||
		     osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
 | 
					 | 
				
			||||||
		     osmo_quote_str_c(OTC_SELECT, query->service, -1));
 | 
					 | 
				
			||||||
		*result = not_found;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	set_result(result, service_host, age);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,157 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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 <stdlib.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mslookup.h>
 | 
					 | 
				
			||||||
#include <osmocom/mslookup/mdns.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/mslookup_server_mdns.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void osmo_mslookup_server_mdns_tx(struct osmo_mslookup_server_mdns *server,
 | 
					 | 
				
			||||||
					 uint16_t packet_id,
 | 
					 | 
				
			||||||
					 const struct osmo_mslookup_query *query,
 | 
					 | 
				
			||||||
					 const struct osmo_mslookup_result *result)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct msgb *msg;
 | 
					 | 
				
			||||||
	const char *errmsg = NULL;
 | 
					 | 
				
			||||||
	void *ctx = talloc_named_const(server, 0, __func__);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg = osmo_mdns_result_encode(ctx, packet_id, query, result, server->domain_suffix);
 | 
					 | 
				
			||||||
	if (!msg)
 | 
					 | 
				
			||||||
		errmsg = "Error encoding mDNS answer packet";
 | 
					 | 
				
			||||||
	else if (osmo_mdns_sock_send(server->sock, msg))
 | 
					 | 
				
			||||||
		errmsg = "Error sending mDNS answer";
 | 
					 | 
				
			||||||
	if (errmsg)
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "%s: mDNS: %s\n", osmo_mslookup_result_name_c(ctx, query, result), errmsg);
 | 
					 | 
				
			||||||
	talloc_free(ctx);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void osmo_mslookup_server_mdns_handle_request(uint16_t packet_id,
 | 
					 | 
				
			||||||
						     struct osmo_mslookup_server_mdns *server,
 | 
					 | 
				
			||||||
						     const struct osmo_mslookup_query *query)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_mslookup_result result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mslookup_server_rx(query, &result);
 | 
					 | 
				
			||||||
	/* Error logging already happens in mslookup_server_rx() */
 | 
					 | 
				
			||||||
	if (result.rc != OSMO_MSLOOKUP_RC_RESULT)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_mslookup_server_mdns_tx(server, packet_id, query, &result);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int osmo_mslookup_server_mdns_rx(struct osmo_fd *osmo_fd, unsigned int what)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_mslookup_server_mdns *server = osmo_fd->data;
 | 
					 | 
				
			||||||
	struct osmo_mslookup_query *query;
 | 
					 | 
				
			||||||
	uint16_t packet_id;
 | 
					 | 
				
			||||||
	int n;
 | 
					 | 
				
			||||||
	uint8_t buffer[1024];
 | 
					 | 
				
			||||||
	void *ctx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Parse the message and print it */
 | 
					 | 
				
			||||||
	n = read(osmo_fd->fd, buffer, sizeof(buffer));
 | 
					 | 
				
			||||||
	if (n <= 0)
 | 
					 | 
				
			||||||
		return n;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx = talloc_named_const(server, 0, __func__);
 | 
					 | 
				
			||||||
	query = osmo_mdns_query_decode(ctx, buffer, n, &packet_id, server->domain_suffix);
 | 
					 | 
				
			||||||
	if (!query) {
 | 
					 | 
				
			||||||
		talloc_free(ctx);
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_mslookup_id_name_buf((char *)buffer, sizeof(buffer), &query->id);
 | 
					 | 
				
			||||||
	LOGP(DMSLOOKUP, LOGL_DEBUG, "mDNS rx request: %s.%s\n", query->service, buffer);
 | 
					 | 
				
			||||||
	osmo_mslookup_server_mdns_handle_request(packet_id, server, query);
 | 
					 | 
				
			||||||
	talloc_free(ctx);
 | 
					 | 
				
			||||||
	return n;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct osmo_mslookup_server_mdns *osmo_mslookup_server_mdns_start(void *ctx, const struct osmo_sockaddr_str *bind_addr,
 | 
					 | 
				
			||||||
								  const char *domain_suffix)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_mslookup_server_mdns *server = talloc_zero(ctx, struct osmo_mslookup_server_mdns);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(server);
 | 
					 | 
				
			||||||
	*server = (struct osmo_mslookup_server_mdns){
 | 
					 | 
				
			||||||
		.bind_addr = *bind_addr,
 | 
					 | 
				
			||||||
		.domain_suffix = talloc_strdup(server, domain_suffix)
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	server->sock = osmo_mdns_sock_init(server,
 | 
					 | 
				
			||||||
					   bind_addr->ip, bind_addr->port,
 | 
					 | 
				
			||||||
					   osmo_mslookup_server_mdns_rx,
 | 
					 | 
				
			||||||
					   server, 0);
 | 
					 | 
				
			||||||
	if (!server->sock) {
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
					 | 
				
			||||||
		     "mslookup mDNS server: error initializing multicast bind on " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(bind_addr));
 | 
					 | 
				
			||||||
		talloc_free(server);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return server;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!server)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	osmo_mdns_sock_cleanup(server->sock);
 | 
					 | 
				
			||||||
	talloc_free(server);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void mslookup_server_mdns_config_apply(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* Check whether to start/stop/restart mDNS server */
 | 
					 | 
				
			||||||
	bool should_run;
 | 
					 | 
				
			||||||
	bool should_stop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	should_run = g_hlr->mslookup.allow_startup
 | 
					 | 
				
			||||||
		&& g_hlr->mslookup.server.enable && g_hlr->mslookup.server.mdns.enable;
 | 
					 | 
				
			||||||
	should_stop = g_hlr->mslookup.server.mdns.running
 | 
					 | 
				
			||||||
		&& (!should_run
 | 
					 | 
				
			||||||
		    || osmo_sockaddr_str_cmp(&g_hlr->mslookup.server.mdns.bind_addr,
 | 
					 | 
				
			||||||
					     &g_hlr->mslookup.server.mdns.running->bind_addr)
 | 
					 | 
				
			||||||
		    || strcmp(g_hlr->mslookup.server.mdns.domain_suffix,
 | 
					 | 
				
			||||||
			      g_hlr->mslookup.server.mdns.running->domain_suffix));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (should_stop) {
 | 
					 | 
				
			||||||
		osmo_mslookup_server_mdns_stop(g_hlr->mslookup.server.mdns.running);
 | 
					 | 
				
			||||||
		g_hlr->mslookup.server.mdns.running = NULL;
 | 
					 | 
				
			||||||
		LOGP(DMSLOOKUP, LOGL_NOTICE, "Stopped mslookup mDNS server\n");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (should_run && !g_hlr->mslookup.server.mdns.running) {
 | 
					 | 
				
			||||||
		g_hlr->mslookup.server.mdns.running =
 | 
					 | 
				
			||||||
			osmo_mslookup_server_mdns_start(g_hlr, &g_hlr->mslookup.server.mdns.bind_addr,
 | 
					 | 
				
			||||||
							g_hlr->mslookup.server.mdns.domain_suffix);
 | 
					 | 
				
			||||||
		if (!g_hlr->mslookup.server.mdns.running)
 | 
					 | 
				
			||||||
			LOGP(DMSLOOKUP, LOGL_ERROR, "Failed to start mslookup mDNS server on " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.bind_addr));
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			LOGP(DMSLOOKUP, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.running->bind_addr));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										568
									
								
								src/proxy.c
									
									
									
									
									
								
							
							
						
						
									
										568
									
								
								src/proxy.c
									
									
									
									
									
								
							@@ -1,568 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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 <string.h>
 | 
					 | 
				
			||||||
#include <talloc.h>
 | 
					 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <inttypes.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/core/timer.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_client.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/proxy.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/remote_hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_router.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_PROXY_SUBSCR(proxy_subscr, level, fmt, args...) \
 | 
					 | 
				
			||||||
	LOGP(DDGSM, level, "(Proxy IMSI-%s MSISDN-%s HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \
 | 
					 | 
				
			||||||
	     ((proxy_subscr) && *(proxy_subscr)->imsi)? (proxy_subscr)->imsi : "?", \
 | 
					 | 
				
			||||||
	     ((proxy_subscr) && *(proxy_subscr)->msisdn)? (proxy_subscr)->msisdn : "?", \
 | 
					 | 
				
			||||||
	     OSMO_SOCKADDR_STR_FMT_ARGS((proxy_subscr)? &(proxy_subscr)->remote_hlr_addr : NULL), \
 | 
					 | 
				
			||||||
	     ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup_msg, level, fmt, args...) \
 | 
					 | 
				
			||||||
		     LOG_PROXY_SUBSCR(proxy_subscr, level, "%s: " fmt, \
 | 
					 | 
				
			||||||
				      (gsup_msg) ? osmo_gsup_message_type_name((gsup_msg)->message_type) : "NULL", \
 | 
					 | 
				
			||||||
				      ##args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The proxy subscriber database.
 | 
					 | 
				
			||||||
 * Why have a separate struct to add an llist_head entry?
 | 
					 | 
				
			||||||
 * This is to keep the option open to store the proxy data in the database instead, without any visible effect outside
 | 
					 | 
				
			||||||
 * of proxy.c. */
 | 
					 | 
				
			||||||
struct proxy_subscr_listentry {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	timestamp_t last_update;
 | 
					 | 
				
			||||||
	struct proxy_subscr data;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct proxy_pending_gsup_req {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	struct osmo_gsup_req *req;
 | 
					 | 
				
			||||||
	timestamp_t received_at;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Defer a GSUP message until we know a remote HLR to proxy to.
 | 
					 | 
				
			||||||
 * Where to send this GSUP message is indicated by its IMSI: as soon as an MS lookup has yielded the IMSI's home HLR,
 | 
					 | 
				
			||||||
 * that's where the message should go. */
 | 
					 | 
				
			||||||
static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_pending_gsup_req *m;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m = talloc_zero(proxy, struct proxy_pending_gsup_req);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(m);
 | 
					 | 
				
			||||||
	m->req = req;
 | 
					 | 
				
			||||||
	timestamp_update(&m->received_at);
 | 
					 | 
				
			||||||
	llist_add_tail(&m->entry, &proxy->pending_gsup_reqs);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) {
 | 
					 | 
				
			||||||
		/* Do not respond with an error to a CHECK_IMEI_REQUEST as osmo-msc will send a LU Reject Cause #6
 | 
					 | 
				
			||||||
		 * Just respond ACK and deal with the IMSI check that comes next. */
 | 
					 | 
				
			||||||
		if (req->gsup.message_type == OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST) {
 | 
					 | 
				
			||||||
			/* Accept all IMEIs */
 | 
					 | 
				
			||||||
			struct osmo_gsup_message gsup_reply = (struct osmo_gsup_message){
 | 
					 | 
				
			||||||
				.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
 | 
					 | 
				
			||||||
				.imei_result  = OSMO_GSUP_IMEI_RESULT_ACK,
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
			osmo_gsup_req_respond(req, &gsup_reply, false, true);
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, g_hlr->no_proxy_reject_cause,
 | 
					 | 
				
			||||||
					  "Proxy: Failed to connect to home HLR");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool proxy_deferred_gsup_req_waiting(struct proxy *proxy, const char *imsi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_pending_gsup_req *p;
 | 
					 | 
				
			||||||
	OSMO_ASSERT(imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(p, &proxy->pending_gsup_reqs, entry) {
 | 
					 | 
				
			||||||
		if (strcmp(p->req->gsup.imsi, imsi))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the remote_hlr may be passed
 | 
					 | 
				
			||||||
 * NULL. */
 | 
					 | 
				
			||||||
static void proxy_deferred_gsup_req_pop(struct proxy *proxy, const char *imsi, struct remote_hlr *remote_hlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_pending_gsup_req *p, *n;
 | 
					 | 
				
			||||||
	OSMO_ASSERT(imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry_safe(p, n, &proxy->pending_gsup_reqs, entry) {
 | 
					 | 
				
			||||||
		if (strcmp(p->req->gsup.imsi, imsi))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		proxy_pending_req_remote_hlr_connect_result(p->req, remote_hlr);
 | 
					 | 
				
			||||||
		p->req = NULL;
 | 
					 | 
				
			||||||
		llist_del(&p->entry);
 | 
					 | 
				
			||||||
		talloc_free(p);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool proxy_subscr_matches_imsi(const struct proxy_subscr *proxy_subscr, const char *imsi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!proxy_subscr || !imsi)
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	return strcmp(proxy_subscr->imsi, imsi) == 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool proxy_subscr_matches_msisdn(const struct proxy_subscr *proxy_subscr, const char *msisdn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!proxy_subscr || !msisdn)
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	return strcmp(proxy_subscr->msisdn, msisdn) == 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct proxy_subscr_listentry *_proxy_get_by_imsi(struct proxy *proxy, const char *imsi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e;
 | 
					 | 
				
			||||||
	if (!proxy)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	llist_for_each_entry(e, &proxy->subscr_list, entry) {
 | 
					 | 
				
			||||||
		if (proxy_subscr_matches_imsi(&e->data, imsi))
 | 
					 | 
				
			||||||
			return e;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct proxy_subscr_listentry *_proxy_get_by_msisdn(struct proxy *proxy, const char *msisdn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e;
 | 
					 | 
				
			||||||
	if (!proxy)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	llist_for_each_entry(e, &proxy->subscr_list, entry) {
 | 
					 | 
				
			||||||
		if (proxy_subscr_matches_msisdn(&e->data, msisdn))
 | 
					 | 
				
			||||||
			return e;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_get_by_imsi(struct proxy_subscr *dst, struct proxy *proxy, const char *imsi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, imsi);
 | 
					 | 
				
			||||||
	if (!e)
 | 
					 | 
				
			||||||
		return -ENOENT;
 | 
					 | 
				
			||||||
	*dst = e->data;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_get_by_msisdn(struct proxy_subscr *dst, struct proxy *proxy, const char *msisdn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e = _proxy_get_by_msisdn(proxy, msisdn);
 | 
					 | 
				
			||||||
	if (!e)
 | 
					 | 
				
			||||||
		return -ENOENT;
 | 
					 | 
				
			||||||
	*dst = e->data;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, proxy_subscr->imsi);
 | 
					 | 
				
			||||||
	if (!e) {
 | 
					 | 
				
			||||||
		/* Does not exist yet */
 | 
					 | 
				
			||||||
		e = talloc_zero(proxy, struct proxy_subscr_listentry);
 | 
					 | 
				
			||||||
		llist_add(&e->entry, &proxy->subscr_list);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	e->data = *proxy_subscr;
 | 
					 | 
				
			||||||
	timestamp_update(&e->last_update);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int _proxy_subscr_del(struct proxy_subscr_listentry *e)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	llist_del(&e->entry);
 | 
					 | 
				
			||||||
	talloc_free(e);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_del(struct proxy *proxy, const char *imsi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e;
 | 
					 | 
				
			||||||
	proxy_deferred_gsup_req_pop(proxy, imsi, NULL);
 | 
					 | 
				
			||||||
	e = _proxy_get_by_imsi(proxy, imsi);
 | 
					 | 
				
			||||||
	if (!e)
 | 
					 | 
				
			||||||
		return -ENOENT;
 | 
					 | 
				
			||||||
	return _proxy_subscr_del(e);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Discard stale proxy entries. */
 | 
					 | 
				
			||||||
static void proxy_cleanup(void *proxy_v)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy *proxy = proxy_v;
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e, *n;
 | 
					 | 
				
			||||||
	uint32_t age;
 | 
					 | 
				
			||||||
	llist_for_each_entry_safe(e, n, &proxy->subscr_list, entry) {
 | 
					 | 
				
			||||||
		if (!timestamp_age(&e->last_update, &age))
 | 
					 | 
				
			||||||
			LOGP(DDGSM, LOGL_ERROR, "Invalid timestamp, deleting proxy entry\n");
 | 
					 | 
				
			||||||
		else if (age <= proxy->fresh_time)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR(&e->data, LOGL_INFO, "proxy entry timed out, deleting\n");
 | 
					 | 
				
			||||||
		_proxy_subscr_del(e);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (proxy->gc_period)
 | 
					 | 
				
			||||||
		osmo_timer_schedule(&proxy->gc_timer, proxy->gc_period, 0);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_NOTICE, "Proxy cleanup is switched off (gc_period == 0)\n");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	proxy->gc_period = gc_period;
 | 
					 | 
				
			||||||
	proxy_cleanup(proxy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	OSMO_ASSERT(!gsup_server_to_vlr->proxy);
 | 
					 | 
				
			||||||
	struct proxy *proxy = talloc_zero(gsup_server_to_vlr, struct proxy);
 | 
					 | 
				
			||||||
	*proxy = (struct proxy){
 | 
					 | 
				
			||||||
		.gsup_server_to_vlr = gsup_server_to_vlr,
 | 
					 | 
				
			||||||
		.fresh_time = 60*60,
 | 
					 | 
				
			||||||
		.gc_period = 60,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	INIT_LLIST_HEAD(&proxy->subscr_list);
 | 
					 | 
				
			||||||
	INIT_LLIST_HEAD(&proxy->pending_gsup_reqs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_timer_setup(&proxy->gc_timer, proxy_cleanup, proxy);
 | 
					 | 
				
			||||||
	/* Invoke to trigger the first timer schedule */
 | 
					 | 
				
			||||||
	proxy_set_gc_period(proxy, proxy->gc_period);
 | 
					 | 
				
			||||||
	gsup_server_to_vlr->proxy = proxy;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void proxy_del(struct proxy *proxy)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	osmo_timer_del(&proxy->gc_timer);
 | 
					 | 
				
			||||||
	talloc_free(proxy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* All GSUP messages sent to the remote HLR pass through this function, to modify the subscriber state or disallow
 | 
					 | 
				
			||||||
 * sending the message. Return 0 to allow sending the message. */
 | 
					 | 
				
			||||||
static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
						const struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr_new = *proxy_subscr;
 | 
					 | 
				
			||||||
	bool ps;
 | 
					 | 
				
			||||||
	bool cs;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "Unsupported GSUP peer id type: %s\n",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_type_name(req->source_name.type));
 | 
					 | 
				
			||||||
		return -ENOTSUP;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (req->gsup.message_type) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
 | 
					 | 
				
			||||||
		/* Store the CS and PS VLR name in vlr_name_preliminary to later update the right {cs,ps} LU timestamp
 | 
					 | 
				
			||||||
		 * when receiving an OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT. Store in vlr_name_preliminary so that in
 | 
					 | 
				
			||||||
		 * case the LU fails, we keep the vlr_name intact. */
 | 
					 | 
				
			||||||
		switch (req->gsup.cn_domain) {
 | 
					 | 
				
			||||||
		case OSMO_GSUP_CN_DOMAIN_CS:
 | 
					 | 
				
			||||||
			proxy_subscr_new.cs.vlr_name_preliminary = req->source_name.ipa_name;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case OSMO_GSUP_CN_DOMAIN_PS:
 | 
					 | 
				
			||||||
			proxy_subscr_new.ps.vlr_name_preliminary = req->source_name.ipa_name;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ps = cs = false;
 | 
					 | 
				
			||||||
		if (osmo_ipa_name_cmp(&proxy_subscr_new.cs.vlr_name_preliminary, &proxy_subscr->cs.vlr_name_preliminary))
 | 
					 | 
				
			||||||
			cs = true;
 | 
					 | 
				
			||||||
		if (osmo_ipa_name_cmp(&proxy_subscr_new.ps.vlr_name_preliminary, &proxy_subscr->ps.vlr_name_preliminary))
 | 
					 | 
				
			||||||
			ps = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!(cs || ps)) {
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "VLR names remain unchanged\n");
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new);
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, rc ? LOGL_ERROR : LOGL_INFO,
 | 
					 | 
				
			||||||
				     "%s: preliminary VLR name for%s%s to %s\n",
 | 
					 | 
				
			||||||
				     rc ? "failed to update" : "updated",
 | 
					 | 
				
			||||||
				     cs ? " CS" : "", ps ? " PS" : "",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_to_str(&req->source_name));
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	/* TODO: delete proxy entry in case of a Purge Request? */
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* All GSUP messages received from the remote HLR to be sent to a local MSC pass through this function, to modify the
 | 
					 | 
				
			||||||
 * subscriber state or disallow sending the message. Return 0 to allow sending the message.
 | 
					 | 
				
			||||||
 * The local MSC shall be indicated by gsup.destination_name. */
 | 
					 | 
				
			||||||
static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
						  const struct osmo_gsup_message *gsup,
 | 
					 | 
				
			||||||
						  struct remote_hlr *from_remote_hlr,
 | 
					 | 
				
			||||||
						  const struct osmo_ipa_name *destination,
 | 
					 | 
				
			||||||
						  const struct osmo_ipa_name *via_peer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr_new = *proxy_subscr;
 | 
					 | 
				
			||||||
	bool ps;
 | 
					 | 
				
			||||||
	bool cs;
 | 
					 | 
				
			||||||
	bool vlr_name_changed_cs = false;
 | 
					 | 
				
			||||||
	bool vlr_name_changed_ps = false;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	struct osmo_ipa_name via_proxy = {};
 | 
					 | 
				
			||||||
	if (osmo_ipa_name_cmp(via_peer, destination))
 | 
					 | 
				
			||||||
		via_proxy = *via_peer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (gsup->message_type) {
 | 
					 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
 | 
					 | 
				
			||||||
		/* Remember the MSISDN of the subscriber. This does not need to be a preliminary record, because when
 | 
					 | 
				
			||||||
		 * the HLR tells us about subscriber data, it is definitive info and there is no ambiguity (like there
 | 
					 | 
				
			||||||
		 * would be with failed LU attempts from various sources). */
 | 
					 | 
				
			||||||
		if (!gsup->msisdn_enc_len)
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "No MSISDN in this Insert Data Request\n");
 | 
					 | 
				
			||||||
		else if (gsm48_decode_bcd_number2(proxy_subscr_new.msisdn, sizeof(proxy_subscr_new.msisdn),
 | 
					 | 
				
			||||||
						  gsup->msisdn_enc, gsup->msisdn_enc_len, 0))
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "Failed to decode MSISDN\n");
 | 
					 | 
				
			||||||
		else if (!osmo_msisdn_str_valid(proxy_subscr_new.msisdn))
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "invalid MSISDN: %s\n",
 | 
					 | 
				
			||||||
					     osmo_quote_str_c(OTC_SELECT, proxy_subscr_new.msisdn, -1));
 | 
					 | 
				
			||||||
		else if (!strcmp(proxy_subscr->msisdn, proxy_subscr_new.msisdn))
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "already have MSISDN = %s\n",
 | 
					 | 
				
			||||||
					     proxy_subscr_new.msisdn);
 | 
					 | 
				
			||||||
		else if (proxy_subscr_create_or_update(proxy, &proxy_subscr_new))
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "failed to update MSISDN to %s\n",
 | 
					 | 
				
			||||||
					     proxy_subscr_new.msisdn);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "stored MSISDN=%s\n",
 | 
					 | 
				
			||||||
					     proxy_subscr_new.msisdn);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
 | 
					 | 
				
			||||||
		/* Update the Location Updating timestamp */
 | 
					 | 
				
			||||||
		cs = ps = false;
 | 
					 | 
				
			||||||
		if (!osmo_ipa_name_cmp(destination, &proxy_subscr->cs.vlr_name_preliminary)) {
 | 
					 | 
				
			||||||
			timestamp_update(&proxy_subscr_new.cs.last_lu);
 | 
					 | 
				
			||||||
			proxy_subscr_new.cs.vlr_name_preliminary = (struct osmo_ipa_name){};
 | 
					 | 
				
			||||||
			vlr_name_changed_cs =
 | 
					 | 
				
			||||||
				osmo_ipa_name_cmp(&proxy_subscr->cs.vlr_name, destination)
 | 
					 | 
				
			||||||
				|| osmo_ipa_name_cmp(&proxy_subscr->cs.vlr_via_proxy, &via_proxy);
 | 
					 | 
				
			||||||
			proxy_subscr_new.cs.vlr_name = *destination;
 | 
					 | 
				
			||||||
			proxy_subscr_new.cs.vlr_via_proxy = via_proxy;
 | 
					 | 
				
			||||||
			cs = true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (!osmo_ipa_name_cmp(destination, &proxy_subscr->ps.vlr_name_preliminary)) {
 | 
					 | 
				
			||||||
			timestamp_update(&proxy_subscr_new.ps.last_lu);
 | 
					 | 
				
			||||||
			proxy_subscr_new.ps.vlr_name_preliminary = (struct osmo_ipa_name){};
 | 
					 | 
				
			||||||
			proxy_subscr_new.ps.vlr_name = *destination;
 | 
					 | 
				
			||||||
			vlr_name_changed_ps =
 | 
					 | 
				
			||||||
				osmo_ipa_name_cmp(&proxy_subscr->ps.vlr_name, destination)
 | 
					 | 
				
			||||||
				|| osmo_ipa_name_cmp(&proxy_subscr->ps.vlr_via_proxy, &via_proxy);
 | 
					 | 
				
			||||||
			proxy_subscr_new.ps.vlr_via_proxy = via_proxy;
 | 
					 | 
				
			||||||
			ps = true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (!(cs || ps)) {
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
					     "destination is neither CS nor PS VLR: %s\n",
 | 
					 | 
				
			||||||
					     osmo_ipa_name_to_str(destination));
 | 
					 | 
				
			||||||
			return GMM_CAUSE_PROTO_ERR_UNSPEC;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, rc ? LOGL_ERROR : LOGL_INFO,
 | 
					 | 
				
			||||||
				     "%s LU: timestamp for%s%s%s%s%s%s%s%s%s%s\n",
 | 
					 | 
				
			||||||
				     rc ? "failed to update" : "updated",
 | 
					 | 
				
			||||||
				     cs ? " CS" : "", ps ? " PS" : "",
 | 
					 | 
				
			||||||
				     vlr_name_changed_cs? ", CS VLR=" : "",
 | 
					 | 
				
			||||||
				     vlr_name_changed_cs? osmo_ipa_name_to_str(&proxy_subscr_new.cs.vlr_name) : "",
 | 
					 | 
				
			||||||
				     proxy_subscr_new.cs.vlr_via_proxy.len ? " via proxy " : "",
 | 
					 | 
				
			||||||
				     proxy_subscr_new.cs.vlr_via_proxy.len ?
 | 
					 | 
				
			||||||
					     osmo_ipa_name_to_str(&proxy_subscr_new.cs.vlr_via_proxy) : "",
 | 
					 | 
				
			||||||
				     vlr_name_changed_ps? ", PS VLR=" : "",
 | 
					 | 
				
			||||||
				     vlr_name_changed_ps? osmo_ipa_name_to_str(&proxy_subscr_new.ps.vlr_name) : "",
 | 
					 | 
				
			||||||
				     proxy_subscr_new.ps.vlr_via_proxy.len ? " via proxy " : "",
 | 
					 | 
				
			||||||
				     proxy_subscr_new.ps.vlr_via_proxy.len ?
 | 
					 | 
				
			||||||
					     osmo_ipa_name_to_str(&proxy_subscr_new.ps.vlr_via_proxy) : ""
 | 
					 | 
				
			||||||
				    );
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void proxy_remote_hlr_connect_result_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr,
 | 
					 | 
				
			||||||
					       void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy *proxy = data;
 | 
					 | 
				
			||||||
	struct proxy_subscr_listentry *e;
 | 
					 | 
				
			||||||
	if (!proxy)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	llist_for_each_entry(e, &proxy->subscr_list, entry) {
 | 
					 | 
				
			||||||
		if (!osmo_sockaddr_str_cmp(addr, &e->data.remote_hlr_addr)) {
 | 
					 | 
				
			||||||
			proxy_deferred_gsup_req_pop(proxy, e->data.imsi, remote_hlr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Store the remote HLR's GSUP address for this proxy subscriber.
 | 
					 | 
				
			||||||
 * This can be set before the remote_hlr is connected, or after.
 | 
					 | 
				
			||||||
 * And, this can be set before the gsup_req has been queued for this HLR, or after.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				      const struct osmo_sockaddr_str *remote_hlr_addr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr_new;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
 | 
					 | 
				
			||||||
		if (!osmo_sockaddr_str_cmp(remote_hlr_addr, &proxy_subscr->remote_hlr_addr)) {
 | 
					 | 
				
			||||||
			/* Already have this remote address */
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			LOG_PROXY_SUBSCR(proxy_subscr, LOGL_NOTICE,
 | 
					 | 
				
			||||||
					 "Remote HLR address changes to " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
					 OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Store the address. Make a copy to modify. */
 | 
					 | 
				
			||||||
	proxy_subscr_new = *proxy_subscr;
 | 
					 | 
				
			||||||
	proxy_subscr = &proxy_subscr_new;
 | 
					 | 
				
			||||||
	proxy_subscr_new.remote_hlr_addr = *remote_hlr_addr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (proxy_subscr_create_or_update(proxy, proxy_subscr)) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR(proxy_subscr, LOGL_ERROR, "Failed to store proxy entry for remote HLR\n");
 | 
					 | 
				
			||||||
		/* If no remote HLR is known for the IMSI, the proxy entry is pointless. */
 | 
					 | 
				
			||||||
		proxy_subscr_del(proxy, proxy_subscr->imsi);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	LOG_PROXY_SUBSCR(proxy_subscr, LOGL_DEBUG, "Remote HLR resolved, stored address\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If any messages for this HLR are already spooled, connect now. Otherwise wait for
 | 
					 | 
				
			||||||
	 * proxy_subscr_forward_to_remote_hlr() to connect then. */
 | 
					 | 
				
			||||||
	if (proxy_deferred_gsup_req_waiting(proxy, proxy_subscr->imsi))
 | 
					 | 
				
			||||||
		remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true,
 | 
					 | 
				
			||||||
					  proxy_remote_hlr_connect_result_cb, proxy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct remote_hlr *remote_hlr;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = proxy_acknowledge_gsup_to_remote_hlr(proxy, proxy_subscr, req);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC, "Proxy does not allow this message");
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
 | 
					 | 
				
			||||||
		/* We don't know the remote target yet. Still waiting for an MS lookup response, which will end up
 | 
					 | 
				
			||||||
		 * calling proxy_subscr_remote_hlr_resolved(). See dgsm.c. */
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "deferring until remote HLR is known\n");
 | 
					 | 
				
			||||||
		proxy_deferred_gsup_req_add(proxy, req);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_to_str(&req->source_name),
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_to_str(&req->via_proxy));
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n",
 | 
					 | 
				
			||||||
				     osmo_cni_peer_id_to_str(&req->source_name));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* We could always store in the defer queue and empty the queue if the connection is already up.
 | 
					 | 
				
			||||||
	 * Slight optimisation: if the remote_hlr is already up and running, skip the defer queue.
 | 
					 | 
				
			||||||
	 * First ask for an existing remote_hlr. */
 | 
					 | 
				
			||||||
	remote_hlr = remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, false, NULL, NULL);
 | 
					 | 
				
			||||||
	if (remote_hlr && remote_hlr_is_up(remote_hlr)) {
 | 
					 | 
				
			||||||
		proxy_pending_req_remote_hlr_connect_result(req, remote_hlr);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Not existing or not up. Defer req and ask to be notified when it is up.
 | 
					 | 
				
			||||||
	 * If the remote_hlr exists but is not connected yet, there should actually already be a pending
 | 
					 | 
				
			||||||
	 * proxy_remote_hlr_connect_result_cb queued, but it doesn't hurt to do that more often. */
 | 
					 | 
				
			||||||
	proxy_deferred_gsup_req_add(proxy, req);
 | 
					 | 
				
			||||||
	remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true,
 | 
					 | 
				
			||||||
				  proxy_remote_hlr_connect_result_cb, proxy);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
 | 
					 | 
				
			||||||
				const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_ipa_name destination;
 | 
					 | 
				
			||||||
	struct osmo_gsup_conn *vlr_conn;
 | 
					 | 
				
			||||||
	struct msgb *msg;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_ipa_name_set(&destination, gsup->destination_name, gsup->destination_name_len)) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "no valid Destination Name IE, cannot route to VLR.\n");
 | 
					 | 
				
			||||||
		return GMM_CAUSE_INV_MAND_INFO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Route to MSC/SGSN that we're proxying for */
 | 
					 | 
				
			||||||
	vlr_conn = gsup_route_find_by_ipa_name(proxy->gsup_server_to_vlr, &destination);
 | 
					 | 
				
			||||||
	if (!vlr_conn) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "Destination VLR unreachable: %s\n", osmo_ipa_name_to_str(&destination));
 | 
					 | 
				
			||||||
		return GMM_CAUSE_MSC_TEMP_NOTREACH;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (proxy_acknowledge_gsup_from_remote_hlr(proxy, proxy_subscr, gsup, from_remote_hlr, &destination,
 | 
					 | 
				
			||||||
						   &vlr_conn->peer_name)) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "Proxy does not allow forwarding this message\n");
 | 
					 | 
				
			||||||
		return GMM_CAUSE_PROTO_ERR_UNSPEC;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg = osmo_gsup_msgb_alloc("GSUP proxy to VLR");
 | 
					 | 
				
			||||||
	if (osmo_gsup_encode(msg, gsup)) {
 | 
					 | 
				
			||||||
		LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
 | 
					 | 
				
			||||||
				     "Failed to re-encode GSUP message, cannot forward\n");
 | 
					 | 
				
			||||||
		return GMM_CAUSE_INV_MAND_INFO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "VLR<-HLR: forwarding to %s%s%s\n",
 | 
					 | 
				
			||||||
			     osmo_ipa_name_to_str(&destination),
 | 
					 | 
				
			||||||
			     osmo_ipa_name_cmp(&destination, &vlr_conn->peer_name) ? " via " : "",
 | 
					 | 
				
			||||||
			     osmo_ipa_name_cmp(&destination, &vlr_conn->peer_name) ?
 | 
					 | 
				
			||||||
						       osmo_ipa_name_to_str(&vlr_conn->peer_name) : "");
 | 
					 | 
				
			||||||
	return osmo_gsup_conn_send(vlr_conn, msg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										252
									
								
								src/remote_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										252
									
								
								src/remote_hlr.c
									
									
									
									
									
								
							@@ -1,252 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/protocol/gsm_04_08_gprs.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsm/gsm23003.h>
 | 
					 | 
				
			||||||
#include <osmocom/abis/ipa.h>
 | 
					 | 
				
			||||||
#include <osmocom/gsupclient/gsup_client.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_server.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/gsup_router.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/remote_hlr.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/proxy.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static LLIST_HEAD(remote_hlrs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void remote_hlr_err_reply(struct remote_hlr *rh, const struct osmo_gsup_message *gsup_orig,
 | 
					 | 
				
			||||||
				 enum gsm48_gmm_cause cause)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_gsup_message gsup_reply;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
 | 
					 | 
				
			||||||
	if (!OSMO_GSUP_IS_MSGT_REQUEST(gsup_orig->message_type))
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gsup_reply = (struct osmo_gsup_message){
 | 
					 | 
				
			||||||
		.cause = cause,
 | 
					 | 
				
			||||||
		.message_type = OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type),
 | 
					 | 
				
			||||||
		.message_class = gsup_orig->message_class,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* RP-Message-Reference is mandatory for SM Service */
 | 
					 | 
				
			||||||
		.sm_rp_mr = gsup_orig->sm_rp_mr,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(gsup_reply.imsi, gsup_orig->imsi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* For SS/USSD, it's important to keep both session state and ID IEs */
 | 
					 | 
				
			||||||
	if (gsup_orig->session_state != OSMO_GSUP_SESSION_STATE_NONE) {
 | 
					 | 
				
			||||||
		gsup_reply.session_state = OSMO_GSUP_SESSION_STATE_END;
 | 
					 | 
				
			||||||
		gsup_reply.session_id = gsup_orig->session_id;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (osmo_gsup_client_enc_send(rh->gsupc, &gsup_reply))
 | 
					 | 
				
			||||||
		LOGP(DLGSUP, LOGL_ERROR, "Failed to send Error reply (imsi=%s)\n",
 | 
					 | 
				
			||||||
		     osmo_quote_str(gsup_orig->imsi, -1));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* We are receiving a GSUP message from a remote HLR to go back to a local MSC.
 | 
					 | 
				
			||||||
 * The local MSC shall be indicated by gsup.destination_name. */
 | 
					 | 
				
			||||||
static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct remote_hlr *rh = gsupc->data;
 | 
					 | 
				
			||||||
	struct proxy_subscr proxy_subscr;
 | 
					 | 
				
			||||||
	struct osmo_gsup_message gsup;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
 | 
					 | 
				
			||||||
	if (rc < 0) {
 | 
					 | 
				
			||||||
		LOG_REMOTE_HLR(rh, LOGL_ERROR, "Failed to decode GSUP message: '%s' (%d) [ %s]\n",
 | 
					 | 
				
			||||||
			       get_value_string(gsm48_gmm_cause_names, -rc),
 | 
					 | 
				
			||||||
			       -rc, osmo_hexdump(msg->data, msg->len));
 | 
					 | 
				
			||||||
		return rc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!osmo_imsi_str_valid(gsup.imsi)) {
 | 
					 | 
				
			||||||
		LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Invalid IMSI\n");
 | 
					 | 
				
			||||||
		remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_INV_MAND_INFO);
 | 
					 | 
				
			||||||
		return -GMM_CAUSE_INV_MAND_INFO;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, gsup.imsi)) {
 | 
					 | 
				
			||||||
		LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "No proxy entry for this IMSI\n");
 | 
					 | 
				
			||||||
		remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
 | 
					 | 
				
			||||||
		return -GMM_CAUSE_NET_FAIL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = proxy_subscr_forward_to_vlr(g_hlr->gs->proxy, &proxy_subscr, &gsup, rh);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Failed to forward GSUP message towards VLR\n");
 | 
					 | 
				
			||||||
		remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
 | 
					 | 
				
			||||||
		return -GMM_CAUSE_NET_FAIL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct remote_hlr_pending_up {
 | 
					 | 
				
			||||||
	struct llist_head entry;
 | 
					 | 
				
			||||||
	remote_hlr_connect_result_cb_t connect_result_cb;
 | 
					 | 
				
			||||||
	void *data;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct remote_hlr *remote_hlr = gsupc->data;
 | 
					 | 
				
			||||||
	struct remote_hlr_pending_up *p, *n;
 | 
					 | 
				
			||||||
	if (!up) {
 | 
					 | 
				
			||||||
		LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link to remote HLR is down, removing GSUP client\n");
 | 
					 | 
				
			||||||
		remote_hlr_destroy(remote_hlr);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link up\n");
 | 
					 | 
				
			||||||
	llist_for_each_entry_safe(p, n, &remote_hlr->pending_up_callbacks, entry) {
 | 
					 | 
				
			||||||
		if (p->connect_result_cb)
 | 
					 | 
				
			||||||
			p->connect_result_cb(&remote_hlr->addr, remote_hlr, p->data);
 | 
					 | 
				
			||||||
		llist_del(&p->entry);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool remote_hlr_is_up(struct remote_hlr *remote_hlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return remote_hlr && remote_hlr->gsupc && remote_hlr->gsupc->is_connected;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect,
 | 
					 | 
				
			||||||
					     remote_hlr_connect_result_cb_t connect_result_cb, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct remote_hlr *rh = NULL;
 | 
					 | 
				
			||||||
	struct remote_hlr *rh_i;
 | 
					 | 
				
			||||||
	struct osmo_gsup_client_config cfg;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	llist_for_each_entry(rh_i, &remote_hlrs, entry) {
 | 
					 | 
				
			||||||
		if (!osmo_sockaddr_str_cmp(&rh_i->addr, addr)) {
 | 
					 | 
				
			||||||
			rh = rh_i;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rh)
 | 
					 | 
				
			||||||
		goto add_result_cb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!connect) {
 | 
					 | 
				
			||||||
		if (connect_result_cb)
 | 
					 | 
				
			||||||
			connect_result_cb(addr, NULL, data);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Doesn't exist yet, create a GSUP client to remote HLR. */
 | 
					 | 
				
			||||||
	cfg = (struct osmo_gsup_client_config){
 | 
					 | 
				
			||||||
		.ipa_dev = &g_hlr->gsup_unit_name,
 | 
					 | 
				
			||||||
		.ip_addr = addr->ip,
 | 
					 | 
				
			||||||
		.tcp_port = addr->port,
 | 
					 | 
				
			||||||
		.oapc_config = NULL,
 | 
					 | 
				
			||||||
		.read_cb = remote_hlr_rx,
 | 
					 | 
				
			||||||
		.up_down_cb = remote_hlr_up_down,
 | 
					 | 
				
			||||||
		.data = rh,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	rh = talloc_zero(dgsm_ctx, struct remote_hlr);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(rh);
 | 
					 | 
				
			||||||
	*rh = (struct remote_hlr){
 | 
					 | 
				
			||||||
		.addr = *addr,
 | 
					 | 
				
			||||||
		.gsupc = osmo_gsup_client_create3(rh, &cfg),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	INIT_LLIST_HEAD(&rh->pending_up_callbacks);
 | 
					 | 
				
			||||||
	if (!rh->gsupc) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_ERROR,
 | 
					 | 
				
			||||||
		     "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(addr));
 | 
					 | 
				
			||||||
		talloc_free(rh);
 | 
					 | 
				
			||||||
		if (connect_result_cb)
 | 
					 | 
				
			||||||
			connect_result_cb(addr, NULL, data);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rh->gsupc->data = rh;
 | 
					 | 
				
			||||||
	llist_add(&rh->entry, &remote_hlrs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_result_cb:
 | 
					 | 
				
			||||||
	if (connect_result_cb) {
 | 
					 | 
				
			||||||
		if (remote_hlr_is_up(rh)) {
 | 
					 | 
				
			||||||
			connect_result_cb(addr, rh, data);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			struct remote_hlr_pending_up *p;
 | 
					 | 
				
			||||||
			p = talloc_zero(rh, struct remote_hlr_pending_up);
 | 
					 | 
				
			||||||
			OSMO_ASSERT(p);
 | 
					 | 
				
			||||||
			p->connect_result_cb = connect_result_cb;
 | 
					 | 
				
			||||||
			p->data = data;
 | 
					 | 
				
			||||||
			llist_add_tail(&p->entry, &rh->pending_up_callbacks);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rh;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void remote_hlr_destroy(struct remote_hlr *remote_hlr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	osmo_gsup_client_destroy(remote_hlr->gsupc);
 | 
					 | 
				
			||||||
	remote_hlr->gsupc = NULL;
 | 
					 | 
				
			||||||
	llist_del(&remote_hlr->entry);
 | 
					 | 
				
			||||||
	talloc_free(remote_hlr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* This function takes ownership of the msg, do not free it after passing to this function. */
 | 
					 | 
				
			||||||
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc = osmo_gsup_client_send(remote_hlr->gsupc, msg);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		LOGP(DDGSM, LOGL_ERROR, "Failed to send GSUP message to " OSMO_SOCKADDR_STR_FMT "\n",
 | 
					 | 
				
			||||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* A GSUP message was received from the MS/MSC side, forward it to the remote HLR. */
 | 
					 | 
				
			||||||
void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req,
 | 
					 | 
				
			||||||
					   struct osmo_gsup_message *modified_gsup)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	struct msgb *msg;
 | 
					 | 
				
			||||||
	/* To forward to a remote HLR, we need to indicate the source MSC's name in the Source Name IE to make sure the
 | 
					 | 
				
			||||||
	 * reply can be routed back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return
 | 
					 | 
				
			||||||
	 * this as gsup->destination_name so that the reply gets routed to the original MSC. */
 | 
					 | 
				
			||||||
	struct osmo_gsup_message forward;
 | 
					 | 
				
			||||||
	if (modified_gsup)
 | 
					 | 
				
			||||||
		forward = *modified_gsup;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		forward = req->gsup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
 | 
					 | 
				
			||||||
					  osmo_cni_peer_id_type_name(req->source_name.type));
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	forward.source_name = req->source_name.ipa_name.val;
 | 
					 | 
				
			||||||
	forward.source_name_len = req->source_name.ipa_name.len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg = osmo_gsup_msgb_alloc("GSUP proxy to remote HLR");
 | 
					 | 
				
			||||||
	rc = osmo_gsup_encode(msg, &forward);
 | 
					 | 
				
			||||||
	if (rc) {
 | 
					 | 
				
			||||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to encode GSUP message for forwarding");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	remote_hlr_msgb_send(remote_hlr, msg);
 | 
					 | 
				
			||||||
	osmo_gsup_req_free(req);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,53 +0,0 @@
 | 
				
			|||||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 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/core/timer.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/timestamp.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Central implementation to set a timestamp to the current time, in case we want to modify this in the future. */
 | 
					 | 
				
			||||||
void timestamp_update(timestamp_t *timestamp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timeval tv;
 | 
					 | 
				
			||||||
	time_t raw;
 | 
					 | 
				
			||||||
	struct tm utc;
 | 
					 | 
				
			||||||
	/* The simpler way would be just time(&raw), but by using osmo_gettimeofday() we can also use
 | 
					 | 
				
			||||||
	 * osmo_gettimeofday_override for unit tests independent from real time. */
 | 
					 | 
				
			||||||
	osmo_gettimeofday(&tv, NULL);
 | 
					 | 
				
			||||||
	raw = tv.tv_sec;
 | 
					 | 
				
			||||||
	gmtime_r(&raw, &utc);
 | 
					 | 
				
			||||||
	*timestamp = mktime(&utc);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Calculate seconds since a given timestamp was taken. Return true for a valid age returned in age_p, return false if
 | 
					 | 
				
			||||||
 * the timestamp is either in the future or the age surpasses uint32_t range. When false is returned, *age_p is set to
 | 
					 | 
				
			||||||
 * UINT32_MAX. */
 | 
					 | 
				
			||||||
bool timestamp_age(const timestamp_t *timestamp, uint32_t *age_p)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int64_t age64;
 | 
					 | 
				
			||||||
	timestamp_t now;
 | 
					 | 
				
			||||||
	timestamp_update(&now);
 | 
					 | 
				
			||||||
	age64 = (int64_t)now - (int64_t)(*timestamp);
 | 
					 | 
				
			||||||
	if (age64 < 0 || age64 > UINT32_MAX) {
 | 
					 | 
				
			||||||
		*age_p = UINT32_MAX;
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	*age_p = (uint32_t)age64;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
SUBDIRS = \
 | 
					SUBDIRS = \
 | 
				
			||||||
	auc \
 | 
						auc \
 | 
				
			||||||
 | 
						gsup_server \
 | 
				
			||||||
	db \
 | 
						db \
 | 
				
			||||||
	gsup \
 | 
					 | 
				
			||||||
	db_upgrade \
 | 
						db_upgrade \
 | 
				
			||||||
	mslookup \
 | 
						mslookup \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
@@ -69,9 +69,6 @@ vty-test:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
CTRL_TEST_DB = hlr_ctrl_test.db
 | 
					CTRL_TEST_DB = hlr_ctrl_test.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Run a specific test with: 'make ctrl-test CTRL_TEST=test_subscriber.ctrl'
 | 
					 | 
				
			||||||
CTRL_TEST ?= *.ctrl
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# To update the CTRL script from current application behavior,
 | 
					# To update the CTRL script from current application behavior,
 | 
				
			||||||
# pass -u to ctrl_script_runner.py by doing:
 | 
					# pass -u to ctrl_script_runner.py by doing:
 | 
				
			||||||
#   make ctrl-test U=-u
 | 
					#   make ctrl-test U=-u
 | 
				
			||||||
@@ -82,7 +79,7 @@ ctrl-test:
 | 
				
			|||||||
	osmo_verify_transcript_ctrl.py -v \
 | 
						osmo_verify_transcript_ctrl.py -v \
 | 
				
			||||||
		-p 4259 \
 | 
							-p 4259 \
 | 
				
			||||||
		-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \
 | 
							-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \
 | 
				
			||||||
		$(U) $(srcdir)/$(CTRL_TEST)
 | 
							$(U) $(srcdir)/*.ctrl
 | 
				
			||||||
	-rm -f $(CTRL_TEST_DB)
 | 
						-rm -f $(CTRL_TEST_DB)
 | 
				
			||||||
	-rm $(CTRL_TEST_DB)-*
 | 
						-rm $(CTRL_TEST_DB)-*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,9 @@ EXTRA_DIST = \
 | 
				
			|||||||
	auc_ts_55_205_test_sets.err \
 | 
						auc_ts_55_205_test_sets.err \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
check_PROGRAMS = auc_test auc_ts_55_205_test_sets
 | 
					check_PROGRAMS = auc_ts_55_205_test_sets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_PROGRAMS = auc_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
auc_test_SOURCES = \
 | 
					auc_test_SOURCES = \
 | 
				
			||||||
	auc_test.c \
 | 
						auc_test.c \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,7 +113,6 @@ int rand_get(uint8_t *rand, unsigned int len)
 | 
				
			|||||||
	return len;
 | 
						return len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Subscriber with 2G-only (COMP128v1) authentication data */
 | 
					 | 
				
			||||||
static void test_gen_vectors_2g_only(void)
 | 
					static void test_gen_vectors_2g_only(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
						struct osmo_sub_auth_data aud2g;
 | 
				
			||||||
@@ -175,8 +174,6 @@ static void test_gen_vectors_2g_only(void)
 | 
				
			|||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Subscriber with separate 2G (COMP128v1) and 3G (MILENAGE) authentication data,
 | 
					 | 
				
			||||||
 * reflects the default configuration of sysmoUSIM-SJS1 */
 | 
					 | 
				
			||||||
static void test_gen_vectors_2g_plus_3g(void)
 | 
					static void test_gen_vectors_2g_plus_3g(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
						struct osmo_sub_auth_data aud2g;
 | 
				
			||||||
@@ -287,9 +284,6 @@ void _test_gen_vectors_3g_only__expect_vecs(struct osmo_auth_vector vecs[3])
 | 
				
			|||||||
	      );
 | 
						      );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Subscriber with only 3G (MILENAGE) authentication data,
 | 
					 | 
				
			||||||
 * reflects the default configuration of sysmoISIM-SJA2. Resulting
 | 
					 | 
				
			||||||
 * tuples are suitable for both 2G and 3G authentication */
 | 
					 | 
				
			||||||
static void test_gen_vectors_3g_only(void)
 | 
					static void test_gen_vectors_3g_only(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
						struct osmo_sub_auth_data aud2g;
 | 
				
			||||||
@@ -460,56 +454,7 @@ static void test_gen_vectors_3g_only(void)
 | 
				
			|||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Subscriber with only 3G (XOR) authentication data,
 | 
					void test_gen_vectors_bad_args()
 | 
				
			||||||
 * reflects the default configuration of sysmoTSIM-SJAx as well
 | 
					 | 
				
			||||||
 * as many "Test USIM" cards. Resulting tuples are suitable for both
 | 
					 | 
				
			||||||
 * 2G and 3G authentication */
 | 
					 | 
				
			||||||
static void test_gen_vectors_3g_xor(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
					 | 
				
			||||||
	struct osmo_sub_auth_data aud3g;
 | 
					 | 
				
			||||||
	struct osmo_auth_vector vec;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	comment_start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	aud2g = (struct osmo_sub_auth_data){ 0 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	aud3g = (struct osmo_sub_auth_data){
 | 
					 | 
				
			||||||
		.type = OSMO_AUTH_TYPE_UMTS,
 | 
					 | 
				
			||||||
		.algo = OSMO_AUTH_ALG_XOR,
 | 
					 | 
				
			||||||
		.u.umts.sqn = 0,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	osmo_hexparse("000102030405060708090a0b0c0d0e0f",
 | 
					 | 
				
			||||||
		      aud3g.u.umts.k, sizeof(aud3g.u.umts.k));
 | 
					 | 
				
			||||||
	osmo_hexparse("00000000000000000000000000000000",
 | 
					 | 
				
			||||||
		      aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc));
 | 
					 | 
				
			||||||
	next_rand("b5039c57e4a75051551d1a390a71ce48", true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vec = (struct osmo_auth_vector){ {0} };
 | 
					 | 
				
			||||||
	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
 | 
					 | 
				
			||||||
	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL);
 | 
					 | 
				
			||||||
	VERBOSE_ASSERT(rc, == 1, "%d");
 | 
					 | 
				
			||||||
	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VEC_IS(&vec,
 | 
					 | 
				
			||||||
	       "  rand: b5039c57e4a75051551d1a390a71ce48\n"
 | 
					 | 
				
			||||||
	       "  autn: 54e0a256565d0000b5029e54e0a25656\n"
 | 
					 | 
				
			||||||
	       "  ck: 029e54e0a256565d141032067cc047b5\n"
 | 
					 | 
				
			||||||
	       "  ik: 9e54e0a256565d141032067cc047b502\n"
 | 
					 | 
				
			||||||
	       "  res: b5029e54e0a256565d141032067cc047\n"
 | 
					 | 
				
			||||||
	       "  res_len: 10\n"
 | 
					 | 
				
			||||||
	       "  kc: 98e880384887f9fe\n"
 | 
					 | 
				
			||||||
	       "  sres: 0ec81877\n"
 | 
					 | 
				
			||||||
	       "  auth_types: 03000000\n"
 | 
					 | 
				
			||||||
	      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	comment_end();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Test a variety of invalid authentication data combinations */
 | 
					 | 
				
			||||||
void test_gen_vectors_bad_args(void)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_auth_vector vec;
 | 
						struct osmo_auth_vector vec;
 | 
				
			||||||
	uint8_t auts[14];
 | 
						uint8_t auts[14];
 | 
				
			||||||
@@ -668,20 +613,15 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
 | 
						void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_init_logging2(tall_ctx, &hlr_log_info);
 | 
						osmo_init_logging2(tall_ctx, &hlr_log_info);
 | 
				
			||||||
	log_set_print_filename2(osmo_stderr_target,
 | 
						log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
 | 
				
			||||||
				cmdline_opts.verbose ?
 | 
					 | 
				
			||||||
					LOG_FILENAME_BASENAME :
 | 
					 | 
				
			||||||
					LOG_FILENAME_NONE);
 | 
					 | 
				
			||||||
	log_set_print_timestamp(osmo_stderr_target, 0);
 | 
						log_set_print_timestamp(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
						log_set_use_color(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
					 | 
				
			||||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
						log_set_print_category(osmo_stderr_target, 1);
 | 
				
			||||||
	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
						log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test_gen_vectors_2g_only();
 | 
						test_gen_vectors_2g_only();
 | 
				
			||||||
	test_gen_vectors_2g_plus_3g();
 | 
						test_gen_vectors_2g_plus_3g();
 | 
				
			||||||
	test_gen_vectors_3g_only();
 | 
						test_gen_vectors_3g_only();
 | 
				
			||||||
	test_gen_vectors_3g_xor();
 | 
					 | 
				
			||||||
	test_gen_vectors_bad_args();
 | 
						test_gen_vectors_bad_args();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("Done\n");
 | 
						printf("Done\n");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -217,29 +217,6 @@ DAUC vector [2]: auth_types = 0x3
 | 
				
			|||||||
===== test_gen_vectors_3g_only: SUCCESS
 | 
					===== test_gen_vectors_3g_only: SUCCESS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
===== test_gen_vectors_3g_xor
 | 
					 | 
				
			||||||
aud3g.u.umts.sqn == 0
 | 
					 | 
				
			||||||
DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys)
 | 
					 | 
				
			||||||
DAUC 3G: k = 000102030405060708090a0b0c0d0e0f
 | 
					 | 
				
			||||||
DAUC 3G: opc = 00000000000000000000000000000000
 | 
					 | 
				
			||||||
DAUC 3G: for sqn ind 0, previous sqn was 0
 | 
					 | 
				
			||||||
DAUC vector [0]: rand = b5039c57e4a75051551d1a390a71ce48
 | 
					 | 
				
			||||||
DAUC vector [0]: sqn = 0
 | 
					 | 
				
			||||||
DAUC vector [0]: autn = 54e0a256565d0000b5029e54e0a25656
 | 
					 | 
				
			||||||
DAUC vector [0]: ck = 029e54e0a256565d141032067cc047b5
 | 
					 | 
				
			||||||
DAUC vector [0]: ik = 9e54e0a256565d141032067cc047b502
 | 
					 | 
				
			||||||
DAUC vector [0]: res = b5029e54e0a256565d141032067cc047
 | 
					 | 
				
			||||||
DAUC vector [0]: res_len = 16
 | 
					 | 
				
			||||||
DAUC vector [0]: deriving 2G from 3G
 | 
					 | 
				
			||||||
DAUC vector [0]: kc = 98e880384887f9fe
 | 
					 | 
				
			||||||
DAUC vector [0]: sres = 0ec81877
 | 
					 | 
				
			||||||
DAUC vector [0]: auth_types = 0x3
 | 
					 | 
				
			||||||
rc == 1
 | 
					 | 
				
			||||||
aud3g.u.umts.sqn == 0
 | 
					 | 
				
			||||||
vector matches expectations
 | 
					 | 
				
			||||||
===== test_gen_vectors_3g_xor: SUCCESS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
===== test_gen_vectors_bad_args
 | 
					===== test_gen_vectors_bad_args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- no auth data (a)
 | 
					- no auth data (a)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,16 +100,15 @@ int rand_get(uint8_t *rand, unsigned int len)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FUNCTIONS
 | 
					FUNCTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char **argv)
 | 
					int main()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	printf("3GPP TS 55.205 Test Sets\n");
 | 
						printf("3GPP TS 55.205 Test Sets\n");
 | 
				
			||||||
	void *tall_ctx = talloc_named_const(NULL, 1, "test");
 | 
						void *tall_ctx = talloc_named_const(NULL, 1, "test");
 | 
				
			||||||
	msgb_talloc_ctx_init(tall_ctx, 0);
 | 
						msgb_talloc_ctx_init(tall_ctx, 0);
 | 
				
			||||||
	osmo_init_logging2(tall_ctx, &hlr_log_info);
 | 
						osmo_init_logging2(tall_ctx, &hlr_log_info);
 | 
				
			||||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
						log_set_print_filename(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_print_timestamp(osmo_stderr_target, 0);
 | 
						log_set_print_timestamp(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
						log_set_use_color(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
					 | 
				
			||||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
						log_set_print_category(osmo_stderr_target, 1);
 | 
				
			||||||
	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
						log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,6 @@ db_test_LDADD = \
 | 
				
			|||||||
	$(top_builddir)/src/db_auc.o \
 | 
						$(top_builddir)/src/db_auc.o \
 | 
				
			||||||
	$(top_builddir)/src/db_hlr.o \
 | 
						$(top_builddir)/src/db_hlr.o \
 | 
				
			||||||
	$(top_builddir)/src/db.o \
 | 
						$(top_builddir)/src/db.o \
 | 
				
			||||||
	$(top_builddir)/src/cni_peer_id.o \
 | 
					 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
	$(LIBOSMOGSM_LIBS) \
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
	$(LIBOSMOABIS_LIBS) \
 | 
						$(LIBOSMOABIS_LIBS) \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,6 @@
 | 
				
			|||||||
#include <osmocom/core/utils.h>
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
					 | 
				
			||||||
#include <osmocom/hlr/db.h>
 | 
					#include <osmocom/hlr/db.h>
 | 
				
			||||||
#include <osmocom/hlr/logging.h>
 | 
					#include <osmocom/hlr/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,8 +145,6 @@ void dump_subscr(struct hlr_subscriber *subscr)
 | 
				
			|||||||
#define Ps(name) \
 | 
					#define Ps(name) \
 | 
				
			||||||
	if (*subscr->name) \
 | 
						if (*subscr->name) \
 | 
				
			||||||
		Pfo(name, "'%s'", subscr)
 | 
							Pfo(name, "'%s'", subscr)
 | 
				
			||||||
#define Pgt(name) \
 | 
					 | 
				
			||||||
	Pfv(name, "%s", osmo_ipa_name_to_str(&subscr->name))
 | 
					 | 
				
			||||||
#define Pd(name) \
 | 
					#define Pd(name) \
 | 
				
			||||||
	Pfv(name, "%"PRId64, (int64_t)subscr->name)
 | 
						Pfv(name, "%"PRId64, (int64_t)subscr->name)
 | 
				
			||||||
#define Pd_nonzero(name) \
 | 
					#define Pd_nonzero(name) \
 | 
				
			||||||
@@ -238,15 +235,7 @@ static const char *imsi2 = "123456789000002";
 | 
				
			|||||||
static const char *short_imsi = "123456";
 | 
					static const char *short_imsi = "123456";
 | 
				
			||||||
static const char *unknown_imsi = "999999999";
 | 
					static const char *unknown_imsi = "999999999";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
 | 
					static void test_subscr_create_update_sel_delete()
 | 
				
			||||||
			    const char *vlr_or_sgsn_number, bool is_ps)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct osmo_ipa_name vlr_nr;
 | 
					 | 
				
			||||||
	osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number);
 | 
					 | 
				
			||||||
	return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void test_subscr_create_update_sel_delete(void)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t id0, id1, id2, id_short;
 | 
						int64_t id0, id1, id2, id_short;
 | 
				
			||||||
	comment_start();
 | 
						comment_start();
 | 
				
			||||||
@@ -262,13 +251,13 @@ static void test_subscr_create_update_sel_delete(void)
 | 
				
			|||||||
	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi2, 0);
 | 
						ASSERT_SEL(imsi, imsi2, 0);
 | 
				
			||||||
	id2 = g_subscr.id;
 | 
						id2 = g_subscr.id;
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi1, 0);
 | 
						ASSERT_SEL(imsi, imsi1, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi2, 0);
 | 
						ASSERT_SEL(imsi, imsi2, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
						ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
				
			||||||
@@ -397,39 +386,39 @@ static void test_subscr_create_update_sel_delete(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "5952", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "712", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
						comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for non-existent ID");
 | 
						comment("Record LU for non-existent ID");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, 99999, "5952", true), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu_str(dbc, 99999, "712", false), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT);
 | 
				
			||||||
	ASSERT_SEL(id, 99999, -ENOENT);
 | 
						ASSERT_SEL(id, 99999, -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Purge and un-purge PS and CS");
 | 
						comment("Purge and un-purge PS and CS");
 | 
				
			||||||
@@ -541,7 +530,7 @@ static const struct sub_auth_data_str *mk_aud_3g(enum osmo_auth_algo algo,
 | 
				
			|||||||
	return &aud;
 | 
						return &aud;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_subscr_aud(void)
 | 
					static void test_subscr_aud()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t id;
 | 
						int64_t id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -783,7 +772,7 @@ static void test_subscr_aud(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the
 | 
					/* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the
 | 
				
			||||||
 * table structure. */
 | 
					 * table structure. */
 | 
				
			||||||
static void test_subscr_aud_invalid_len(void)
 | 
					static void test_subscr_aud_invalid_len()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t id;
 | 
						int64_t id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -845,7 +834,7 @@ static void test_subscr_aud_invalid_len(void)
 | 
				
			|||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_subscr_sqn(void)
 | 
					static void test_subscr_sqn()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t id;
 | 
						int64_t id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -918,50 +907,6 @@ static void test_subscr_sqn(void)
 | 
				
			|||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_ind(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	comment_start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ASSERT_IND(VLR, IND) do { \
 | 
					 | 
				
			||||||
		unsigned int ind; \
 | 
					 | 
				
			||||||
		struct osmo_cni_peer_id vlr; \
 | 
					 | 
				
			||||||
		OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
 | 
					 | 
				
			||||||
		ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \
 | 
					 | 
				
			||||||
		fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \
 | 
					 | 
				
			||||||
		if (ind != (IND)) \
 | 
					 | 
				
			||||||
			fprintf(stderr, "  ERROR: expected " #IND "\n"); \
 | 
					 | 
				
			||||||
	} while (0)
 | 
					 | 
				
			||||||
#define IND_DEL(VLR) do { \
 | 
					 | 
				
			||||||
		struct osmo_cni_peer_id vlr; \
 | 
					 | 
				
			||||||
		OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
 | 
					 | 
				
			||||||
		ASSERT_RC(db_ind_del(dbc, &vlr), 0); \
 | 
					 | 
				
			||||||
		fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \
 | 
					 | 
				
			||||||
	} while (0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-23", 1);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-11", 2);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-42", 3);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-22", 4);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-0x17", 5);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-0xaa", 6);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-42", 3);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-22", 4);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-0x17", 5);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-0xaa", 6);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-0xbb", 7);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-0x2a", 8);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-42", 3);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-22", 4);
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-23", 1);
 | 
					 | 
				
			||||||
	ASSERT_IND("sgsn-11", 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	IND_DEL("msc-0x17"); /* dropped IND == 5 */
 | 
					 | 
				
			||||||
	ASSERT_IND("msc-0x2a", 8); /* known CS remains where it is */
 | 
					 | 
				
			||||||
	ASSERT_IND("any-unknown", 9); /* new VLR takes a new IND from the end */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	comment_end();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct {
 | 
					static struct {
 | 
				
			||||||
	bool verbose;
 | 
						bool verbose;
 | 
				
			||||||
} cmdline_opts = {
 | 
					} cmdline_opts = {
 | 
				
			||||||
@@ -1024,13 +969,9 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	handle_options(argc, argv);
 | 
						handle_options(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	osmo_init_logging2(ctx, &hlr_log_info);
 | 
						osmo_init_logging2(ctx, &hlr_log_info);
 | 
				
			||||||
	log_set_print_filename2(osmo_stderr_target,
 | 
						log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
 | 
				
			||||||
				cmdline_opts.verbose ?
 | 
					 | 
				
			||||||
					LOG_FILENAME_BASENAME :
 | 
					 | 
				
			||||||
					LOG_FILENAME_NONE);
 | 
					 | 
				
			||||||
	log_set_print_timestamp(osmo_stderr_target, 0);
 | 
						log_set_print_timestamp(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
						log_set_use_color(osmo_stderr_target, 0);
 | 
				
			||||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
					 | 
				
			||||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
						log_set_print_category(osmo_stderr_target, 1);
 | 
				
			||||||
	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
						log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1046,7 +987,6 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	test_subscr_aud();
 | 
						test_subscr_aud();
 | 
				
			||||||
	test_subscr_aud_invalid_len();
 | 
						test_subscr_aud_invalid_len();
 | 
				
			||||||
	test_subscr_sqn();
 | 
						test_subscr_sqn();
 | 
				
			||||||
	test_ind();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("Done\n");
 | 
						printf("Done\n");
 | 
				
			||||||
	db_close(dbc);
 | 
						db_close(dbc);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000002',
 | 
					  .imsi = '123456789000002',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
@@ -36,10 +36,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
				
			||||||
@@ -48,10 +48,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000001',
 | 
					  .imsi = '123456789000001',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
				
			||||||
@@ -64,21 +64,25 @@ db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG
 | 
				
			|||||||
DAUC Cannot create subscriber: invalid IMSI: '123456789 000003'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123456789 000003'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123456789000003", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123456789000003", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000003': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
					db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: '123456789000002123456'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123456789000002123456'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123456789000002123456", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123456789000002123456", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000002123456': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
					db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: 'foobar123'
 | 
					DAUC Cannot create subscriber: invalid IMSI: 'foobar123'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='foobar123': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
					db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: '123'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, short_imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
					db_subscr_create(dbc, short_imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,6 +142,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "54321012345678912345678", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "54321012345678912345678", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='54321012345678912345678': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, imsi0, "543 21") --> -EINVAL
 | 
					db_subscr_update_msisdn_by_imsi(dbc, imsi0, "543 21") --> -EINVAL
 | 
				
			||||||
DAUC IMSI='123456789000000': Cannot update subscriber: invalid MSISDN: '543 21'
 | 
					DAUC IMSI='123456789000000': Cannot update subscriber: invalid MSISDN: '543 21'
 | 
				
			||||||
@@ -150,6 +155,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "543 21", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "543 21", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='543 21': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, imsi0, "foobar123") --> -EINVAL
 | 
					db_subscr_update_msisdn_by_imsi(dbc, imsi0, "foobar123") --> -EINVAL
 | 
				
			||||||
DAUC IMSI='123456789000000': Cannot update subscriber: invalid MSISDN: 'foobar123'
 | 
					DAUC IMSI='123456789000000': Cannot update subscriber: invalid MSISDN: 'foobar123'
 | 
				
			||||||
@@ -162,6 +168,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='foobar123': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, imsi0, "5") --> 0
 | 
					db_subscr_update_msisdn_by_imsi(dbc, imsi0, "5") --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -180,6 +187,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "54321", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "54321", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='54321': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, imsi0, "543210123456789") --> 0
 | 
					db_subscr_update_msisdn_by_imsi(dbc, imsi0, "543210123456789") --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -208,6 +216,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "5432101234567891", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "5432101234567891", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='5432101234567891': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Check if subscriber exists (by MSISDN)
 | 
					--- Check if subscriber exists (by MSISDN)
 | 
				
			||||||
@@ -223,11 +232,13 @@ db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99") --> -ENOENT
 | 
				
			|||||||
DAUC Cannot update MSISDN: no such subscriber: IMSI='999999999'
 | 
					DAUC Cannot update MSISDN: no such subscriber: IMSI='999999999'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "99", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "99", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='99': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99") --> -ENOENT
 | 
					db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99") --> -ENOENT
 | 
				
			||||||
DAUC Cannot update MSISDN: no such subscriber: IMSI='foobar'
 | 
					DAUC Cannot update MSISDN: no such subscriber: IMSI='foobar'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_msisdn(dbc, "99", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_msisdn(dbc, "99", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: MSISDN='99': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set valid / invalid IMEI
 | 
					--- Set valid / invalid IMEI
 | 
				
			||||||
@@ -254,6 +265,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imei(dbc, "123456789012345", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imei(dbc, "123456789012345", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMEI=123456789012345: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set the same IMEI again
 | 
					--- Set the same IMEI again
 | 
				
			||||||
@@ -274,6 +286,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_update_imei_by_imsi(dbc, imsi0, NULL) --> 0
 | 
					db_subscr_update_imei_by_imsi(dbc, imsi0, NULL) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMEI=12345678901234: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set / unset nam_cs and nam_ps
 | 
					--- Set / unset nam_cs and nam_ps
 | 
				
			||||||
@@ -411,6 +424,7 @@ db_subscr_nam(dbc, unknown_imsi, false, false) --> -ENOENT
 | 
				
			|||||||
DAUC Cannot disable CS: no such subscriber: IMSI='999999999'
 | 
					DAUC Cannot disable CS: no such subscriber: IMSI='999999999'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='999999999': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_nam(dbc, "foobar", false, true) --> -ENOENT
 | 
					db_subscr_nam(dbc, "foobar", false, true) --> -ENOENT
 | 
				
			||||||
DAUC Cannot disable PS: no such subscriber: IMSI='foobar'
 | 
					DAUC Cannot disable PS: no such subscriber: IMSI='foobar'
 | 
				
			||||||
@@ -421,7 +435,7 @@ DAUC Cannot disable CS: no such subscriber: IMSI='foobar'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names)
 | 
					--- Record LU for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "5952", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "5952", true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -431,7 +445,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '5952',
 | 
					  .sgsn_number = '5952',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "712", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "712", false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -445,7 +459,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
					--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -456,7 +470,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -467,7 +481,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -478,7 +492,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -492,7 +506,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
					--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "", true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -502,7 +516,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '222',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "", false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -511,9 +525,9 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .msisdn = '543210123456789',
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -524,7 +538,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, NULL, true) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, true) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -534,7 +548,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '222',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, id0, NULL, false) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, false) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -546,13 +560,14 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for non-existent ID
 | 
					--- Record LU for non-existent ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, 99999, "5952", true) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "5952", true) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu_str(dbc, 99999, "712", false) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "712", false) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: ID=99999: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Purge and un-purge PS and CS
 | 
					--- Purge and un-purge PS and CS
 | 
				
			||||||
@@ -683,11 +698,13 @@ db_subscr_purge(dbc, unknown_imsi, true, true) --> -ENOENT
 | 
				
			|||||||
DAUC Cannot purge PS: no such subscriber: IMSI='999999999'
 | 
					DAUC Cannot purge PS: no such subscriber: IMSI='999999999'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='999999999': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_purge(dbc, unknown_imsi, true, false) --> -ENOENT
 | 
					db_subscr_purge(dbc, unknown_imsi, true, false) --> -ENOENT
 | 
				
			||||||
DAUC Cannot purge CS: no such subscriber: IMSI='999999999'
 | 
					DAUC Cannot purge CS: no such subscriber: IMSI='999999999'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, unknown_imsi, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='999999999': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Delete non-existent / invalid IDs
 | 
					--- Delete non-existent / invalid IDs
 | 
				
			||||||
@@ -711,6 +728,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id0) --> 0
 | 
					db_subscr_delete_by_id(dbc, id0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_delete_by_id(dbc, id0) --> -ENOENT
 | 
					db_subscr_delete_by_id(dbc, id0) --> -ENOENT
 | 
				
			||||||
DAUC Cannot delete: no such subscriber: ID=1
 | 
					DAUC Cannot delete: no such subscriber: ID=1
 | 
				
			||||||
@@ -724,6 +742,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id1) --> 0
 | 
					db_subscr_delete_by_id(dbc, id1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000001': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -734,6 +753,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id2) --> 0
 | 
					db_subscr_delete_by_id(dbc, id2) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000002': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -744,6 +764,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id_short) --> 0
 | 
					db_subscr_delete_by_id(dbc, id_short) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Create and delete subscribers with non-default nam_cs and nam_ps
 | 
					--- Create and delete subscribers with non-default nam_cs and nam_ps
 | 
				
			||||||
@@ -1284,6 +1305,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id) --> 0
 | 
					db_subscr_delete_by_id(dbc, id) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Re-add subscriber and verify auth data didn't come back
 | 
					--- Re-add subscriber and verify auth data didn't come back
 | 
				
			||||||
@@ -1308,6 +1330,7 @@ DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id) --> 0
 | 
					db_subscr_delete_by_id(dbc, id) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL, false) --> -2
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL, false) --> -2
 | 
				
			||||||
DAUC IMSI='123456789000000': No such subscriber
 | 
					DAUC IMSI='123456789000000': No such subscriber
 | 
				
			||||||
@@ -1408,11 +1431,13 @@ db_update_sqn(dbc, 99, 999) --> -ENOENT
 | 
				
			|||||||
DAUC Cannot update SQN for subscriber ID=99: no auc_3g entry for such subscriber
 | 
					DAUC Cannot update SQN for subscriber ID=99: no auc_3g entry for such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, 99, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_id(dbc, 99, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: ID=99: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_update_sqn(dbc, 9999, 99) --> -ENOENT
 | 
					db_update_sqn(dbc, 9999, 99) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SQN for subscriber ID=9999: no auc_3g entry for such subscriber
 | 
					DAUC Cannot update SQN for subscriber ID=9999: no auc_3g entry for such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, 9999, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_id(dbc, 9999, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: ID=9999: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Create subscriber
 | 
					--- Create subscriber
 | 
				
			||||||
@@ -1610,86 +1635,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
db_subscr_delete_by_id(dbc, id) --> 0
 | 
					db_subscr_delete_by_id(dbc, id) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
===== test_subscr_sqn: SUCCESS
 | 
					===== test_subscr_sqn: SUCCESS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
===== test_ind
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-23\0" ind = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-11\0" ind = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-42\0" ind = 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-22\0" ind = 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-0x17\0" ind = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-0xaa\0" ind = 6
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-42\0" ind = 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-22\0" ind = 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-0x17\0" ind = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-0xaa\0" ind = 6
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-0xbb\0" ind = 7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-0x2a\0" ind = 8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-42\0" ind = 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-22\0" ind = 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-23\0" ind = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"sgsn-11\0" ind = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind_del(dbc, &vlr) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-0x17\0" ind deleted
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"msc-0x2a\0" ind = 8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_ind(dbc, &vlr, &ind) --> 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"any-unknown\0" ind = 9
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
===== test_ind: SUCCESS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,8 +84,6 @@ DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 1
 | 
				
			|||||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 2
 | 
					DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 2
 | 
				
			||||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 3
 | 
					DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 3
 | 
				
			||||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 4
 | 
					DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 4
 | 
				
			||||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 5
 | 
					 | 
				
			||||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6
 | 
					 | 
				
			||||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
					DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Resulting db:
 | 
					Resulting db:
 | 
				
			||||||
@@ -118,13 +116,6 @@ algo_id_3g|ind_bitlen|k|op|opc|sqn|subscriber_id
 | 
				
			|||||||
5|5|44444444444444444444444444444444|44444444444444444444444444444444||0|5
 | 
					5|5|44444444444444444444444444444444|44444444444444444444444444444444||0|5
 | 
				
			||||||
5|5|55555555555555555555555555555555||55555555555555555555555555555555|0|6
 | 
					5|5|55555555555555555555555555555555||55555555555555555555555555555555|0|6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Table: ind
 | 
					 | 
				
			||||||
name|type|notnull|dflt_value|pk
 | 
					 | 
				
			||||||
ind|INTEGER|0||1
 | 
					 | 
				
			||||||
vlr|TEXT|1||0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Table ind contents:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Table: subscriber
 | 
					Table: subscriber
 | 
				
			||||||
name|type|notnull|dflt_value|pk
 | 
					name|type|notnull|dflt_value|pk
 | 
				
			||||||
ggsn_number|VARCHAR(15)|0||0
 | 
					ggsn_number|VARCHAR(15)|0||0
 | 
				
			||||||
@@ -146,19 +137,17 @@ periodic_lu_tmr|INTEGER|0||0
 | 
				
			|||||||
periodic_rau_tau_tmr|INTEGER|0||0
 | 
					periodic_rau_tau_tmr|INTEGER|0||0
 | 
				
			||||||
sgsn_address|VARCHAR|0||0
 | 
					sgsn_address|VARCHAR|0||0
 | 
				
			||||||
sgsn_number|VARCHAR(15)|0||0
 | 
					sgsn_number|VARCHAR(15)|0||0
 | 
				
			||||||
sgsn_via_proxy|VARCHAR|0||0
 | 
					 | 
				
			||||||
smsc_number|VARCHAR(15)|0||0
 | 
					smsc_number|VARCHAR(15)|0||0
 | 
				
			||||||
vlr_number|VARCHAR(15)|0||0
 | 
					vlr_number|VARCHAR(15)|0||0
 | 
				
			||||||
vlr_via_proxy|VARCHAR|0||0
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Table subscriber contents:
 | 
					Table subscriber contents:
 | 
				
			||||||
ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy
 | 
					ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|smsc_number|vlr_number
 | 
				
			||||||
||1|||123456789012345||||0|0||098765432109876|1|1|||||||MSC-1|
 | 
					||1|||123456789012345||||0|0||098765432109876|1|1||||||MSC-1
 | 
				
			||||||
||2|||111111111||||1|0|||1|1||||||||
 | 
					||2|||111111111||||1|0|||1|1||||||
 | 
				
			||||||
||3|||222222222||||0|1||22222|1|1||||||||
 | 
					||3|||222222222||||0|1||22222|1|1||||||
 | 
				
			||||||
||4|||333333||||0|0||3|0|1||||||||
 | 
					||4|||333333||||0|0||3|0|1||||||
 | 
				
			||||||
||5|||444444444444444||||0|0||4444|1|0||||||||
 | 
					||5|||444444444444444||||0|0||4444|1|0||||||
 | 
				
			||||||
||6|||5555555||||0|0||55555555555555|0|0||||||||
 | 
					||6|||5555555||||0|0||55555555555555|0|0||||||
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Table: subscriber_apn
 | 
					Table: subscriber_apn
 | 
				
			||||||
name|type|notnull|dflt_value|pk
 | 
					name|type|notnull|dflt_value|pk
 | 
				
			||||||
@@ -179,5 +168,5 @@ osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg
 | 
				
			|||||||
rc = 0
 | 
					rc = 0
 | 
				
			||||||
DMAIN hlr starting
 | 
					DMAIN hlr starting
 | 
				
			||||||
DDB using database: <PATH>test.db
 | 
					DDB using database: <PATH>test.db
 | 
				
			||||||
DDB Database <PATH>test.db' has HLR DB schema version 6
 | 
					DDB Database <PATH>test.db' has HLR DB schema version 4
 | 
				
			||||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
					DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user