mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-11-04 06:03:28 +00:00 
			
		
		
		
	Compare commits
	
		
			37 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1f8f1b9777 | ||
| 
						 | 
					a5ed663dea | ||
| 
						 | 
					15031855bf | ||
| 
						 | 
					83b909e4f3 | ||
| 
						 | 
					fde8f64d1d | ||
| 
						 | 
					d8e8a8425a | ||
| 
						 | 
					c1d56fd2e9 | ||
| 
						 | 
					6ac66ac3ad | ||
| 
						 | 
					769a7f46f4 | ||
| 
						 | 
					5bc457e19a | ||
| 
						 | 
					e3c788d9a8 | ||
| 
						 | 
					e6751dd207 | ||
| 
						 | 
					e8b7b8634b | ||
| 
						 | 
					ce172efc0b | ||
| 
						 | 
					2f1049827b | ||
| 
						 | 
					92d1c4a0de | ||
| 
						 | 
					fc4af602ba | ||
| 
						 | 
					011e781da7 | ||
| 
						 | 
					cb88c34aca | ||
| 
						 | 
					e18e3a42f1 | ||
| 
						 | 
					710e0c9486 | ||
| 
						 | 
					f6d457e7ef | ||
| 
						 | 
					fa287c579c | ||
| 
						 | 
					e78241ae07 | ||
| 
						 | 
					62d916f3cd | ||
| 
						 | 
					3ad481afbf | ||
| 
						 | 
					6aa871db71 | ||
| 
						 | 
					16a16e6aa4 | ||
| 
						 | 
					fac52fbd6b | ||
| 
						 | 
					f0c02ad9c7 | ||
| 
						 | 
					43c36f99dd | ||
| 
						 | 
					008ce4bd43 | ||
| 
						 | 
					f13a8bc4f9 | ||
| 
						 | 
					50bf7b775b | ||
| 
						 | 
					cb508554df | ||
| 
						 | 
					06455eac9c | ||
| 
						 | 
					2bdcc8eec9 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -26,7 +26,6 @@ m4
 | 
			
		||||
*.m4
 | 
			
		||||
missing
 | 
			
		||||
.deps
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
*.pc
 | 
			
		||||
.libs
 | 
			
		||||
@@ -68,5 +67,3 @@ doc/manuals/generated/
 | 
			
		||||
doc/manuals/osmomsc-usermanual.xml
 | 
			
		||||
doc/manuals/common
 | 
			
		||||
doc/manuals/build
 | 
			
		||||
 | 
			
		||||
contrib/osmo-hlr.spec
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
AUTOMAKE_OPTIONS = foreign dist-bzip2
 | 
			
		||||
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	doc \
 | 
			
		||||
	src \
 | 
			
		||||
	include \
 | 
			
		||||
	doc \
 | 
			
		||||
	sql \
 | 
			
		||||
	contrib \
 | 
			
		||||
	tests \
 | 
			
		||||
@@ -11,16 +11,13 @@ SUBDIRS = \
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	.version \
 | 
			
		||||
	contrib/osmo-hlr.spec.in \
 | 
			
		||||
	debian \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 | 
			
		||||
pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = libosmo-gsup-client.pc \
 | 
			
		||||
		 libosmo-mslookup.pc
 | 
			
		||||
pkgconfig_DATA = libosmo-gsup-client.pc
 | 
			
		||||
 | 
			
		||||
@RELMAKE@
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										30
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								configure.ac
									
									
									
									
									
								
							@@ -12,8 +12,6 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_TESTDIR(tests)
 | 
			
		||||
 | 
			
		||||
CFLAGS="$CFLAGS -std=gnu11"
 | 
			
		||||
 | 
			
		||||
dnl kernel style compile messages
 | 
			
		||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
			
		||||
 | 
			
		||||
@@ -27,11 +25,6 @@ AC_PROG_MKDIR_P
 | 
			
		||||
AC_PROG_CC
 | 
			
		||||
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)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
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(LIBOSMOCORE, libosmocore >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
 | 
			
		||||
 | 
			
		||||
@@ -107,22 +100,13 @@ if test "x$enable_ext_tests" = "xyes" ; then
 | 
			
		||||
	AM_PATH_PYTHON
 | 
			
		||||
	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
			
		||||
	 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
 | 
			
		||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
			
		||||
AC_MSG_RESULT([$enable_ext_tests])
 | 
			
		||||
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
 | 
			
		||||
AC_ARG_ENABLE(manuals,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -202,10 +186,10 @@ AC_OUTPUT(
 | 
			
		||||
	contrib/Makefile
 | 
			
		||||
	contrib/systemd/Makefile
 | 
			
		||||
	contrib/dgsm/Makefile
 | 
			
		||||
	contrib/osmo-hlr.spec
 | 
			
		||||
	tests/Makefile
 | 
			
		||||
	tests/auc/Makefile
 | 
			
		||||
	tests/auc/gen_ts_55_205_test_sets/Makefile
 | 
			
		||||
	tests/gsup_server/Makefile
 | 
			
		||||
	tests/gsup/Makefile
 | 
			
		||||
	tests/db/Makefile
 | 
			
		||||
	tests/db_upgrade/Makefile
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,3 @@ SUBDIRS = \
 | 
			
		||||
	systemd \
 | 
			
		||||
	dgsm \
 | 
			
		||||
	$(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,13 +1,13 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
"""
 | 
			
		||||
SPDX-License-Identifier: MIT
 | 
			
		||||
SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
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):
 | 
			
		||||
Based on esme.py from RCCN:
 | 
			
		||||
https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
 | 
			
		||||
Copyright 2017 keith <keith@rhizomatica.org>
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +39,13 @@ import time
 | 
			
		||||
 | 
			
		||||
def can_handle_pdu(pdu):
 | 
			
		||||
    if not isinstance(pdu, smpplib.command.DeliverSM):
 | 
			
		||||
        logging.info('PDU is not a DeliverSM, ignoring')
 | 
			
		||||
        logging.info('PDU is not a DeliverSM. Is OsmoMSC configured properly?')
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    # Multipart SMS etc. not handled here (see RCCN's esme.py)
 | 
			
		||||
    if pdu.esm_class & smpplib.consts.SMPP_GSMFEAT_UDHI:
 | 
			
		||||
        logging.info("UDH (User Data Header) handling not implemented in this"
 | 
			
		||||
                     " example, dropping message.")
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
 | 
			
		||||
@@ -63,8 +69,7 @@ def query_mslookup(service_type, id, id_type='msisdn'):
 | 
			
		||||
    return json.loads(result_line)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
 | 
			
		||||
           unicode_text):
 | 
			
		||||
def tx_sms(dst_host, dst_port, source, destination, 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)
 | 
			
		||||
@@ -79,7 +84,7 @@ def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
 | 
			
		||||
        dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
 | 
			
		||||
        destination_addr=destination.decode(),
 | 
			
		||||
        short_message=unicode_text,
 | 
			
		||||
        registered_delivery=registered_delivery,
 | 
			
		||||
        registered_delivery=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    smpp_client.unbind()
 | 
			
		||||
@@ -100,9 +105,6 @@ def rx_deliver_sm(pdu):
 | 
			
		||||
        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'
 | 
			
		||||
@@ -111,8 +113,7 @@ def rx_deliver_sm(pdu):
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
           pdu.destination_addr, pdu.short_message)
 | 
			
		||||
 | 
			
		||||
    return smpplib.consts.SMPP_ESME_ROK
 | 
			
		||||
 | 
			
		||||
@@ -150,35 +151,12 @@ def main():
 | 
			
		||||
    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,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
"""
 | 
			
		||||
SPDX-License-Identifier: MIT
 | 
			
		||||
SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
 | 
			
		||||
 | 
			
		||||
This is a freeswitch dialplan implementation, see:
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-abis
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@@ -48,19 +49,14 @@ set -x
 | 
			
		||||
 | 
			
		||||
cd "$base"
 | 
			
		||||
autoreconf --install --force
 | 
			
		||||
./configure \
 | 
			
		||||
	--enable-sanitize \
 | 
			
		||||
	--enable-external-tests \
 | 
			
		||||
	--enable-mslookup-client-mdns-test \
 | 
			
		||||
	--enable-werror \
 | 
			
		||||
	$CONFIG
 | 
			
		||||
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
 | 
			
		||||
$MAKE $PARALLEL_MAKE
 | 
			
		||||
$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
 | 
			
		||||
	make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
			
		||||
$MAKE maintainer-clean
 | 
			
		||||
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.3.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.7.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-mslookup0
 | 
			
		||||
Summary:        Osmocom MS lookup library
 | 
			
		||||
License:        GPL-2.0-or-later
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mslookup0
 | 
			
		||||
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-mslookup0 = %{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-mslookup0 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libosmo-mslookup0 -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-mslookup0
 | 
			
		||||
%{_libdir}/libosmo-mslookup.so.0*
 | 
			
		||||
 | 
			
		||||
%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
 | 
			
		||||
@@ -5,7 +5,7 @@ Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
			
		||||
ExecStart=/usr/bin/osmo-hlr -U -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
			
		||||
RestartSec=2
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										198
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										198
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,201 +1,3 @@
 | 
			
		||||
osmo-hlr (1.5.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 12:18:25 +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
 | 
			
		||||
 | 
			
		||||
  [ Ruben Undheim ]
 | 
			
		||||
  * Fix test for return codes on mipsel and alpha archs
 | 
			
		||||
 | 
			
		||||
  [ Thorsten Alteholz ]
 | 
			
		||||
  * fix spelling errors detected by lintian
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * tests: Fix db_test err file to expect error code name instead of value
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * tests/test_nodes.vty: check less libosmocore cmds
 | 
			
		||||
  * tests/db_upgrade: disable for old sqlite versions
 | 
			
		||||
  * gitignore: add tests/db_upgrade/*.dump
 | 
			
		||||
  * gsup_client.h: fix license header: GPLv2+
 | 
			
		||||
  * tests/auc: change back to python3
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * fix double free in osmo_gsup_client_enc_send()
 | 
			
		||||
  * db upgrade to v2: log version 2, not 1
 | 
			
		||||
  * fix upgrade to version 2: imei column default value
 | 
			
		||||
  * add --db-check option
 | 
			
		||||
  * hlr.sql: move comment
 | 
			
		||||
  * add db_upgrade test
 | 
			
		||||
  * hlr db schema 3: hlr_number -> msc_number
 | 
			
		||||
  * db.c: code dup: add db_run_statements() for arrays of statements
 | 
			
		||||
  * move headers to include/osmocom/hlr
 | 
			
		||||
  * fix upgrade test in presence of ~/.sqliterc
 | 
			
		||||
  * db upgrade: remove some code dup
 | 
			
		||||
  * add osmo_gsup_msgb_alloc()
 | 
			
		||||
  * Makefile convenience: add VTY_TEST var to run only one test
 | 
			
		||||
  * remove gsup_test
 | 
			
		||||
  * test_nodes.vty: tweak: add some '?' checks
 | 
			
		||||
  * db v4: add column last_lu_seen_ps
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * AUC: Add support for setting the AMF separation bit to '1' for EUTRAN
 | 
			
		||||
  * hlr: exit(2) on unsupported positional arguments on command line
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 12:37:35 +0100
 | 
			
		||||
 | 
			
		||||
osmo-hlr (1.1.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -1,26 +1,27 @@
 | 
			
		||||
Source: osmo-hlr
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Maintainer: Max Suraev <msuraev@sysmocom.de>
 | 
			
		||||
Build-Depends: debhelper (>= 9),
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               dh-systemd (>= 1.5),
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               python3-minimal,
 | 
			
		||||
               libosmocore-dev (>= 1.7.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.3.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.2.0),
 | 
			
		||||
               libosmocore-dev,
 | 
			
		||||
               libosmo-abis-dev,
 | 
			
		||||
               libosmo-netif-dev,
 | 
			
		||||
               libsqlite3-dev,
 | 
			
		||||
               sqlite3,
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.3.0)
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
Standards-Version: 3.9.6
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
 | 
			
		||||
Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-hlr
 | 
			
		||||
Homepage: https://projects.osmocom.org/projects/osmo-hlr
 | 
			
		||||
 | 
			
		||||
Package: osmo-hlr
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
 | 
			
		||||
Description: Osmocom Home Location Register
 | 
			
		||||
 OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which works over GSUP
 | 
			
		||||
 protocol. The subscribers are store in sqlite DB. It supports both 2G and 3G authentication.
 | 
			
		||||
@@ -80,16 +81,6 @@ Description: Development headers of Osmocom MS lookup library
 | 
			
		||||
  .
 | 
			
		||||
  This package contains the development headers.
 | 
			
		||||
 | 
			
		||||
Package: osmo-mslookup-utils
 | 
			
		||||
Architecture: any
 | 
			
		||||
Section: utils
 | 
			
		||||
Depends: ${shlibs:Depends},
 | 
			
		||||
	 libosmo-mslookup0 (= ${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
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							@@ -5,5 +5,4 @@
 | 
			
		||||
/usr/share/doc/osmo-hlr/sql/hlr.sql
 | 
			
		||||
/usr/share/doc/osmo-hlr/sql/hlr_data.sql
 | 
			
		||||
/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
 | 
			
		||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
 | 
			
		||||
/var/lib/osmocom
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 db notice
 | 
			
		||||
 logging level auc notice
 | 
			
		||||
 logging level ss notice
 | 
			
		||||
 logging level ss info
 | 
			
		||||
 logging level linp error
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
@@ -24,3 +24,10 @@ hlr
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
 ussd route prefix *#100# internal own-msisdn
 | 
			
		||||
 ussd route prefix *#101# internal own-imsi
 | 
			
		||||
 ussd route prefix *#102# internal get-ran
 | 
			
		||||
 ussd route prefix *#200# internal gsm-off
 | 
			
		||||
 ussd route prefix *#201# internal gsm-on
 | 
			
		||||
 ussd route prefix *#300# internal umts-off
 | 
			
		||||
 ussd route prefix *#301# internal umts-on
 | 
			
		||||
 ussd route prefix *#400# internal lte-off
 | 
			
		||||
 ussd route prefix *#401# internal lte-on
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
    example_subscriber_add_update_delete.vty \
 | 
			
		||||
    example_subscriber_aud2g.ctrl \
 | 
			
		||||
    example_subscriber_aud3g.ctrl \
 | 
			
		||||
EXTRA_DIST = example_subscriber_add_update_delete.vty \
 | 
			
		||||
    example_subscriber_cs_ps_enabled.ctrl \
 | 
			
		||||
    example_subscriber_info.ctrl \
 | 
			
		||||
    example_subscriber_msisdn.ctrl \
 | 
			
		||||
    osmohlr-usermanual.adoc \
 | 
			
		||||
    osmohlr-usermanual-docinfo.xml \
 | 
			
		||||
    osmohlr-vty-reference.xml \
 | 
			
		||||
@@ -18,12 +14,6 @@ if BUILD_MANUALS
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
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
 | 
			
		||||
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-\*.*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-\*.*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
 | 
			
		||||
@@ -117,63 +104,3 @@ commands:
 | 
			
		||||
----
 | 
			
		||||
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[]
 | 
			
		||||
----
 | 
			
		||||
 
 | 
			
		||||
@@ -214,12 +214,6 @@ port, but beware that the IP address must be from a multicast range, see <<ietf-
 | 
			
		||||
 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
 | 
			
		||||
@@ -230,25 +224,23 @@ requests matching locally attached subscribers.
 | 
			
		||||
   service sip.voice at 10.9.8.7 5060
 | 
			
		||||
   service smpp.sms at 10.9.8.7 2775
 | 
			
		||||
 | 
			
		||||
In this example:
 | 
			
		||||
In this example, "10.9.8.7 5060" would be the IP address and port on which the local site's PBX is bound to receive SIP
 | 
			
		||||
Invite requests; "10.9.8.7 2775" would be the local site's OsmoMSC SMPP bind address and port.
 | 
			
		||||
 | 
			
		||||
- "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).
 | 
			
		||||
Obviously, these IP addresses must be routable back to this site from all other sites. If, for example, the PBX is
 | 
			
		||||
configured to bind on "0.0.0.0", it won't work to enter the same as service address -- remote sites cannot route to
 | 
			
		||||
0.0.0.0. Instead, the mslookup service requires a public IP address of a local interface. For the same reasons, never
 | 
			
		||||
add link-local addresses like 127.0.0.1 as mslookup services.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
   msc 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
 | 
			
		||||
   msc msc-901-70-0
 | 
			
		||||
    service sip.voice at 10.9.8.7 5060
 | 
			
		||||
    service smpp.sms at 10.9.8.7 2775
 | 
			
		||||
 | 
			
		||||
@@ -332,7 +324,7 @@ The mslookup ID types are fixed, while service names can be chosen arbitrarily.
 | 
			
		||||
|===
 | 
			
		||||
|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)
 | 
			
		||||
|sip.voice | SIP | SIP PBX or 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
 | 
			
		||||
|===
 | 
			
		||||
@@ -420,7 +412,6 @@ dialplan implementation. An example dialplan implementation for FreeSWITCH that
 | 
			
		||||
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=".*">
 | 
			
		||||
@@ -434,7 +425,6 @@ To integrate it with your FREESWITCH setup, add a new `extension` block to your
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,83 +127,3 @@ OsmoHLR# subscriber imei 35761300444848 show
 | 
			
		||||
----
 | 
			
		||||
<1> Randomly generated 5 digit MSISDN
 | 
			
		||||
<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.
 | 
			
		||||
 | 
			
		||||
`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 subscribers own phone number.  More information on internal USSD
 | 
			
		||||
handlers can be found in <<iuse_handlers>>.
 | 
			
		||||
the subscribers own phone number.  There is one other handler called
 | 
			
		||||
`own-imsi` which will return the IMSI instead of the MSISDN.
 | 
			
		||||
 | 
			
		||||
`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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -28,8 +28,6 @@ include::{srcdir}/chapters/dgsm.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gsup.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty_cpu_sched.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
@@ -37,3 +35,4 @@ include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
include::./common/chapters/glossary.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,7 +1,7 @@
 | 
			
		||||
SUBDIRS = osmocom
 | 
			
		||||
 | 
			
		||||
nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/gsupclient/cni_peer_id.h \
 | 
			
		||||
	osmocom/gsupclient/gsup_peer_id.h \
 | 
			
		||||
	osmocom/gsupclient/gsup_client.h \
 | 
			
		||||
	osmocom/gsupclient/gsup_req.h \
 | 
			
		||||
	osmocom/mslookup/mdns.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -32,35 +32,33 @@ struct osmo_ipa_name {
 | 
			
		||||
	uint8_t val[128];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name);
 | 
			
		||||
bool osmo_ipa_name_is_empty(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 */
 | 
			
		||||
enum osmo_gsup_peer_id_type {
 | 
			
		||||
	OSMO_GSUP_PEER_ID_EMPTY=0,
 | 
			
		||||
	OSMO_GSUP_PEER_ID_IPA_NAME,
 | 
			
		||||
	/* OSMO_GSUP_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); }
 | 
			
		||||
extern const struct value_string osmo_gsup_peer_id_type_names[];
 | 
			
		||||
static inline const char *osmo_gsup_peer_id_type_name(enum osmo_gsup_peer_id_type val)
 | 
			
		||||
{ return get_value_string(osmo_gsup_peer_id_type_names, val); }
 | 
			
		||||
 | 
			
		||||
struct osmo_cni_peer_id {
 | 
			
		||||
	enum osmo_cni_peer_id_type type;
 | 
			
		||||
struct osmo_gsup_peer_id {
 | 
			
		||||
	enum osmo_gsup_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,
 | 
			
		||||
bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id);
 | 
			
		||||
int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_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,
 | 
			
		||||
int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_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);
 | 
			
		||||
int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_peer_id *b);
 | 
			
		||||
const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gsup_peer_id);
 | 
			
		||||
@@ -19,14 +19,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsup.h>
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_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) ? osmo_gsup_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)
 | 
			
		||||
@@ -56,11 +56,11 @@ struct osmo_gsup_req {
 | 
			
		||||
	/* 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;
 | 
			
		||||
	struct osmo_gsup_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;
 | 
			
		||||
	struct osmo_gsup_peer_id via_proxy;
 | 
			
		||||
 | 
			
		||||
	/* Identify this request by number, for logging. */
 | 
			
		||||
	unsigned int nr;
 | 
			
		||||
@@ -82,28 +82,24 @@ struct osmo_gsup_req {
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
 | 
			
		||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_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. */
 | 
			
		||||
/*! Call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
 | 
			
		||||
#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. */
 | 
			
		||||
/*! Call _osmo_gsup_req_respond_msgt() to convey the sender's source file and line in the logs. */
 | 
			
		||||
#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.
 | 
			
		||||
 */
 | 
			
		||||
/*! Log an error message, and call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
 | 
			
		||||
#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); \
 | 
			
		||||
 
 | 
			
		||||
@@ -16,5 +16,6 @@ noinst_HEADERS = \
 | 
			
		||||
	proxy.h \
 | 
			
		||||
	rand.h \
 | 
			
		||||
	remote_hlr.h \
 | 
			
		||||
	sms_over_gsup.h \
 | 
			
		||||
	timestamp.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,4 +30,5 @@ enum hlr_ctrl_node {
 | 
			
		||||
	_LAST_CTRL_NODE_HLR
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int hlr_ctrl_cmds_install();
 | 
			
		||||
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,19 +3,13 @@
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <sqlite3.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/gsm/gsup.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
struct hlr;
 | 
			
		||||
 | 
			
		||||
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_MSISDN,
 | 
			
		||||
	DB_STMT_SEL_BY_ID,
 | 
			
		||||
@@ -44,6 +38,8 @@ enum stmt_idx {
 | 
			
		||||
	DB_STMT_IND_ADD,
 | 
			
		||||
	DB_STMT_IND_SELECT,
 | 
			
		||||
	DB_STMT_IND_DEL,
 | 
			
		||||
	DB_STMT_UPD_RAT_FLAG,
 | 
			
		||||
	DB_STMT_RAT_BY_ID,
 | 
			
		||||
	_NUM_DB_STMT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -111,11 +107,18 @@ struct hlr_subscriber {
 | 
			
		||||
	bool		ms_purged_ps;
 | 
			
		||||
	time_t		last_lu_seen;
 | 
			
		||||
	time_t		last_lu_seen_ps;
 | 
			
		||||
	char		last_lu_rat_cs[128];
 | 
			
		||||
	char		last_lu_rat_ps[128];
 | 
			
		||||
	/* talloc'd IPA unit name */
 | 
			
		||||
	struct osmo_ipa_name	vlr_via_proxy;
 | 
			
		||||
	struct osmo_ipa_name	sgsn_via_proxy;
 | 
			
		||||
	bool		rat_types[OSMO_RAT_COUNT];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct hlr_subscriber hlr_subscriber_empty = {
 | 
			
		||||
		.rat_types = { true, true, true },
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
/* A format string for use with strptime(3). This format string is
 | 
			
		||||
 * used to parse the last_lu_seen column stored in the HLR database.
 | 
			
		||||
 * See https://sqlite.org/lang_datefunc.html, function datetime(). */
 | 
			
		||||
@@ -159,9 +162,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_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,
 | 
			
		||||
			  struct hlr_subscriber *subscr);
 | 
			
		||||
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
			
		||||
@@ -172,13 +172,18 @@ int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_s
 | 
			
		||||
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,
 | 
			
		||||
		 const struct osmo_ipa_name *vlr_name, bool is_ps,
 | 
			
		||||
		 const struct osmo_ipa_name *via_proxy);
 | 
			
		||||
		 const struct osmo_ipa_name *via_proxy,
 | 
			
		||||
		 const enum osmo_rat_type rat_types[], size_t rat_types_len);
 | 
			
		||||
 | 
			
		||||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
			
		||||
		    bool purge_val, bool is_ps);
 | 
			
		||||
 | 
			
		||||
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind);
 | 
			
		||||
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
 | 
			
		||||
int db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr, unsigned int *ind);
 | 
			
		||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr);
 | 
			
		||||
 | 
			
		||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
 | 
			
		||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
 | 
			
		||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
 | 
			
		||||
 | 
			
		||||
/*! Call sqlite3_column_text() and copy result to a char[].
 | 
			
		||||
 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#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_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_DGSM(imsi, level, fmt, args...) \
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
#include <osmocom/abis/ipa.h>
 | 
			
		||||
#include <osmocom/abis/ipaccess.h>
 | 
			
		||||
#include <osmocom/gsm/gsup.h>
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
			
		||||
 | 
			
		||||
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
 | 
			
		||||
@@ -42,6 +42,8 @@ struct osmo_gsup_conn {
 | 
			
		||||
	//struct oap_state oap_state;
 | 
			
		||||
	struct tlv_parsed ccm;
 | 
			
		||||
 | 
			
		||||
	unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
 | 
			
		||||
 | 
			
		||||
	/* Set when Location Update is received: */
 | 
			
		||||
	bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
 | 
			
		||||
	bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
 | 
			
		||||
@@ -72,5 +74,5 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
 | 
			
		||||
					    uint8_t *msisdn_enc, size_t msisdn_enc_size,
 | 
			
		||||
				            uint8_t *apn_buf, size_t apn_buf_size,
 | 
			
		||||
					    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,
 | 
			
		||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
 | 
			
		||||
				    struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup);
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ struct hlr {
 | 
			
		||||
 | 
			
		||||
	struct llist_head euse_list;
 | 
			
		||||
	struct hlr_euse *euse_default;
 | 
			
		||||
	struct llist_head iuse_list;
 | 
			
		||||
 | 
			
		||||
	/* NCSS (call independent) session guard timeout value */
 | 
			
		||||
	int ncss_guard_timeout;
 | 
			
		||||
@@ -111,6 +112,14 @@ struct hlr {
 | 
			
		||||
			} mdns;
 | 
			
		||||
		} client;
 | 
			
		||||
	} mslookup;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		/* FIXME actually use branch fixeria/sms for SMSC routing. completely unimplemented */
 | 
			
		||||
		struct osmo_gsup_peer_id smsc;
 | 
			
		||||
 | 
			
		||||
		/* If no SMSC is present / responsible, try punching the SMS through directly when this is true. */
 | 
			
		||||
		bool try_direct_delivery;
 | 
			
		||||
	} sms_over_gsup;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct hlr *g_hlr;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,12 +37,6 @@ enum hlr_vty_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_go_parent(struct vty *vty);
 | 
			
		||||
void hlr_vty_init(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ enum {
 | 
			
		||||
	DMSLOOKUP,
 | 
			
		||||
	DLU,
 | 
			
		||||
	DDGSM,
 | 
			
		||||
	DCTRL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct log_info hlr_log_info;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/mslookup/mslookup.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_mslookup_query;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#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/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/hlr/timestamp.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_gsup_req;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								include/osmocom/hlr/sms_over_gsup.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								include/osmocom/hlr/sms_over_gsup.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
			
		||||
 | 
			
		||||
#define OSMO_MSLOOKUP_SERVICE_SMS_GSUP "gsup.sms"
 | 
			
		||||
 | 
			
		||||
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req);
 | 
			
		||||
@@ -71,7 +71,7 @@ struct osmo_mdns_rfc_header {
 | 
			
		||||
	uint16_t nscount; /* Number of authority records */
 | 
			
		||||
	uint16_t arcount; /* Number of additional records */
 | 
			
		||||
#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;
 | 
			
		||||
	uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1;
 | 
			
		||||
	uint8_t ra:1, z:3, rcode:4;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							@@ -45,6 +45,12 @@ CREATE TABLE subscriber (
 | 
			
		||||
	last_lu_seen TIMESTAMP default NULL,
 | 
			
		||||
	last_lu_seen_ps TIMESTAMP default NULL,
 | 
			
		||||
 | 
			
		||||
	-- Last Radio Access Type list as sent during Location Updating Request.
 | 
			
		||||
	-- This is usually just one RAT name, but can be a comma separated list of strings
 | 
			
		||||
	-- of all the RAT types sent during Location Updating Request.
 | 
			
		||||
	last_lu_rat_cs	TEXT default NULL,
 | 
			
		||||
	last_lu_rat_ps	TEXT 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,
 | 
			
		||||
@@ -87,8 +93,18 @@ CREATE TABLE ind (
 | 
			
		||||
	UNIQUE (vlr)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
 | 
			
		||||
-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
 | 
			
		||||
CREATE TABLE subscriber_rat (
 | 
			
		||||
	subscriber_id	INTEGER,		-- subscriber.id
 | 
			
		||||
	rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu','EUTRAN-SGs')) NOT NULL,	-- Radio Access Technology, see enum ran_type
 | 
			
		||||
	allowed		BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,
 | 
			
		||||
	UNIQUE (subscriber_id, rat)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
			
		||||
CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
 | 
			
		||||
 | 
			
		||||
-- Set HLR database schema version number
 | 
			
		||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
			
		||||
PRAGMA user_version = 6;
 | 
			
		||||
PRAGMA user_version = 8;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								sql/upgrade_v2_to_v3.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								sql/upgrade_v2_to_v3.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
CREATE TABLE subscriber_rat (
 | 
			
		||||
	subscriber_id	INTEGER,		-- subscriber.id
 | 
			
		||||
	rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL,	-- Radio Access Technology, see enum ran_type
 | 
			
		||||
	allowed		BOOLEAN NOT NULL DEFAULT 0,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
PRAGMA user_version = 3;
 | 
			
		||||
@@ -61,6 +61,7 @@ osmo_hlr_SOURCES = \
 | 
			
		||||
	mslookup_server.c \
 | 
			
		||||
	mslookup_server_mdns.c \
 | 
			
		||||
	dgsm_vty.c \
 | 
			
		||||
	sms_over_gsup.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_hlr_LDADD = \
 | 
			
		||||
@@ -82,7 +83,7 @@ osmo_hlr_db_tool_SOURCES = \
 | 
			
		||||
	logging.c \
 | 
			
		||||
	rand_urandom.c \
 | 
			
		||||
	dbd_decode_binary.c \
 | 
			
		||||
	$(srcdir)/gsupclient/cni_peer_id.c \
 | 
			
		||||
	$(srcdir)/gsupclient/gsup_peer_id.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_hlr_db_tool_LDADD = \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										395
									
								
								src/ctrl.c
									
									
									
									
									
								
							
							
						
						
									
										395
									
								
								src/ctrl.c
									
									
									
									
									
								
							@@ -31,16 +31,12 @@
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/ctrl.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/hlr/hlr_vty.h>
 | 
			
		||||
 | 
			
		||||
#define SEL_BY "by-"
 | 
			
		||||
#define SEL_BY_IMSI SEL_BY "imsi-"
 | 
			
		||||
#define SEL_BY_MSISDN SEL_BY "msisdn-"
 | 
			
		||||
#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))
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
 | 
			
		||||
static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
int hlr_ctrl_cmds_install()
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	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;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	if (strlen(subscr.msisdn) == 0)
 | 
			
		||||
		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;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
			
		||||
@@ -749,25 +389,6 @@ static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hlr_ctrl_cmds_install()
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										83
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								src/db.c
									
									
									
									
									
								
							@@ -22,13 +22,15 @@
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <sqlite3.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/hlr/logging.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
 | 
			
		||||
#include "db_bootstrap.h"
 | 
			
		||||
 | 
			
		||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
			
		||||
#define CURRENT_SCHEMA_VERSION	6
 | 
			
		||||
#define CURRENT_SCHEMA_VERSION	8
 | 
			
		||||
 | 
			
		||||
#define SEL_COLUMNS \
 | 
			
		||||
	"id," \
 | 
			
		||||
@@ -47,18 +49,12 @@
 | 
			
		||||
	"ms_purged_ps," \
 | 
			
		||||
	"last_lu_seen," \
 | 
			
		||||
	"last_lu_seen_ps," \
 | 
			
		||||
	"last_lu_rat_cs," \
 | 
			
		||||
	"last_lu_rat_ps," \
 | 
			
		||||
	"vlr_via_proxy," \
 | 
			
		||||
	"sgsn_via_proxy"
 | 
			
		||||
 | 
			
		||||
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_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 | 
			
		||||
	[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
 | 
			
		||||
@@ -89,13 +85,23 @@ static const char *stmt_sql[] = {
 | 
			
		||||
		"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
 | 
			
		||||
		" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
 | 
			
		||||
	[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
 | 
			
		||||
	[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = 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_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'),"
 | 
			
		||||
		" last_lu_rat_cs = $rat"
 | 
			
		||||
		" WHERE id = $subscriber_id",
 | 
			
		||||
	[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch'),"
 | 
			
		||||
		" last_lu_rat_ps = $rat"
 | 
			
		||||
		" WHERE id = $subscriber_id",
 | 
			
		||||
	[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_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",
 | 
			
		||||
	[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)"
 | 
			
		||||
		" VALUES ($subscriber_id, $rat, $allowed)",
 | 
			
		||||
	[DB_STMT_RAT_BY_ID] =
 | 
			
		||||
		"SELECT rat, allowed"
 | 
			
		||||
		" FROM subscriber_rat"
 | 
			
		||||
		" WHERE subscriber_id = $subscriber_id",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
			
		||||
@@ -237,7 +243,7 @@ void db_close(struct db_context *dbc)
 | 
			
		||||
 | 
			
		||||
static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < statements_count; i++) {
 | 
			
		||||
		const char *stmt_str = statements[i];
 | 
			
		||||
@@ -341,7 +347,7 @@ static int db_upgrade_v3(struct db_context *dbc)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* A newer SQLite version would allow simply 'ATLER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'.
 | 
			
		||||
	/* A newer SQLite version would allow simply 'ALTER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'.
 | 
			
		||||
	 * This is a really expensive workaround for that in order to cover earlier SQLite versions as well:
 | 
			
		||||
	 * Create a new table with the new column name and copy the data over (https://www.sqlite.org/faq.html#q11).
 | 
			
		||||
	 */
 | 
			
		||||
@@ -513,6 +519,47 @@ static int db_upgrade_v6(struct db_context *dbc)
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int db_upgrade_v7(struct db_context *dbc)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	const char *statements[] = {
 | 
			
		||||
		"-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).\n"
 | 
			
		||||
		"-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).\n"
 | 
			
		||||
		"CREATE TABLE subscriber_rat (\n"
 | 
			
		||||
		"	subscriber_id	INTEGER,		-- subscriber.id\n"
 | 
			
		||||
		"	rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL,	-- Radio Access Technology, see enum ran_type\n"
 | 
			
		||||
		"	allowed		BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,\n"
 | 
			
		||||
		"	UNIQUE (subscriber_id, rat)\n"
 | 
			
		||||
		")",
 | 
			
		||||
		"CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat)",
 | 
			
		||||
		"PRAGMA user_version = 7",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	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 7\n");
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int db_upgrade_v8(struct db_context *dbc)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	const char *statements[] = {
 | 
			
		||||
		"ALTER TABLE subscriber ADD COLUMN last_lu_rat_cs TEXT default NULL",
 | 
			
		||||
		"ALTER TABLE subscriber ADD COLUMN last_lu_rat_ps TEXT default NULL",
 | 
			
		||||
		"PRAGMA user_version = 8",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	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 7\n");
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef int (*db_upgrade_func_t)(struct db_context *dbc);
 | 
			
		||||
static db_upgrade_func_t db_upgrade_path[] = {
 | 
			
		||||
	db_upgrade_v1,
 | 
			
		||||
@@ -521,6 +568,8 @@ static db_upgrade_func_t db_upgrade_path[] = {
 | 
			
		||||
	db_upgrade_v4,
 | 
			
		||||
	db_upgrade_v5,
 | 
			
		||||
	db_upgrade_v6,
 | 
			
		||||
	db_upgrade_v7,
 | 
			
		||||
	db_upgrade_v8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int db_get_user_version(struct db_context *dbc)
 | 
			
		||||
@@ -609,11 +658,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
			
		||||
 | 
			
		||||
	char *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",
 | 
			
		||||
			err_msg);
 | 
			
		||||
		sqlite3_free(err_msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	version = db_get_user_version(dbc);
 | 
			
		||||
	if (version < 0) {
 | 
			
		||||
@@ -651,9 +698,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
			
		||||
		if (version < CURRENT_SCHEMA_VERSION) {
 | 
			
		||||
			LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
 | 
			
		||||
			if (!allow_upgrade) {
 | 
			
		||||
				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; "
 | 
			
		||||
				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
 | 
			
		||||
				     "use the --db-upgrade option to allow HLR database upgrades\n",
 | 
			
		||||
				     CURRENT_SCHEMA_VERSION);
 | 
			
		||||
				     version, CURRENT_SCHEMA_VERSION);
 | 
			
		||||
			}
 | 
			
		||||
		} else
 | 
			
		||||
			LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										256
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								src/db_hlr.c
									
									
									
									
									
								
							@@ -37,7 +37,7 @@
 | 
			
		||||
#include <osmocom/hlr/logging.h>
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
 | 
			
		||||
#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] imsi  ASCII string of IMSI digits, is validated.
 | 
			
		||||
 * \param[in] flags  Bitmask of DB_SUBSCR_FLAG_*.
 | 
			
		||||
 * \returns 0 on success, -EINVAL on invalid IMSI, -EEXIST if subscriber with
 | 
			
		||||
 *          provided imsi already exists, -EIO on other database errors.
 | 
			
		||||
 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
 | 
			
		||||
 */
 | 
			
		||||
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) {
 | 
			
		||||
		LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
 | 
			
		||||
		       rc, sqlite3_errmsg(dbc->db));
 | 
			
		||||
		if (rc == SQLITE_CONSTRAINT_UNIQUE)
 | 
			
		||||
			return -EEXIST;
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -267,11 +264,11 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
 | 
			
		||||
		switch (aud->algo) {
 | 
			
		||||
		case OSMO_AUTH_ALG_NONE:
 | 
			
		||||
		case OSMO_AUTH_ALG_MILENAGE:
 | 
			
		||||
		case OSMO_AUTH_ALG_XOR:
 | 
			
		||||
			break;
 | 
			
		||||
		case OSMO_AUTH_ALG_COMP128v1:
 | 
			
		||||
		case OSMO_AUTH_ALG_COMP128v2:
 | 
			
		||||
		case OSMO_AUTH_ALG_COMP128v3:
 | 
			
		||||
		case OSMO_AUTH_ALG_XOR:
 | 
			
		||||
			LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
 | 
			
		||||
			     " auth algo not suited for 3G: %s\n",
 | 
			
		||||
			     osmo_auth_alg_name(aud->algo));
 | 
			
		||||
@@ -486,7 +483,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
			
		||||
	if (!subscr)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	*subscr = (struct hlr_subscriber){};
 | 
			
		||||
	*subscr = hlr_subscriber_empty;
 | 
			
		||||
 | 
			
		||||
	/* obtain the various columns */
 | 
			
		||||
	subscr->id = sqlite3_column_int64(stmt, 0);
 | 
			
		||||
@@ -508,12 +505,17 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
			
		||||
			   subscr->imsi, "CS");
 | 
			
		||||
	parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
 | 
			
		||||
			   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);
 | 
			
		||||
	copy_sqlite3_text_to_buf(subscr->last_lu_rat_cs, stmt, 16);
 | 
			
		||||
	copy_sqlite3_text_to_buf(subscr->last_lu_rat_ps, stmt, 17);
 | 
			
		||||
	copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 18);
 | 
			
		||||
	copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 19);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	db_remove_reset(stmt);
 | 
			
		||||
 | 
			
		||||
	if (ret == 0)
 | 
			
		||||
		db_subscr_get_rat_types(dbc, subscr);
 | 
			
		||||
 | 
			
		||||
	switch (ret) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		*err = NULL;
 | 
			
		||||
@@ -628,94 +630,6 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
			
		||||
	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.
 | 
			
		||||
 * \param[in,out] dbc  database context.
 | 
			
		||||
 * \param[in] id  ID of the subscriber in the HLR db.
 | 
			
		||||
@@ -828,11 +742,14 @@ out:
 | 
			
		||||
 */
 | 
			
		||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
			
		||||
		 const struct osmo_ipa_name *vlr_name, bool is_ps,
 | 
			
		||||
		 const struct osmo_ipa_name *via_proxy)
 | 
			
		||||
		 const struct osmo_ipa_name *via_proxy,
 | 
			
		||||
		 const enum osmo_rat_type rat_types[], size_t rat_types_len)
 | 
			
		||||
{
 | 
			
		||||
	sqlite3_stmt *stmt;
 | 
			
		||||
	int rc, ret = 0;
 | 
			
		||||
	struct timespec localtime;
 | 
			
		||||
	char rat_types_str[128] = "";
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
 | 
			
		||||
			       : DB_STMT_UPD_VLR_BY_ID];
 | 
			
		||||
@@ -894,6 +811,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < rat_types_len; i++) {
 | 
			
		||||
		char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str));
 | 
			
		||||
		int len = sizeof(rat_types_str) - (pos - rat_types_str);
 | 
			
		||||
		rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i]));
 | 
			
		||||
		if (rc > len) {
 | 
			
		||||
			osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!db_bind_text(stmt, "$rat", rat_types_str)) {
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = sqlite3_step(stmt);
 | 
			
		||||
	if (rc != SQLITE_DONE) {
 | 
			
		||||
		LOGP(DAUC, LOGL_ERROR,
 | 
			
		||||
@@ -1030,14 +962,14 @@ out:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
 | 
			
		||||
int _db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr,
 | 
			
		||||
	    unsigned int *ind, bool del)
 | 
			
		||||
{
 | 
			
		||||
	const char *vlr_name = NULL;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	switch (vlr->type) {
 | 
			
		||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
	case OSMO_GSUP_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));
 | 
			
		||||
@@ -1046,8 +978,8 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
 | 
			
		||||
		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));
 | 
			
		||||
		LOGP(DDB, LOGL_ERROR, "Unsupported osmo_gsup_peer_id type: %s\n",
 | 
			
		||||
		     osmo_gsup_peer_id_type_name(vlr->type));
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1069,12 +1001,128 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
 | 
			
		||||
	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)
 | 
			
		||||
int db_ind(struct db_context *dbc, const struct osmo_gsup_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)
 | 
			
		||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr)
 | 
			
		||||
{
 | 
			
		||||
	return _db_ind(dbc, vlr, NULL, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
 | 
			
		||||
 | 
			
		||||
	if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
 | 
			
		||||
	if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	/* execute the statement */
 | 
			
		||||
	rc = sqlite3_step(stmt);
 | 
			
		||||
	if (rc != SQLITE_DONE) {
 | 
			
		||||
		LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
 | 
			
		||||
		     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
			
		||||
		     sqlite3_errmsg(dbc->db));
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* verify execution result */
 | 
			
		||||
	rc = sqlite3_changes(dbc->db);
 | 
			
		||||
	if (!rc) {
 | 
			
		||||
		LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
 | 
			
		||||
		     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
			
		||||
		     subscr_id);
 | 
			
		||||
		ret = -ENOENT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	} else if (rc != 1) {
 | 
			
		||||
		LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
 | 
			
		||||
		     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
			
		||||
		     rc);
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	db_remove_reset(stmt);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
 | 
			
		||||
 | 
			
		||||
	if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OSMO_RAT_COUNT; i++)
 | 
			
		||||
		subscr->rat_types[i] = true;
 | 
			
		||||
 | 
			
		||||
	/* execute the statement */
 | 
			
		||||
	while (1) {
 | 
			
		||||
		enum osmo_rat_type rat;
 | 
			
		||||
		bool allowed;
 | 
			
		||||
 | 
			
		||||
		rc = sqlite3_step(stmt);
 | 
			
		||||
 | 
			
		||||
		if (rc == SQLITE_DONE)
 | 
			
		||||
			break;
 | 
			
		||||
		if (rc != SQLITE_ROW)
 | 
			
		||||
			return -rc;
 | 
			
		||||
 | 
			
		||||
		rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
 | 
			
		||||
		if (rc == -EINVAL) {
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		rat = rc;
 | 
			
		||||
 | 
			
		||||
		allowed = sqlite3_column_int(stmt, 1);
 | 
			
		||||
 | 
			
		||||
		subscr->rat_types[rat] = allowed;
 | 
			
		||||
		LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
 | 
			
		||||
		     subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	db_remove_reset(stmt);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
 | 
			
		||||
 | 
			
		||||
	db_subscr_get_rat_types(hlr->dbc, subscr);
 | 
			
		||||
 | 
			
		||||
	if (subscr->rat_types[rat] == allowed) {
 | 
			
		||||
		LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
 | 
			
		||||
		       allowed ? "enable" : "disable", osmo_rat_type_name(rat));
 | 
			
		||||
		return -ENOEXEC;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc > 0? -rc : rc;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: If we're disabling, send message to VLR to detach subscriber */
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,10 @@
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * 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 $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#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/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/hlr/logging.h>
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
#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>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
 | 
			
		||||
struct cmd_node mslookup_node = {
 | 
			
		||||
	MSLOOKUP_NODE,
 | 
			
		||||
@@ -119,7 +119,7 @@ DEFUN(cfg_mslookup_mdns_domain_suffix,
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mslookup_no_mdns,
 | 
			
		||||
      cfg_mslookup_no_mdns_cmd,
 | 
			
		||||
      "no mdns bind",
 | 
			
		||||
      "no mdns",
 | 
			
		||||
      NO_STR "Disable both server and client for mDNS mslookup\n")
 | 
			
		||||
{
 | 
			
		||||
	g_hlr->mslookup.server.mdns.enable = false;
 | 
			
		||||
@@ -178,9 +178,9 @@ DEFUN(cfg_mslookup_server_mdns_domain_suffix,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mslookup_server_no_mdns_bind,
 | 
			
		||||
      cfg_mslookup_server_no_mdns_bind_cmd,
 | 
			
		||||
      "no mdns bind",
 | 
			
		||||
DEFUN(cfg_mslookup_server_no_mdns,
 | 
			
		||||
      cfg_mslookup_server_no_mdns_cmd,
 | 
			
		||||
      "no mdns",
 | 
			
		||||
      NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
 | 
			
		||||
{
 | 
			
		||||
	g_hlr->mslookup.server.mdns.enable = false;
 | 
			
		||||
@@ -196,9 +196,8 @@ struct cmd_node mslookup_server_msc_node = {
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mslookup_server_msc,
 | 
			
		||||
      cfg_mslookup_server_msc_cmd,
 | 
			
		||||
      "msc ipa-name .IPA_NAME",
 | 
			
		||||
      "msc .UNIT_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;
 | 
			
		||||
@@ -366,8 +365,8 @@ DEFUN(cfg_mslookup_client_timeout,
 | 
			
		||||
		vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mslookup_client_mdns_bind,
 | 
			
		||||
      cfg_mslookup_client_mdns_bind_cmd,
 | 
			
		||||
DEFUN(cfg_mslookup_client_mdns,
 | 
			
		||||
      cfg_mslookup_client_mdns_cmd,
 | 
			
		||||
      "mdns bind [IP] [<1-65535>]",
 | 
			
		||||
      MDNS_STR
 | 
			
		||||
      "Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
 | 
			
		||||
@@ -388,9 +387,9 @@ DEFUN(cfg_mslookup_client_mdns_domain_suffix,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mslookup_client_no_mdns_bind,
 | 
			
		||||
      cfg_mslookup_client_no_mdns_bind_cmd,
 | 
			
		||||
      "no mdns bind",
 | 
			
		||||
DEFUN(cfg_mslookup_client_no_mdns,
 | 
			
		||||
      cfg_mslookup_client_no_mdns_cmd,
 | 
			
		||||
      "no mdns",
 | 
			
		||||
      NO_STR "Disable mDNS client, do not query remote services by mDNS\n")
 | 
			
		||||
{
 | 
			
		||||
	g_hlr->mslookup.client.mdns.enable = false;
 | 
			
		||||
@@ -467,7 +466,7 @@ int config_write_mslookup(struct vty *vty)
 | 
			
		||||
 | 
			
		||||
		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",
 | 
			
		||||
			vty_out(vty, "  mdns to %s %u%s",
 | 
			
		||||
				g_hlr->mslookup.client.mdns.query_addr.ip,
 | 
			
		||||
				g_hlr->mslookup.client.mdns.query_addr.port,
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
@@ -535,7 +534,7 @@ DEFUN(do_mslookup_show_services,
 | 
			
		||||
	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);
 | 
			
		||||
		vty_out(vty, "msc %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
 | 
			
		||||
		config_write_msc_services(vty, " ", msc);
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
@@ -555,7 +554,7 @@ void dgsm_vty_init(void)
 | 
			
		||||
	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_no_mdns_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);
 | 
			
		||||
@@ -570,9 +569,9 @@ void dgsm_vty_init(void)
 | 
			
		||||
	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_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_no_mdns_cmd);
 | 
			
		||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
 | 
			
		||||
	install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
@@ -67,13 +65,13 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
			
		||||
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_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)) {
 | 
			
		||||
	    && !osmo_gsup_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. */
 | 
			
		||||
@@ -82,12 +80,12 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
 | 
			
		||||
		routing = &req->source_name;
 | 
			
		||||
	}
 | 
			
		||||
	switch (routing->type) {
 | 
			
		||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
	case OSMO_GSUP_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));
 | 
			
		||||
			     osmo_gsup_peer_id_type_name(routing->type));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -113,22 +111,22 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
 | 
			
		||||
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,
 | 
			
		||||
	struct osmo_gsup_peer_id gpi = {
 | 
			
		||||
		.type = OSMO_GSUP_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);
 | 
			
		||||
	req = osmo_gsup_req_new(conn->server, &gpi, msg, gsup_server_send_req_response, conn->server, NULL);
 | 
			
		||||
	if (!req)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
 | 
			
		||||
	if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
 | 
			
		||||
		switch (req->via_proxy.type) {
 | 
			
		||||
		case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
		case OSMO_GSUP_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_peer_id_type_name(req->source_name.type));
 | 
			
		||||
			osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
@@ -139,7 +137,7 @@ struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb
 | 
			
		||||
			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_gsup_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;
 | 
			
		||||
@@ -276,7 +274,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
 | 
			
		||||
	uint8_t *addr = NULL;
 | 
			
		||||
	int addr_len;
 | 
			
		||||
	size_t addr_len;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
 | 
			
		||||
 | 
			
		||||
@@ -317,17 +315,41 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
 | 
			
		||||
	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;
 | 
			
		||||
	int val;
 | 
			
		||||
	struct osmo_gsup_conn *c;
 | 
			
		||||
	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;
 | 
			
		||||
	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));
 | 
			
		||||
	/* Is the first index, 0, unused? */
 | 
			
		||||
	if (!c || c->auc_3g_ind > 0) {
 | 
			
		||||
		conn->auc_3g_ind = 0;
 | 
			
		||||
		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 */
 | 
			
		||||
@@ -349,11 +371,10 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd)
 | 
			
		||||
 | 
			
		||||
	/* link data structure with server structure */
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	update_fd_settings(fd);
 | 
			
		||||
	LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n",
 | 
			
		||||
	     conn->conn->addr, conn->conn->port, conn->auc_3g_ind);
 | 
			
		||||
 | 
			
		||||
	/* request the identity of the client */
 | 
			
		||||
	rc = ipa_ccm_send_id_req(fd);
 | 
			
		||||
@@ -438,9 +459,10 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[out] gsup  The gsup message to populate.
 | 
			
		||||
@@ -493,7 +515,7 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
 | 
			
		||||
       return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
 | 
			
		||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
 | 
			
		||||
				    struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
@@ -502,22 +524,22 @@ int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struc
 | 
			
		||||
	 * 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) {
 | 
			
		||||
	if (req->source_name.type != OSMO_GSUP_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));
 | 
			
		||||
					  osmo_gsup_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) {
 | 
			
		||||
	if (to_peer->type != OSMO_GSUP_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));
 | 
			
		||||
					  osmo_gsup_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));
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_gsup_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;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
# This is _NOT_ the library release version, it's an API version.
 | 
			
		||||
# 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
 | 
			
		||||
LIBVERSION=1:0:1
 | 
			
		||||
LIBVERSION=0:0:0
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
 | 
			
		||||
	    $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
 | 
			
		||||
@@ -9,7 +9,7 @@ AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/incl
 | 
			
		||||
lib_LTLIBRARIES = libosmo-gsup-client.la
 | 
			
		||||
 | 
			
		||||
libosmo_gsup_client_la_SOURCES = \
 | 
			
		||||
	cni_peer_id.c \
 | 
			
		||||
	gsup_peer_id.c \
 | 
			
		||||
	gsup_client.c \
 | 
			
		||||
	gsup_req.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,6 @@
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
static void start_test_procedure(struct osmo_gsup_client *gsupc);
 | 
			
		||||
 | 
			
		||||
@@ -131,19 +129,6 @@ static void gsup_client_oap_register(struct osmo_gsup_client *gsupc)
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gsup_client *gsupc = link->data;
 | 
			
		||||
@@ -154,7 +139,6 @@ static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
 | 
			
		||||
	gsupc->is_connected = up;
 | 
			
		||||
 | 
			
		||||
	if (up) {
 | 
			
		||||
		update_fd_settings(link->ofd->fd);
 | 
			
		||||
		start_test_procedure(gsupc);
 | 
			
		||||
 | 
			
		||||
		if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,9 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
 | 
			
		||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name)
 | 
			
		||||
bool osmo_ipa_name_is_empty(struct osmo_ipa_name *ipa_name)
 | 
			
		||||
{
 | 
			
		||||
	return (!ipa_name) || (!ipa_name->len);
 | 
			
		||||
}
 | 
			
		||||
@@ -90,61 +90,55 @@ int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
 | 
			
		||||
{
 | 
			
		||||
	size_t len = ipa_name->len;
 | 
			
		||||
	if (!len)
 | 
			
		||||
		return talloc_strdup(ctx, "");
 | 
			
		||||
		return "";
 | 
			
		||||
	if (ipa_name->val[len-1] == '\0')
 | 
			
		||||
		len--;
 | 
			
		||||
	return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);
 | 
			
		||||
	return osmo_escape_str_c(OTC_SELECT, (char*)ipa_name->val, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id)
 | 
			
		||||
bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id)
 | 
			
		||||
{
 | 
			
		||||
	if (!cni_peer_id)
 | 
			
		||||
	if (!gsup_peer_id)
 | 
			
		||||
		return true;
 | 
			
		||||
	switch (cni_peer_id->type) {
 | 
			
		||||
	case OSMO_CNI_PEER_ID_EMPTY:
 | 
			
		||||
	switch (gsup_peer_id->type) {
 | 
			
		||||
	case OSMO_GSUP_PEER_ID_EMPTY:
 | 
			
		||||
		return true;
 | 
			
		||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
		return osmo_ipa_name_is_empty(&cni_peer_id->ipa_name);
 | 
			
		||||
	case OSMO_GSUP_PEER_ID_IPA_NAME:
 | 
			
		||||
		return osmo_ipa_name_is_empty(&gsup_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,
 | 
			
		||||
int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
 | 
			
		||||
			  const uint8_t *val, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	cni_peer_id->type = type;
 | 
			
		||||
	gsup_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);
 | 
			
		||||
	case OSMO_GSUP_PEER_ID_IPA_NAME:
 | 
			
		||||
		return osmo_ipa_name_set(&gsup_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,
 | 
			
		||||
int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
 | 
			
		||||
			      const char *str_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	*cni_peer_id = (struct osmo_cni_peer_id){};
 | 
			
		||||
	*gsup_peer_id = (struct osmo_gsup_peer_id){};
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
		cni_peer_id->type = OSMO_CNI_PEER_ID_IPA_NAME;
 | 
			
		||||
	case OSMO_GSUP_PEER_ID_IPA_NAME:
 | 
			
		||||
		gsup_peer_id->type = OSMO_GSUP_PEER_ID_IPA_NAME;
 | 
			
		||||
		va_start(ap, str_fmt);
 | 
			
		||||
		rc = osmo_ipa_name_set_str_va(&cni_peer_id->ipa_name, str_fmt, ap);
 | 
			
		||||
		rc = osmo_ipa_name_set_str_va(&gsup_peer_id->ipa_name, str_fmt, ap);
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
		return rc;
 | 
			
		||||
	default:
 | 
			
		||||
@@ -152,42 +146,30 @@ int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b)
 | 
			
		||||
int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_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:
 | 
			
		||||
	case OSMO_GSUP_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" },
 | 
			
		||||
const struct value_string osmo_gsup_peer_id_type_names[] = {
 | 
			
		||||
	{ OSMO_GSUP_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)
 | 
			
		||||
const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gpi)
 | 
			
		||||
{
 | 
			
		||||
	switch (cpi->type) {
 | 
			
		||||
	case OSMO_CNI_PEER_ID_IPA_NAME:
 | 
			
		||||
		return osmo_ipa_name_to_str_c(ctx, &cpi->ipa_name);
 | 
			
		||||
	switch (gpi->type) {
 | 
			
		||||
	case OSMO_GSUP_PEER_ID_IPA_NAME:
 | 
			
		||||
		return osmo_ipa_name_to_str(&gpi->ipa_name);
 | 
			
		||||
	default:
 | 
			
		||||
		return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type));
 | 
			
		||||
		return osmo_gsup_peer_id_type_name(gpi->type);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -25,81 +25,26 @@
 | 
			
		||||
#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.
 | 
			
		||||
 * When this function returns, the original sender is found in req->source_name. If this is not the immediate peer name,
 | 
			
		||||
 * 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).
 | 
			
		||||
 *
 | 
			
		||||
 * 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] msg  The GSUP message buffer.
 | 
			
		||||
 * \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().
 | 
			
		||||
 *				osmo_gsup_req_respond*() functions.
 | 
			
		||||
 * \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.
 | 
			
		||||
 * \return a newly allocated osmo_gsup_req, or NULL on error.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
 | 
			
		||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_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)
 | 
			
		||||
{
 | 
			
		||||
@@ -107,15 +52,9 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
 | 
			
		||||
	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));
 | 
			
		||||
		     osmo_gsup_peer_id_to_str(from_peer));
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
@@ -127,10 +66,11 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
 | 
			
		||||
	req->msg = msg;
 | 
			
		||||
	req->send_response_cb = send_response_cb;
 | 
			
		||||
	req->cb_data = cb_data;
 | 
			
		||||
	if (from_peer)
 | 
			
		||||
		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);
 | 
			
		||||
		LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_gsup_peer_id_to_str(from_peer), rc);
 | 
			
		||||
		osmo_gsup_req_free(req);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
@@ -138,18 +78,18 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
 | 
			
		||||
	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,
 | 
			
		||||
		if (osmo_gsup_peer_id_set(&req->source_name, OSMO_GSUP_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_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))
 | 
			
		||||
		if (osmo_gsup_peer_id_cmp(&req->source_name, from_peer))
 | 
			
		||||
			req->via_proxy = *from_peer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -164,8 +104,6 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
 | 
			
		||||
	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");
 | 
			
		||||
@@ -176,25 +114,6 @@ void osmo_gsup_req_free(struct osmo_gsup_req *req)
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
@@ -225,18 +144,6 @@ exit_cleanup:
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
@@ -247,17 +154,6 @@ int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_messag
 | 
			
		||||
				      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)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								src/hlr.c
									
									
									
									
									
								
							@@ -31,7 +31,6 @@
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/cpu_sched_vty.h>
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
#include <osmocom/gsm/apn.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
			
		||||
@@ -39,7 +38,7 @@
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
#include <osmocom/mslookup/mslookup_client.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/ctrl.h>
 | 
			
		||||
@@ -52,6 +51,7 @@
 | 
			
		||||
#include <osmocom/hlr/dgsm.h>
 | 
			
		||||
#include <osmocom/hlr/proxy.h>
 | 
			
		||||
#include <osmocom/hlr/lu_fsm.h>
 | 
			
		||||
#include <osmocom/hlr/sms_over_gsup.h>
 | 
			
		||||
#include <osmocom/mslookup/mdns.h>
 | 
			
		||||
 | 
			
		||||
struct hlr *g_hlr;
 | 
			
		||||
@@ -269,9 +269,9 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
 | 
			
		||||
	if (nam_val)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (subscr->vlr_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
 | 
			
		||||
	if (subscr->vlr_number && 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))
 | 
			
		||||
	if (subscr->sgsn_number && 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -287,7 +287,6 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
 | 
			
		||||
		.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
 | 
			
		||||
	};
 | 
			
		||||
	bool separation_bit = false;
 | 
			
		||||
	int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
 | 
			
		||||
	unsigned int auc_3g_ind;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -296,21 +295,18 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
 | 
			
		||||
	if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS)
 | 
			
		||||
		separation_bit = true;
 | 
			
		||||
 | 
			
		||||
	if (req->gsup.num_auth_vectors > 0 &&
 | 
			
		||||
			req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
 | 
			
		||||
		num_auth_vectors = req->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);
 | 
			
		||||
			     osmo_gsup_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,
 | 
			
		||||
			gsup_out.auth_vectors,
 | 
			
		||||
			num_auth_vectors,
 | 
			
		||||
			ARRAY_SIZE(gsup_out.auth_vectors),
 | 
			
		||||
			req->gsup.rand, req->gsup.auts, separation_bit);
 | 
			
		||||
 | 
			
		||||
	if (rc <= 0) {
 | 
			
		||||
@@ -324,7 +320,7 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
 | 
			
		||||
						  " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
 | 
			
		||||
			return rc;
 | 
			
		||||
		case -ENOENT:
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
 | 
			
		||||
			return rc;
 | 
			
		||||
		default:
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
 | 
			
		||||
@@ -376,7 +372,7 @@ static int rx_purge_ms_req(struct osmo_gsup_req *req)
 | 
			
		||||
	if (rc == 0)
 | 
			
		||||
		osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);
 | 
			
		||||
	else if (rc == -ENOENT)
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
 | 
			
		||||
	else
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");
 | 
			
		||||
	return rc;
 | 
			
		||||
@@ -458,8 +454,8 @@ static int read_cb_forward(struct osmo_gsup_req *req)
 | 
			
		||||
	struct osmo_ipa_name destination_name;
 | 
			
		||||
 | 
			
		||||
	/* Check for routing IEs */
 | 
			
		||||
	if (!req->gsup.source_name || !req->gsup.source_name_len
 | 
			
		||||
	    || !req->gsup.destination_name || !req->gsup.destination_name_len) {
 | 
			
		||||
	if (!req->gsup.source_name[0] || !req->gsup.source_name_len
 | 
			
		||||
	    || !req->gsup.destination_name[0] || !req->gsup.destination_name_len) {
 | 
			
		||||
		LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");
 | 
			
		||||
		goto routing_error;
 | 
			
		||||
	}
 | 
			
		||||
@@ -513,6 +509,10 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* SMS over GSUP */
 | 
			
		||||
	if (sms_over_gsup_check_handle_msg(req))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Distributed GSM: check whether to proxy for / lookup a remote HLR.
 | 
			
		||||
	 * It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
 | 
			
		||||
	 * it becomes semantically easier if we do this once-off ahead of time. */
 | 
			
		||||
@@ -586,10 +586,6 @@ static void print_help()
 | 
			
		||||
	printf("  -U --db-upgrade            Allow HLR database schema upgrades.\n");
 | 
			
		||||
	printf("  -C --db-check              Quit after opening (and upgrading) the database.\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 {
 | 
			
		||||
@@ -605,37 +601,10 @@ static struct {
 | 
			
		||||
	.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)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int option_index = 0, c;
 | 
			
		||||
		static int long_option = 0;
 | 
			
		||||
		static struct option long_options[] = {
 | 
			
		||||
			{"help", 0, 0, 'h'},
 | 
			
		||||
			{"config-file", 1, 0, 'c'},
 | 
			
		||||
@@ -648,8 +617,6 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			{"db-upgrade", 0, 0, 'U' },
 | 
			
		||||
			{"db-check", 0, 0, 'C' },
 | 
			
		||||
			{"version", 0, 0, 'V' },
 | 
			
		||||
			{"vty-ref-mode", 1, &long_option, 1},
 | 
			
		||||
			{"vty-ref-xml", 0, &long_option, 2},
 | 
			
		||||
			{0, 0, 0, 0}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@@ -659,9 +626,6 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		switch (c) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			handle_long_options(argv[0], long_option);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'h':
 | 
			
		||||
			print_usage();
 | 
			
		||||
			print_help();
 | 
			
		||||
@@ -753,6 +717,7 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	g_hlr = talloc_zero(hlr_ctx, struct hlr);
 | 
			
		||||
	INIT_LLIST_HEAD(&g_hlr->euse_list);
 | 
			
		||||
	INIT_LLIST_HEAD(&g_hlr->iuse_list);
 | 
			
		||||
	INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
			
		||||
	INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
			
		||||
	INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
 | 
			
		||||
@@ -763,6 +728,8 @@ int main(int argc, char **argv)
 | 
			
		||||
	/* Init default (call independent) SS session guard timeout value */
 | 
			
		||||
	g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	g_hlr->sms_over_gsup.try_direct_delivery = true;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		fprintf(stderr, "Error initializing logging\n");
 | 
			
		||||
@@ -775,10 +742,9 @@ int main(int argc, char **argv)
 | 
			
		||||
	osmo_stats_init(hlr_ctx);
 | 
			
		||||
	vty_init(&vty_info);
 | 
			
		||||
	ctrl_vty_init(hlr_ctx);
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
	hlr_vty_init();
 | 
			
		||||
	dgsm_vty_init();
 | 
			
		||||
	osmo_cpu_sched_vty_init(hlr_ctx);
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	rc = vty_read_config_file(cmdline_opts.config_file, NULL);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
@@ -71,9 +70,8 @@ static void print_help()
 | 
			
		||||
	printf("                             (All commands imply this if none exists yet.)\n");
 | 
			
		||||
	printf("\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("                             MSISDN, IMEI, nam_cs/ps, 2G auth data and last seen LU are set.\n");
 | 
			
		||||
	printf("                             The most recently associated IMEI from the Equipment table is used.\n");
 | 
			
		||||
	printf("                             Be aware that the import is lossy, only the\n");
 | 
			
		||||
	printf("                             IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_version(int print_copyright)
 | 
			
		||||
@@ -214,15 +212,9 @@ enum nitb_stmt {
 | 
			
		||||
 | 
			
		||||
static const char *nitb_stmt_sql[] = {
 | 
			
		||||
	[NITB_SELECT_SUBSCR] =
 | 
			
		||||
		"SELECT s.imsi, s.id, s.extension, s.authorized,"
 | 
			
		||||
		" SUBSTR(e.imei,0,15), STRFTIME('%s', s.expire_lu)"
 | 
			
		||||
		" FROM Subscriber s LEFT JOIN"
 | 
			
		||||
		" (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",
 | 
			
		||||
		"SELECT imsi, id, extension, authorized"
 | 
			
		||||
		" FROM Subscriber"
 | 
			
		||||
		" ORDER BY id",
 | 
			
		||||
	[NITB_SELECT_AUTH_KEYS] =
 | 
			
		||||
		"SELECT algorithm_id, a3a8_ki from authkeys"
 | 
			
		||||
		" WHERE subscriber_id = $subscr_id",
 | 
			
		||||
@@ -230,65 +222,8 @@ static const char *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);
 | 
			
		||||
 | 
			
		||||
/*! 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)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
@@ -362,7 +297,6 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
 | 
			
		||||
	int64_t imsi;
 | 
			
		||||
	char imsi_str[32];
 | 
			
		||||
	bool authorized;
 | 
			
		||||
	int last_lu_int;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
 | 
			
		||||
	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);
 | 
			
		||||
	/* 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, 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];
 | 
			
		||||
 | 
			
		||||
	while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
 | 
			
		||||
@@ -474,7 +387,6 @@ int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	int (*main_action)(void);
 | 
			
		||||
	int i;
 | 
			
		||||
	main_action = NULL;
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		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);
 | 
			
		||||
	log_fini();
 | 
			
		||||
	exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										281
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							@@ -252,7 +252,6 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
 | 
			
		||||
	rc = osmo_gsup_encode(msg, gsup);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -279,20 +278,20 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	resp = (struct osmo_gsup_message) {
 | 
			
		||||
	struct osmo_gsup_message resp = {
 | 
			
		||||
		.message_type = gsup_msg_type,
 | 
			
		||||
		.session_id = ss->session_id,
 | 
			
		||||
		.session_state = ss->state,
 | 
			
		||||
	};
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	if (ss_msg) {
 | 
			
		||||
		resp.ss_info = msgb_data(ss_msg);
 | 
			
		||||
		resp.ss_info_len = msgb_length(ss_msg);
 | 
			
		||||
@@ -312,8 +311,7 @@ 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,
 | 
			
		||||
		problem_tag, problem_code);
 | 
			
		||||
	OSMO_ASSERT(msg);
 | 
			
		||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
			
		||||
	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, true, msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -322,16 +320,15 @@ static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t e
 | 
			
		||||
	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);
 | 
			
		||||
	OSMO_ASSERT(msg);
 | 
			
		||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
			
		||||
	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, true, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const char *text)
 | 
			
		||||
static int ss_tx_to_ms_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);
 | 
			
		||||
	LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
@@ -347,8 +344,6 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
 | 
			
		||||
	char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
@@ -356,7 +351,7 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
 | 
			
		||||
			snprintf(buf, sizeof(buf), "You have no MSISDN!");
 | 
			
		||||
		else
 | 
			
		||||
			snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
 | 
			
		||||
		ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
 | 
			
		||||
		ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
@@ -374,22 +369,208 @@ static int handle_ussd_own_imsi(struct ss_session *ss,
 | 
			
		||||
{
 | 
			
		||||
	char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
			
		||||
	snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
 | 
			
		||||
	ss->state = OSMO_GSUP_SESSION_STATE_END;
 | 
			
		||||
	ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
 | 
			
		||||
	ss_tx_to_ms_ussd_7bit(ss, true, 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,
 | 
			
		||||
static int handle_ussd_get_ran(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;
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	char response[512];
 | 
			
		||||
	int rc;
 | 
			
		||||
	const char *rat;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		if (!*subscr.last_lu_rat_cs)
 | 
			
		||||
			rat = "nothing, you don't exist";
 | 
			
		||||
		else if (!strcmp(subscr.last_lu_rat_cs, "GERAN-A"))
 | 
			
		||||
			rat = "2G";
 | 
			
		||||
		else if (!strcmp(subscr.last_lu_rat_cs, "UTRAN-Iu"))
 | 
			
		||||
			rat = "3G";
 | 
			
		||||
		else if (!strcmp(subscr.last_lu_rat_cs, "EUTRAN-SGs"))
 | 
			
		||||
			rat = "4G";
 | 
			
		||||
		else
 | 
			
		||||
			rat = subscr.last_lu_rat_cs;
 | 
			
		||||
 | 
			
		||||
		snprintf(response, sizeof(response),
 | 
			
		||||
			 "Now on %s. Available:%s%s%s.",
 | 
			
		||||
			 rat,
 | 
			
		||||
			 subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "",
 | 
			
		||||
			 subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : "",
 | 
			
		||||
			 subscr.rat_types[OSMO_RAT_EUTRAN_SGS]? " 4G" : "");
 | 
			
		||||
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, response);
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_gsm_on(struct ss_session *ss,
 | 
			
		||||
			      const struct osmo_gsup_message *gsup,
 | 
			
		||||
			      const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, true);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Enabled GERAN-A (2G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_gsm_off(struct ss_session *ss,
 | 
			
		||||
			       const struct osmo_gsup_message *gsup,
 | 
			
		||||
			       const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, false);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Disabled GERAN-A (2G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_umts_on(struct ss_session *ss,
 | 
			
		||||
			       const struct osmo_gsup_message *gsup,
 | 
			
		||||
			       const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Enabled UTRAN-Iu (3G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_umts_off(struct ss_session *ss,
 | 
			
		||||
				const struct osmo_gsup_message *gsup,
 | 
			
		||||
				const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Disabled UTRAN-Iu (3G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_lte_on(struct ss_session *ss,
 | 
			
		||||
			       const struct osmo_gsup_message *gsup,
 | 
			
		||||
			       const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, true);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Enabled EUTRAN-SGs (4G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ussd_lte_off(struct ss_session *ss,
 | 
			
		||||
				const struct osmo_gsup_message *gsup,
 | 
			
		||||
				const struct ss_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
			
		||||
	switch (rc) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, false);
 | 
			
		||||
		rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
 | 
			
		||||
			"Disabled EUTRAN-SGs (4G)");
 | 
			
		||||
		break;
 | 
			
		||||
	case -ENOENT:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
			
		||||
		break;
 | 
			
		||||
	case -EIO:
 | 
			
		||||
	default:
 | 
			
		||||
		rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -403,8 +584,32 @@ static const struct hlr_iuse hlr_iuses[] = {
 | 
			
		||||
		.handle_ussd = handle_ussd_own_imsi,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "test-idle",
 | 
			
		||||
		.handle_ussd = handle_ussd_test_idle,
 | 
			
		||||
		.name = "get-ran",
 | 
			
		||||
		.handle_ussd = handle_ussd_get_ran,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "gsm-on",
 | 
			
		||||
		.handle_ussd = handle_ussd_gsm_on,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "gsm-off",
 | 
			
		||||
		.handle_ussd = handle_ussd_gsm_off,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "umts-on",
 | 
			
		||||
		.handle_ussd = handle_ussd_umts_on,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "umts-off",
 | 
			
		||||
		.handle_ussd = handle_ussd_umts_off,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "lte-on",
 | 
			
		||||
		.handle_ussd = handle_ussd_lte_on,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.name = "lte-off",
 | 
			
		||||
		.handle_ussd = handle_ussd_lte_off,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -439,22 +644,22 @@ static bool ss_op_is_ussd(uint8_t opcode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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 peer_name_is_euse(const struct osmo_gsup_peer_id *peer_name)
 | 
			
		||||
{
 | 
			
		||||
	if (peer_name->type != OSMO_CNI_PEER_ID_IPA_NAME)
 | 
			
		||||
	if (peer_name->type != OSMO_GSUP_PEER_ID_IPA_NAME)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (peer_name->ipa_name.len <= 5)
 | 
			
		||||
		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_name(const struct osmo_gsup_peer_id *peer_name)
 | 
			
		||||
{
 | 
			
		||||
	if (!peer_name_is_euse(peer_name))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* above peer_name_is_euse() ensures this: */
 | 
			
		||||
	OSMO_ASSERT(peer_name->type == OSMO_CNI_PEER_ID_IPA_NAME);
 | 
			
		||||
	OSMO_ASSERT(peer_name->type == OSMO_GSUP_PEER_ID_IPA_NAME);
 | 
			
		||||
 | 
			
		||||
	return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5);
 | 
			
		||||
}
 | 
			
		||||
@@ -519,9 +724,9 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
 | 
			
		||||
		} else {
 | 
			
		||||
			/* Handle internally */
 | 
			
		||||
			ss->u.iuse->handle_ussd(ss, gsup, req);
 | 
			
		||||
			/* Release session if the handler has changed its state to END */
 | 
			
		||||
			if (ss->state == OSMO_GSUP_SESSION_STATE_END)
 | 
			
		||||
			/* Release session immediately */
 | 
			
		||||
			ss_session_free(ss);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -545,10 +750,10 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
			
		||||
	LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
			
		||||
		osmo_gsup_session_state_name(gsup->session_state));
 | 
			
		||||
 | 
			
		||||
	if (gsup_req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
			
		||||
	if (gsup_req->source_name.type != OSMO_GSUP_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_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;
 | 
			
		||||
	}
 | 
			
		||||
@@ -590,7 +795,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
 | 
			
		||||
		if (!is_euse_originated) {
 | 
			
		||||
			ss->initial_req_from_ms = gsup_req;
 | 
			
		||||
			free_gsup_req = NULL;
 | 
			
		||||
			OSMO_ASSERT(gsup_req->source_name.type == OSMO_CNI_PEER_ID_IPA_NAME); /* checked above */
 | 
			
		||||
			OSMO_ASSERT(gsup_req->source_name.type == OSMO_GSUP_PEER_ID_IPA_NAME); /* checked above */
 | 
			
		||||
			ss->vlr_name = gsup_req->source_name.ipa_name;
 | 
			
		||||
		} else {
 | 
			
		||||
			ss->initial_req_from_euse = gsup_req;
 | 
			
		||||
 
 | 
			
		||||
@@ -102,8 +102,6 @@ static int config_write_hlr_gsup(struct vty *vty)
 | 
			
		||||
	vty_out(vty, " gsup%s", VTY_NEWLINE);
 | 
			
		||||
	if (g_hlr->gsup_bind_addr)
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -116,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);
 | 
			
		||||
	OSMO_ASSERT(rc);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s",
 | 
			
		||||
		name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps,
 | 
			
		||||
	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, conn->auc_3g_ind,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -176,11 +174,17 @@ DEFUN(cfg_hlr_gsup_ipa_name,
 | 
			
		||||
#define UROUTE_STR "Routing Configuration\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|get-ran|gsm-on|gsm-off|umts-on|umts-off|lte-on|lte-off)"
 | 
			
		||||
#define INT_STR "Internal USSD Handler\n" \
 | 
			
		||||
		"Respond with subscribers' own MSISDN\n" \
 | 
			
		||||
		"Respond with subscribers' own IMSI\n" \
 | 
			
		||||
		"Keep the session idle (useful for testing)\n"
 | 
			
		||||
		"Respond with available RAN types\n" \
 | 
			
		||||
		"Enable GSM service\n" \
 | 
			
		||||
		"Disable GSM service\n" \
 | 
			
		||||
		"Enable UMTS service\n" \
 | 
			
		||||
		"Disable UMTS service\n" \
 | 
			
		||||
		"Enable LTE service\n" \
 | 
			
		||||
		"Disable LTE service\n"
 | 
			
		||||
 | 
			
		||||
#define EXT_STR "External USSD Handler\n" \
 | 
			
		||||
		"Name of External USSD Handler (IPA CCM ID)\n"
 | 
			
		||||
@@ -308,7 +312,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd,
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
	if (g_hlr->euse_default == euse) {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,11 @@
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/hlr/timestamp.h>
 | 
			
		||||
#include <osmocom/hlr/hlr_vty.h>
 | 
			
		||||
 | 
			
		||||
struct vty;
 | 
			
		||||
 | 
			
		||||
@@ -39,22 +39,25 @@ struct vty;
 | 
			
		||||
 | 
			
		||||
static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
 | 
			
		||||
{
 | 
			
		||||
	struct tm tm;
 | 
			
		||||
	gmtime_r(t, &tm);
 | 
			
		||||
	strftime(buf, bufsize, "%FT%T+00:00", &tm);
 | 
			
		||||
	struct tm *tm;
 | 
			
		||||
 | 
			
		||||
	tm = gmtime(t);
 | 
			
		||||
	if (!tm)
 | 
			
		||||
		return "UNKNOWN";
 | 
			
		||||
 | 
			
		||||
	strftime(buf, bufsize, "%FT%T+00:00", tm);
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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, const char *last_lu_rat)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t age;
 | 
			
		||||
	char datebuf[32];
 | 
			
		||||
	if (!last_lu_seen)
 | 
			
		||||
		return;
 | 
			
		||||
	if (!only_age)
 | 
			
		||||
	vty_out(vty, "    last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
 | 
			
		||||
	if (!timestamp_age(&last_lu_seen, &age))
 | 
			
		||||
		vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " (invalid timestamp)");
 | 
			
		||||
	else {
 | 
			
		||||
		vty_out(vty, " (");
 | 
			
		||||
#define UNIT_AGO(UNITNAME, UNITVAL) \
 | 
			
		||||
@@ -66,17 +69,18 @@ static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t
 | 
			
		||||
		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
 | 
			
		||||
	}
 | 
			
		||||
	if (last_lu_rat && *last_lu_rat != '\0')
 | 
			
		||||
		vty_out(vty, " on %s", last_lu_rat);
 | 
			
		||||
	vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_sub_auth_data aud2g;
 | 
			
		||||
	struct osmo_sub_auth_data aud3g;
 | 
			
		||||
 | 
			
		||||
@@ -113,8 +117,12 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
			
		||||
		vty_out(vty, "    PS disabled%s", VTY_NEWLINE);
 | 
			
		||||
	if (subscr->ms_purged_ps)
 | 
			
		||||
		vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
			
		||||
	dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
 | 
			
		||||
	dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
 | 
			
		||||
	dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, subscr->last_lu_rat_cs);
 | 
			
		||||
	dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, subscr->last_lu_rat_ps);
 | 
			
		||||
	for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
 | 
			
		||||
		vty_out(vty, "    %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*subscr->imsi)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -164,28 +172,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)
 | 
			
		||||
{
 | 
			
		||||
	char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
 | 
			
		||||
@@ -213,52 +199,10 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
 | 
			
		||||
	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_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_FILTER "(imei|imsi|msisdn) FILTER"
 | 
			
		||||
 | 
			
		||||
#define SUBSCR_ID_HELP \
 | 
			
		||||
	"Identify subscriber by IMSI\n" \
 | 
			
		||||
	"Identify subscriber by MSISDN (phone number)\n" \
 | 
			
		||||
@@ -276,7 +220,7 @@ static int get_subscrs(struct vty *vty, const char *filter_type, const char *fil
 | 
			
		||||
DEFUN(subscriber_show,
 | 
			
		||||
      subscriber_show_cmd,
 | 
			
		||||
      SUBSCR "show",
 | 
			
		||||
      SUBSCR_HELP SUBSCR_SHOW_HELP)
 | 
			
		||||
      SUBSCR_HELP "Show subscriber information\n")
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	const char *id_type = argv[0];
 | 
			
		||||
@@ -291,50 +235,7 @@ DEFUN(subscriber_show,
 | 
			
		||||
 | 
			
		||||
ALIAS(subscriber_show, show_subscriber_cmd,
 | 
			
		||||
      "show " SUBSCR_CMD SUBSCR_ID,
 | 
			
		||||
      SHOW_STR SUBSCR_SHOW_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;
 | 
			
		||||
}
 | 
			
		||||
      SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
 | 
			
		||||
 | 
			
		||||
DEFUN(subscriber_create,
 | 
			
		||||
      subscriber_create_cmd,
 | 
			
		||||
@@ -471,7 +372,13 @@ static bool is_hexkey_valid(struct vty *vty, const char *label,
 | 
			
		||||
#define AUTH_ALG_TYPES_3G_HELP \
 | 
			
		||||
	"Use Milenage algorithm\n"
 | 
			
		||||
 | 
			
		||||
bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
 | 
			
		||||
			    int *minlen, int *maxlen)
 | 
			
		||||
{
 | 
			
		||||
	if (!strcasecmp(alg_str, "none")) {
 | 
			
		||||
@@ -657,55 +564,6 @@ DEFUN(subscriber_aud3g,
 | 
			
		||||
	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,
 | 
			
		||||
      subscriber_imei_cmd,
 | 
			
		||||
      SUBSCR_UPDATE "imei (none|IMEI)",
 | 
			
		||||
@@ -781,12 +639,50 @@ DEFUN(subscriber_nam,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFUN(subscriber_rat,
 | 
			
		||||
      subscriber_rat_cmd,
 | 
			
		||||
      SUBSCR_UPDATE "rat (geran-a|utran-iu|eutran-sgs) (allowed|forbidden)",
 | 
			
		||||
      SUBSCR_UPDATE_HELP
 | 
			
		||||
      "Allow or forbid specific Radio Access Types\n"
 | 
			
		||||
      "Set access to GERAN-A\n"
 | 
			
		||||
      "Set access to UTRAN-Iu\n"
 | 
			
		||||
      "Set access to EUTRAN-SGs\n"
 | 
			
		||||
      "Allow access\n"
 | 
			
		||||
      "Forbid access\n")
 | 
			
		||||
{
 | 
			
		||||
	struct hlr_subscriber subscr;
 | 
			
		||||
	const char *id_type = argv[0];
 | 
			
		||||
	const char *id = argv[1];
 | 
			
		||||
	const char *rat_str = argv[2];
 | 
			
		||||
	const char *allowed_forbidden = argv[3];
 | 
			
		||||
	enum osmo_rat_type rat = OSMO_RAT_UNKNOWN;
 | 
			
		||||
	bool allowed;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(rat_str, "geran-a") == 0)
 | 
			
		||||
		rat = OSMO_RAT_GERAN_A;
 | 
			
		||||
	else if (strcmp(rat_str, "utran-iu") == 0)
 | 
			
		||||
		rat = OSMO_RAT_UTRAN_IU;
 | 
			
		||||
	else if (strcmp(rat_str, "eutran-sgs") == 0)
 | 
			
		||||
		rat = OSMO_RAT_EUTRAN_SGS;
 | 
			
		||||
 | 
			
		||||
	allowed = (strcmp(allowed_forbidden, "allowed") == 0);
 | 
			
		||||
 | 
			
		||||
	if (get_subscr_by_argv(vty, id_type, id, &subscr))
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
 | 
			
		||||
 | 
			
		||||
	if (rc && rc != -ENOEXEC) {
 | 
			
		||||
		vty_out(vty, "%% Error: cannot set %s to %s%s",
 | 
			
		||||
			osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(&show_subscriber_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &subscriber_create_cmd);
 | 
			
		||||
@@ -796,7 +692,7 @@ void hlr_vty_subscriber_init(void)
 | 
			
		||||
	install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &subscriber_no_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_nam_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &subscriber_rat_cmd);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,6 @@ const struct log_info_cat hlr_log_info_cat[] = {
 | 
			
		||||
		.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 = {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								src/lu_fsm.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								src/lu_fsm.c
									
									
									
									
									
								
							@@ -26,7 +26,7 @@
 | 
			
		||||
#include <osmocom/gsm/apn.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_req.h>
 | 
			
		||||
#include <osmocom/hlr/logging.h>
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
@@ -52,11 +52,11 @@ struct lu {
 | 
			
		||||
	bool is_ps;
 | 
			
		||||
 | 
			
		||||
	/* VLR requesting the LU. */
 | 
			
		||||
	struct osmo_cni_peer_id vlr_name;
 | 
			
		||||
	struct osmo_gsup_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;
 | 
			
		||||
	struct osmo_gsup_peer_id via_proxy;
 | 
			
		||||
};
 | 
			
		||||
LLIST_HEAD(g_all_lu);
 | 
			
		||||
 | 
			
		||||
@@ -108,6 +108,8 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct lu *lu;
 | 
			
		||||
	bool any_rat_allowed;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(update_location_req);
 | 
			
		||||
	OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
 | 
			
		||||
@@ -130,13 +132,13 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
 | 
			
		||||
 | 
			
		||||
	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)) {
 | 
			
		||||
	if (osmo_gsup_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, GMM_CAUSE_IMSI_UNKNOWN, "Subscriber does not exist");
 | 
			
		||||
		lu_failure(lu, GMM_CAUSE_ROAMING_NOTALLOWED, "Subscriber does not exist");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -151,10 +153,32 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
 | 
			
		||||
	 * update' for the "No Suitable cells in location area" error code. */
 | 
			
		||||
	any_rat_allowed = false;
 | 
			
		||||
	for (i = 0; i < update_location_req->gsup.supported_rat_types_len; i++) {
 | 
			
		||||
		enum osmo_rat_type rat = update_location_req->gsup.supported_rat_types[i];
 | 
			
		||||
		if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
 | 
			
		||||
			lu_failure(lu, GMM_CAUSE_COND_IE_ERR, "Invalid RAT type in GSUP request: %s",
 | 
			
		||||
				   osmo_rat_type_name(rat));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (lu->subscr.rat_types[rat]) {
 | 
			
		||||
			any_rat_allowed = true;
 | 
			
		||||
			LOG_LU(lu, LOGL_DEBUG, "subscriber allowed on %s\n", osmo_rat_type_name(rat));
 | 
			
		||||
		} else {
 | 
			
		||||
			LOG_LU(lu, LOGL_DEBUG, "subscriber not allowed on %s\n", osmo_rat_type_name(rat));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!any_rat_allowed && update_location_req->gsup.supported_rat_types_len > 0) {
 | 
			
		||||
		lu_failure(lu, GMM_CAUSE_NO_SUIT_CELL_IN_LA, "subscriber not allowed on any available RAT type");
 | 
			
		||||
		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) */
 | 
			
		||||
	/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
 | 
			
		||||
	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)) {
 | 
			
		||||
@@ -163,30 +187,31 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
 | 
			
		||||
#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)) {
 | 
			
		||||
	if (!osmo_gsup_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));
 | 
			
		||||
			     osmo_gsup_peer_id_to_str(&lu->vlr_name),
 | 
			
		||||
			     osmo_gsup_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));
 | 
			
		||||
		     osmo_gsup_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)) {
 | 
			
		||||
	if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)
 | 
			
		||||
	    || (lu->vlr_name.type != OSMO_GSUP_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));
 | 
			
		||||
			   osmo_gsup_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)) {
 | 
			
		||||
	if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_GSUP_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));
 | 
			
		||||
			   osmo_gsup_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)) {
 | 
			
		||||
			 osmo_gsup_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name,
 | 
			
		||||
			 update_location_req->gsup.supported_rat_types, update_location_req->gsup.supported_rat_types_len)) {
 | 
			
		||||
		lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
 | 
			
		||||
			   lu->is_ps ? "SGSN number" : "VLR number");
 | 
			
		||||
		return;
 | 
			
		||||
 
 | 
			
		||||
@@ -213,9 +213,9 @@ struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const cha
 | 
			
		||||
 | 
			
		||||
	va_start(ap, value_fmt);
 | 
			
		||||
	value = talloc_vasprintf(ctx, value_fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	if (!value)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	r = _osmo_mdns_record_txt_encode(ctx, key, value);
 | 
			
		||||
	talloc_free(value);
 | 
			
		||||
	return r;
 | 
			
		||||
 
 | 
			
		||||
@@ -83,12 +83,7 @@ char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	while (*qname) {
 | 
			
		||||
		size_t len;
 | 
			
		||||
 | 
			
		||||
		if (i >= qname_max_len)
 | 
			
		||||
			return NULL;
 | 
			
		||||
 | 
			
		||||
		len = *qname;
 | 
			
		||||
		size_t len = *qname;
 | 
			
		||||
		next_label = qname + len + 1;
 | 
			
		||||
 | 
			
		||||
		if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		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,
 | 
			
		||||
	       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));
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		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
 | 
			
		||||
@@ -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));
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
 | 
			
		||||
		goto error_sock;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Bind and register osmo_fd callback */
 | 
			
		||||
	rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen);
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		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);
 | 
			
		||||
	if (osmo_fd_register(&ret->osmo_fd) != 0)
 | 
			
		||||
		goto error_sock;
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
error_sock:
 | 
			
		||||
	close(sock);
 | 
			
		||||
error:
 | 
			
		||||
	if (ret->ai)
 | 
			
		||||
		freeaddrinfo(ret->ai);
 | 
			
		||||
 
 | 
			
		||||
@@ -205,9 +205,9 @@ size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
 | 
			
		||||
	if (result && result->rc == OSMO_MSLOOKUP_RC_NONE)
 | 
			
		||||
		result = NULL;
 | 
			
		||||
	if (result) {
 | 
			
		||||
		if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
 | 
			
		||||
		if (result->rc != OSMO_MSLOOKUP_RC_RESULT)
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc));
 | 
			
		||||
		} else {
 | 
			
		||||
		if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
 | 
			
		||||
			if (result->host_v4.ip[0]) {
 | 
			
		||||
				OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT,
 | 
			
		||||
						   OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4));
 | 
			
		||||
@@ -260,8 +260,6 @@ static int token(char *dest, size_t dest_size, const char *start, const char *en
 | 
			
		||||
 | 
			
		||||
/*! Parse a string like "foo.moo.goo.123456789012345.msisdn" into service="foo.moo.goo", id="123456789012345" and
 | 
			
		||||
 * id_type="msisdn", placed in a struct osmo_mslookup_query.
 | 
			
		||||
 * \param q  Write parsed query to this osmo_mslookup_query.
 | 
			
		||||
 * \param domain  Human readable domain string like "sip.voice.12345678.msisdn".
 | 
			
		||||
 * \returns 0 on success, negative on error.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain)
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_
 | 
			
		||||
	if (!req) {
 | 
			
		||||
		LOGP(DMSLOOKUP, LOGL_ERROR,
 | 
			
		||||
		     "Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
 | 
			
		||||
		     request_handle);
 | 
			
		||||
		     req->request_handle);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -114,7 +114,6 @@ static void mdns_method_request(struct osmo_mslookup_client_method *method, cons
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n",
 | 
			
		||||
		     osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Send over the wire */
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		print_error("unable to encode mDNS response\n");
 | 
			
		||||
		goto exit_cleanup_sock;
 | 
			
		||||
		goto exit_cleanup;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmo_mdns_sock_send(sock, msg)) {
 | 
			
		||||
		print_error("unable to send mDNS message\n");
 | 
			
		||||
		goto exit_cleanup_sock;
 | 
			
		||||
		goto exit_cleanup;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = 0;
 | 
			
		||||
exit_cleanup_sock:
 | 
			
		||||
	osmo_mdns_sock_cleanup(sock);
 | 
			
		||||
exit_cleanup:
 | 
			
		||||
	osmo_mdns_sock_cleanup(sock);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -456,11 +455,6 @@ static int socket_read_cb(struct osmo_fd *ofd)
 | 
			
		||||
 | 
			
		||||
	rxbuf[rc] = '\0';
 | 
			
		||||
	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, '@');
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	if (flags & OSMO_FD_READ)
 | 
			
		||||
	if (flags & BSC_FD_READ)
 | 
			
		||||
		rc = socket_read_cb(ofd);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
@@ -512,7 +506,7 @@ int socket_accept(struct osmo_fd *ofd, unsigned int flags)
 | 
			
		||||
	c = talloc_zero(globals.ctx, struct socket_client);
 | 
			
		||||
	OSMO_ASSERT(c);
 | 
			
		||||
	c->ofd.fd = rc;
 | 
			
		||||
	c->ofd.when = OSMO_FD_READ;
 | 
			
		||||
	c->ofd.when = BSC_FD_READ;
 | 
			
		||||
	c->ofd.cb = socket_cb;
 | 
			
		||||
	c->ofd.data = c;
 | 
			
		||||
 | 
			
		||||
@@ -543,7 +537,7 @@ int socket_init(const char *sock_path)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofd->when = OSMO_FD_READ;
 | 
			
		||||
	ofd->when = BSC_FD_READ;
 | 
			
		||||
	ofd->cb = socket_accept;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_fd_register(ofd);
 | 
			
		||||
@@ -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) {
 | 
			
		||||
		if (!strcmp(query_str, c->query_str)) {
 | 
			
		||||
			socket_client_respond_result(c, g_buf);
 | 
			
		||||
			if (!r || r->last)
 | 
			
		||||
			if (r->last)
 | 
			
		||||
				socket_client_close(c);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!r || r->last)
 | 
			
		||||
	if (r->last)
 | 
			
		||||
		globals.requests_handled++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -261,7 +261,6 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu
 | 
			
		||||
	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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ static int osmo_mslookup_server_mdns_rx(struct osmo_fd *osmo_fd, unsigned int wh
 | 
			
		||||
 | 
			
		||||
	/* Parse the message and print it */
 | 
			
		||||
	n = read(osmo_fd->fd, buffer, sizeof(buffer));
 | 
			
		||||
	if (n <= 0)
 | 
			
		||||
	if (n < 0)
 | 
			
		||||
		return n;
 | 
			
		||||
 | 
			
		||||
	ctx = talloc_named_const(server, 0, __func__);
 | 
			
		||||
@@ -148,7 +148,7 @@ void mslookup_server_mdns_config_apply()
 | 
			
		||||
							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));
 | 
			
		||||
			     OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.running->bind_addr));
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DMSLOOKUP, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
 | 
			
		||||
			     OSMO_SOCKADDR_STR_FMT "\n",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								src/proxy.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/proxy.c
									
									
									
									
									
								
							@@ -80,7 +80,7 @@ static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_re
 | 
			
		||||
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)) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "Proxy: Failed to connect to home HLR");
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "Proxy: Failed to connect to home HLR");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -263,10 +263,10 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
 | 
			
		||||
	bool cs;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
			
		||||
	if (req->source_name.type != OSMO_GSUP_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));
 | 
			
		||||
				     osmo_gsup_peer_id_type_name(req->source_name.type));
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -303,7 +303,7 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
 | 
			
		||||
				     "%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));
 | 
			
		||||
				     osmo_gsup_peer_id_to_str(&req->source_name));
 | 
			
		||||
		break;
 | 
			
		||||
	/* TODO: delete proxy entry in case of a Purge Request? */
 | 
			
		||||
	default:
 | 
			
		||||
@@ -483,13 +483,13 @@ int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_s
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
 | 
			
		||||
	if (!osmo_gsup_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));
 | 
			
		||||
				     osmo_gsup_peer_id_to_str(&req->source_name),
 | 
			
		||||
				     osmo_gsup_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));
 | 
			
		||||
				     osmo_gsup_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.
 | 
			
		||||
 
 | 
			
		||||
@@ -233,9 +233,9 @@ void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct
 | 
			
		||||
	else
 | 
			
		||||
		forward = req->gsup;
 | 
			
		||||
 | 
			
		||||
	if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
 | 
			
		||||
	if (req->source_name.type != OSMO_GSUP_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));
 | 
			
		||||
					  osmo_gsup_peer_id_type_name(req->source_name.type));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	forward.source_name = req->source_name.ipa_name.val;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										423
									
								
								src/sms_over_gsup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										423
									
								
								src/sms_over_gsup.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,423 @@
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48_ie.h>
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm0411_utils.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_11.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mslookup/mslookup_client.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/hlr/hlr.h>
 | 
			
		||||
#include <osmocom/hlr/remote_hlr.h>
 | 
			
		||||
#include <osmocom/hlr/mslookup_server.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/hlr/sms_over_gsup.h>
 | 
			
		||||
 | 
			
		||||
static int sms_extract_destination_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	if (req->gsup.sm_rp_da_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
 | 
			
		||||
	    && req->gsup.sm_rp_da_len > 0) {
 | 
			
		||||
		LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from GSUP SM_RP_DA IE: %s\n",
 | 
			
		||||
			     osmo_hexdump(req->gsup.sm_rp_da, req->gsup.sm_rp_da_len));
 | 
			
		||||
		rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_da, req->gsup.sm_rp_da_len, 0);
 | 
			
		||||
		if (!rc)
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
 | 
			
		||||
		else
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The DA was not an MSISDN -- get from inside the SMS PDU */
 | 
			
		||||
	if (req->gsup.sm_rp_ui_len > 3) {
 | 
			
		||||
		const uint8_t *da = req->gsup.sm_rp_ui + 2;
 | 
			
		||||
		uint8_t da_len = *da;
 | 
			
		||||
		uint8_t da_len_bytes;
 | 
			
		||||
		uint8_t address_lv[12] = {};
 | 
			
		||||
 | 
			
		||||
		da_len_bytes = 2 + da_len/2 + da_len%2;
 | 
			
		||||
 | 
			
		||||
		if (da_len_bytes < 4 || da_len_bytes > 12
 | 
			
		||||
		    || da_len_bytes > req->gsup.sm_rp_ui_len - 2) {
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_ERROR, "Invalid da_len_bytes %u\n", da_len_bytes);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memcpy(address_lv, da, da_len_bytes);
 | 
			
		||||
		address_lv[0] = da_len_bytes - 1;
 | 
			
		||||
 | 
			
		||||
		LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from SMS PDU DA: %s\n",
 | 
			
		||||
			     osmo_hexdump(address_lv, da_len_bytes));
 | 
			
		||||
		rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, address_lv, da_len_bytes, 1);
 | 
			
		||||
		if (!rc)
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
 | 
			
		||||
		else
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no SM_RP_DA nor SMS PDU (sm_rp_ui_len > 3)\n");
 | 
			
		||||
	return -ENOTSUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sms_extract_sender_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	if (req->gsup.sm_rp_oa_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
 | 
			
		||||
	    && req->gsup.sm_rp_oa_len > 0) {
 | 
			
		||||
		LOG_GSUP_REQ(req, LOGL_INFO, "Extracting sender MSISDN from GSUP SM_RP_OA IE: %s\n",
 | 
			
		||||
			     osmo_hexdump(req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len));
 | 
			
		||||
		rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len, 0);
 | 
			
		||||
		if (!rc)
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
 | 
			
		||||
		else
 | 
			
		||||
			LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no MSISDN obtained from SM_RP_OA\n");
 | 
			
		||||
	return -ENOTSUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *sms_mo_pdu_to_mt_pdu(const uint8_t *mo_pdu, size_t mo_pdu_len, const char *sender_msisdn)
 | 
			
		||||
{
 | 
			
		||||
	/* Hacky shortened copy-paste of osmo-msc's gsm340_rx_tpdu() */
 | 
			
		||||
 | 
			
		||||
	uint8_t protocol_id;
 | 
			
		||||
	uint8_t data_coding_scheme;
 | 
			
		||||
	uint8_t user_data_len;
 | 
			
		||||
	uint8_t user_data_octet_len;
 | 
			
		||||
	const uint8_t *user_data;
 | 
			
		||||
	uint8_t status_rep_req;
 | 
			
		||||
	uint8_t ud_hdr_ind;
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		const uint8_t *smsp = mo_pdu;
 | 
			
		||||
		enum sms_alphabet sms_alphabet;
 | 
			
		||||
		uint8_t sms_vpf;
 | 
			
		||||
		uint8_t da_len_bytes;
 | 
			
		||||
 | 
			
		||||
		sms_vpf = (*smsp & 0x18) >> 3;
 | 
			
		||||
		status_rep_req = (*smsp & 0x20) >> 5;
 | 
			
		||||
		ud_hdr_ind = (*smsp & 0x40);
 | 
			
		||||
 | 
			
		||||
		smsp += 2;
 | 
			
		||||
 | 
			
		||||
		/* length in bytes of the destination address */
 | 
			
		||||
		da_len_bytes = 2 + *smsp/2 + *smsp%2;
 | 
			
		||||
		if (da_len_bytes < 4 || da_len_bytes > 12)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		smsp += da_len_bytes;
 | 
			
		||||
 | 
			
		||||
		protocol_id = *smsp++;
 | 
			
		||||
		data_coding_scheme = *smsp++;
 | 
			
		||||
 | 
			
		||||
		sms_alphabet = gsm338_get_sms_alphabet(data_coding_scheme);
 | 
			
		||||
		if (sms_alphabet == 0xffffffff)
 | 
			
		||||
			return NULL;
 | 
			
		||||
 | 
			
		||||
		switch (sms_vpf) {
 | 
			
		||||
		case GSM340_TP_VPF_RELATIVE:
 | 
			
		||||
			smsp++;
 | 
			
		||||
			break;
 | 
			
		||||
		case GSM340_TP_VPF_ABSOLUTE:
 | 
			
		||||
		case GSM340_TP_VPF_ENHANCED:
 | 
			
		||||
			/* the additional functionality indicator... */
 | 
			
		||||
			if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
 | 
			
		||||
				smsp++;
 | 
			
		||||
			smsp += 7;
 | 
			
		||||
			break;
 | 
			
		||||
		case GSM340_TP_VPF_NONE:
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* As per 3GPP TS 03.40, section 9.2.3.16, TP-User-Data-Length (TP-UDL)
 | 
			
		||||
		 * may indicate either the number of septets, or the number of octets,
 | 
			
		||||
		 * depending on Data Coding Scheme. We store TP-UDL value as-is,
 | 
			
		||||
		 * so this should be kept in mind to avoid buffer overruns. */
 | 
			
		||||
		user_data_len = *smsp++;
 | 
			
		||||
		user_data = smsp;
 | 
			
		||||
		if (user_data_len > 0) {
 | 
			
		||||
			if (sms_alphabet == DCS_7BIT_DEFAULT) {
 | 
			
		||||
				/* TP-UDL is indicated in septets (up to 160) */
 | 
			
		||||
				if (user_data_len > GSM340_UDL_SPT_MAX) {
 | 
			
		||||
					user_data_len = GSM340_UDL_SPT_MAX;
 | 
			
		||||
				}
 | 
			
		||||
				user_data_octet_len = gsm_get_octet_len(user_data_len);
 | 
			
		||||
			} else {
 | 
			
		||||
				/* TP-UDL is indicated in octets (up to 140) */
 | 
			
		||||
				if (user_data_len > GSM340_UDL_OCT_MAX) {
 | 
			
		||||
					user_data_len = GSM340_UDL_OCT_MAX;
 | 
			
		||||
				}
 | 
			
		||||
				user_data_octet_len = user_data_len;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		/* The following is a hacky copy pasted and shortened version of osmo-msc's gsm340_gen_sms_deliver_tpdu() */
 | 
			
		||||
		struct msgb *msg = gsm411_msgb_alloc();
 | 
			
		||||
		uint8_t *smsp;
 | 
			
		||||
		uint8_t oa[12];	/* max len per 03.40 */
 | 
			
		||||
		int oa_len;
 | 
			
		||||
 | 
			
		||||
		if (!msg)
 | 
			
		||||
			return NULL;
 | 
			
		||||
 | 
			
		||||
		/* generate first octet with masked bits */
 | 
			
		||||
		smsp = msgb_put(msg, 1);
 | 
			
		||||
		/* TP-MTI (message type indicator) */
 | 
			
		||||
		*smsp = GSM340_SMS_DELIVER_SC2MS;
 | 
			
		||||
		/* TP-MMS (more messages to send) */
 | 
			
		||||
		if (0 /* FIXME */)
 | 
			
		||||
			*smsp |= 0x04;
 | 
			
		||||
		/* TP-SRI(deliver)/SRR(submit) */
 | 
			
		||||
		if (status_rep_req)
 | 
			
		||||
			*smsp |= 0x20;
 | 
			
		||||
		/* TP-UDHI (indicating TP-UD contains a header) */
 | 
			
		||||
		if (ud_hdr_ind)
 | 
			
		||||
			*smsp |= 0x40;
 | 
			
		||||
 | 
			
		||||
		/* generate originator address */
 | 
			
		||||
		oa_len = gsm340_gen_oa(oa, sizeof(oa), 0, 0, sender_msisdn);
 | 
			
		||||
		if (oa_len < 0) {
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		smsp = msgb_put(msg, oa_len);
 | 
			
		||||
		memcpy(smsp, oa, oa_len);
 | 
			
		||||
 | 
			
		||||
		/* generate TP-PID */
 | 
			
		||||
		smsp = msgb_put(msg, 1);
 | 
			
		||||
		*smsp = protocol_id;
 | 
			
		||||
 | 
			
		||||
		/* generate TP-DCS */
 | 
			
		||||
		smsp = msgb_put(msg, 1);
 | 
			
		||||
		*smsp = data_coding_scheme;
 | 
			
		||||
 | 
			
		||||
		/* generate TP-SCTS */
 | 
			
		||||
		smsp = msgb_put(msg, 7);
 | 
			
		||||
		gsm340_gen_scts(smsp, time(NULL));
 | 
			
		||||
 | 
			
		||||
		/* generate TP-UDL */
 | 
			
		||||
		smsp = msgb_put(msg, 1);
 | 
			
		||||
		*smsp = user_data_len;
 | 
			
		||||
		smsp = msgb_put(msg, user_data_octet_len);
 | 
			
		||||
		memcpy(smsp, user_data, user_data_octet_len);
 | 
			
		||||
 | 
			
		||||
		return msg;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void sms_recipient_up_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gsup_req *req = data;
 | 
			
		||||
	struct osmo_gsup_message modified_gsup = req->gsup;
 | 
			
		||||
//	struct msgb *mt_pdu = NULL;
 | 
			
		||||
	if (!remote_hlr) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
 | 
			
		||||
					  "Failed to connect to SMS recipient: " OSMO_SOCKADDR_STR_FMT,
 | 
			
		||||
					  OSMO_SOCKADDR_STR_FMT_ARGS(addr));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/* We must not send out another MO request, to make sure we don't send the request in an infinite loop. */
 | 
			
		||||
#if 0
 | 
			
		||||
	if (req->gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
 | 
			
		||||
		char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
 | 
			
		||||
		if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
 | 
			
		||||
		if (!mt_pdu) {
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
 | 
			
		||||
						  "Cannot translate PDU to a DELIVER PDU");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
 | 
			
		||||
		modified_gsup.sm_rp_ui = mt_pdu->data;
 | 
			
		||||
		modified_gsup.sm_rp_ui_len = mt_pdu->len;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
 | 
			
		||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
 | 
			
		||||
	remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, &modified_gsup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sms_over_gsup_mt(struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	/* Find a locally connected MSC that knows this MSISDN. */
 | 
			
		||||
	uint32_t lu_age;
 | 
			
		||||
	struct osmo_gsup_peer_id local_msc_id;
 | 
			
		||||
	struct osmo_mslookup_query query = {
 | 
			
		||||
		.service = OSMO_MSLOOKUP_SERVICE_SMS_GSUP,
 | 
			
		||||
		.id = {
 | 
			
		||||
			.type = OSMO_MSLOOKUP_ID_MSISDN,
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
	struct osmo_gsup_message modified_gsup = req->gsup;
 | 
			
		||||
	struct msgb *mt_pdu = NULL;
 | 
			
		||||
 | 
			
		||||
	if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_NOTICE, "SMS to MSISDN: %s\n", query.id.msisdn);
 | 
			
		||||
 | 
			
		||||
	/* If a local attach is found, write the subscriber's IMSI to the modified_gsup buffer */
 | 
			
		||||
	if (!subscriber_has_done_lu_here(&query, &lu_age, &local_msc_id.ipa_name,
 | 
			
		||||
					 modified_gsup.imsi, sizeof(modified_gsup.imsi))) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
 | 
			
		||||
					  "SMS recipient not reachable: %s\n",
 | 
			
		||||
					  osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	local_msc_id.type = OSMO_GSUP_PEER_ID_IPA_NAME;
 | 
			
		||||
	/* A local MSC indeed has an active subscription for the recipient. Deliver there. */
 | 
			
		||||
 | 
			
		||||
	if (modified_gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
 | 
			
		||||
		/* This is a direct local delivery, and sms_over_gsup_mo_directly_to_mt() just passed the MO request
 | 
			
		||||
		 * altough here we are on the MT side. We must not send out another MO request, to make sure we don't
 | 
			
		||||
		 * send the request in an infinite loop.
 | 
			
		||||
		 * Also patch in the recipient's IMSI.
 | 
			
		||||
		 */
 | 
			
		||||
		char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
 | 
			
		||||
		if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
 | 
			
		||||
		if (!mt_pdu) {
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
 | 
			
		||||
						  "Cannot translate PDU to a DELIVER PDU");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
 | 
			
		||||
		modified_gsup.sm_rp_ui = mt_pdu->data;
 | 
			
		||||
		modified_gsup.sm_rp_ui_len = mt_pdu->len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_gsup_forward_to_local_peer(g_hlr->gs, &local_msc_id, req, &modified_gsup);
 | 
			
		||||
 | 
			
		||||
	if (mt_pdu)
 | 
			
		||||
		msgb_free(mt_pdu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void resolve_sms_recipient_cb(struct osmo_mslookup_client *client,
 | 
			
		||||
				     uint32_t request_handle,
 | 
			
		||||
				     const struct osmo_mslookup_query *query,
 | 
			
		||||
				     const struct osmo_mslookup_result *result)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_gsup_req *req = query->priv;
 | 
			
		||||
	const struct osmo_sockaddr_str *remote_hlr_addr = NULL;
 | 
			
		||||
	const struct mslookup_service_host *local_gsup;
 | 
			
		||||
 | 
			
		||||
	if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!remote_hlr_addr) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
 | 
			
		||||
					  "Failed to resolve SMS recipient: %s\n",
 | 
			
		||||
					  osmo_mslookup_result_name_c(OTC_SELECT, query, result));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Possibly, this HLR here has responded to itself via mslookup. Don't make a GSUP connection to ourselves,
 | 
			
		||||
	 * instead go directly to the MT path. */
 | 
			
		||||
	local_gsup = mslookup_server_get_local_gsup_addr();
 | 
			
		||||
	LOG_GSUP_REQ(req, LOGL_NOTICE, "local_gsup " OSMO_SOCKADDR_STR_FMT " " OSMO_SOCKADDR_STR_FMT
 | 
			
		||||
		     "  remote_hlr_addr " OSMO_SOCKADDR_STR_FMT "\n",
 | 
			
		||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v4),
 | 
			
		||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v6),
 | 
			
		||||
		     OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr));
 | 
			
		||||
	if (local_gsup
 | 
			
		||||
	    && (!osmo_sockaddr_str_cmp(&local_gsup->host_v4, remote_hlr_addr)
 | 
			
		||||
		|| !osmo_sockaddr_str_cmp(&local_gsup->host_v6, remote_hlr_addr))) {
 | 
			
		||||
		sms_over_gsup_mt(req);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	remote_hlr_get_or_connect(remote_hlr_addr, true, sms_recipient_up_cb, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sms_over_gsup_mo_directly_to_mt(struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	/* Figure out the location of the SMS recipient by mslookup */
 | 
			
		||||
	if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
 | 
			
		||||
		/* D-GSM is active. Kick off an mslookup for the current location of the MSISDN. */
 | 
			
		||||
		uint32_t request_handle;
 | 
			
		||||
		struct osmo_mslookup_query_handling handling = {
 | 
			
		||||
			.min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
 | 
			
		||||
			.result_cb = resolve_sms_recipient_cb,
 | 
			
		||||
		};
 | 
			
		||||
		struct osmo_mslookup_query query = {
 | 
			
		||||
			.id = {
 | 
			
		||||
				.type = OSMO_MSLOOKUP_ID_MSISDN,
 | 
			
		||||
			},
 | 
			
		||||
			.priv = req,
 | 
			
		||||
		};
 | 
			
		||||
		if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)
 | 
			
		||||
		    || !osmo_msisdn_str_valid(query.id.msisdn)) {
 | 
			
		||||
			osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_SMS_GSUP);
 | 
			
		||||
 | 
			
		||||
		request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
 | 
			
		||||
		if (request_handle) {
 | 
			
		||||
			/* Querying succeeded. Wait for resolve_sms_recipient_cb() to be called. */
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		/* Querying failed. Try whether delivering to a locally connected MSC works out. */
 | 
			
		||||
		LOG_DGSM(req->gsup.imsi, LOGL_ERROR,
 | 
			
		||||
			 "Error dispatching mslookup query for SMS: %s -- trying local delivery\n",
 | 
			
		||||
			 osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Attempt direct delivery */
 | 
			
		||||
	sms_over_gsup_mt(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sms_over_gsup_mo(struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	if (!osmo_gsup_peer_id_is_empty(&g_hlr->sms_over_gsup.smsc)) {
 | 
			
		||||
		/* Forward to SMSC */
 | 
			
		||||
		/* FIXME actually use branch fixeria/sms for this */
 | 
			
		||||
		osmo_gsup_forward_to_local_peer(g_hlr->gs, &g_hlr->sms_over_gsup.smsc, req, NULL);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!g_hlr->sms_over_gsup.try_direct_delivery) {
 | 
			
		||||
		osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC,
 | 
			
		||||
					  "cannot deliver SMS over GSUP: No SMSC (and direct delivery disabled)");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sms_over_gsup_mo_directly_to_mt(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req)
 | 
			
		||||
{
 | 
			
		||||
	switch (req->gsup.message_type) {
 | 
			
		||||
	case OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST:
 | 
			
		||||
		sms_over_gsup_mo(req);
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
 | 
			
		||||
		sms_over_gsup_mt(req);
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	auc \
 | 
			
		||||
	gsup_server \
 | 
			
		||||
	db \
 | 
			
		||||
	gsup \
 | 
			
		||||
	db_upgrade \
 | 
			
		||||
@@ -69,9 +70,6 @@ vty-test:
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
# pass -u to ctrl_script_runner.py by doing:
 | 
			
		||||
#   make ctrl-test U=-u
 | 
			
		||||
@@ -82,7 +80,7 @@ ctrl-test:
 | 
			
		||||
	osmo_verify_transcript_ctrl.py -v \
 | 
			
		||||
		-p 4259 \
 | 
			
		||||
		-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 $(CTRL_TEST_DB)-*
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,9 @@ EXTRA_DIST = \
 | 
			
		||||
	auc_ts_55_205_test_sets.err \
 | 
			
		||||
	$(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.c \
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,6 @@ int rand_get(uint8_t *rand, unsigned int len)
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Subscriber with 2G-only (COMP128v1) authentication data */
 | 
			
		||||
static void test_gen_vectors_2g_only(void)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sub_auth_data aud2g;
 | 
			
		||||
@@ -175,8 +174,6 @@ static void test_gen_vectors_2g_only(void)
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sub_auth_data aud2g;
 | 
			
		||||
@@ -460,55 +454,6 @@ static void test_gen_vectors_3g_only(void)
 | 
			
		||||
	comment_end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Subscriber with only 3G (XOR) authentication data,
 | 
			
		||||
 * 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()
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_auth_vector vec;
 | 
			
		||||
@@ -668,20 +613,15 @@ int main(int argc, char **argv)
 | 
			
		||||
	void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(tall_ctx, &hlr_log_info);
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target,
 | 
			
		||||
				cmdline_opts.verbose ?
 | 
			
		||||
					LOG_FILENAME_BASENAME :
 | 
			
		||||
					LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
 | 
			
		||||
	log_set_print_timestamp(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_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
			
		||||
 | 
			
		||||
	test_gen_vectors_2g_only();
 | 
			
		||||
	test_gen_vectors_2g_plus_3g();
 | 
			
		||||
	test_gen_vectors_3g_only();
 | 
			
		||||
	test_gen_vectors_3g_xor();
 | 
			
		||||
	test_gen_vectors_bad_args();
 | 
			
		||||
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -217,29 +217,6 @@ DAUC vector [2]: auth_types = 0x3
 | 
			
		||||
===== 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
 | 
			
		||||
 | 
			
		||||
- no auth data (a)
 | 
			
		||||
 
 | 
			
		||||
@@ -106,10 +106,9 @@ int main()
 | 
			
		||||
	void *tall_ctx = talloc_named_const(NULL, 1, "test");
 | 
			
		||||
	msgb_talloc_ctx_init(tall_ctx, 0);
 | 
			
		||||
	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_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ db_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/db_auc.o \
 | 
			
		||||
	$(top_builddir)/src/db_hlr.o \
 | 
			
		||||
	$(top_builddir)/src/db.o \
 | 
			
		||||
	$(top_builddir)/src/cni_peer_id.o \
 | 
			
		||||
	$(top_builddir)/src/gsup_peer_id.o \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsupclient/cni_peer_id.h>
 | 
			
		||||
#include <osmocom/gsupclient/gsup_peer_id.h>
 | 
			
		||||
#include <osmocom/hlr/db.h>
 | 
			
		||||
#include <osmocom/hlr/logging.h>
 | 
			
		||||
 | 
			
		||||
@@ -173,6 +173,8 @@ void dump_subscr(struct hlr_subscriber *subscr)
 | 
			
		||||
		Pfo(lmsi, "0x%x", subscr);
 | 
			
		||||
	Pb(true, ms_purged_cs);
 | 
			
		||||
	Pb(true, ms_purged_ps);
 | 
			
		||||
	Ps(last_lu_rat_cs);
 | 
			
		||||
	Ps(last_lu_rat_ps);
 | 
			
		||||
	fprintf(stderr, "}\n");
 | 
			
		||||
#undef Ps
 | 
			
		||||
#undef Pd
 | 
			
		||||
@@ -243,7 +245,7 @@ static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
	return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_subscr_create_update_sel_delete()
 | 
			
		||||
@@ -262,13 +264,13 @@ static void test_subscr_create_update_sel_delete()
 | 
			
		||||
	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
			
		||||
	ASSERT_SEL(imsi, imsi2, 0);
 | 
			
		||||
	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_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), -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), -EIO);
 | 
			
		||||
	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), -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), -EIO);
 | 
			
		||||
	ASSERT_SEL(imsi, imsi2, 0);
 | 
			
		||||
 | 
			
		||||
	ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
			
		||||
@@ -924,16 +926,16 @@ static void test_ind()
 | 
			
		||||
 | 
			
		||||
#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)); \
 | 
			
		||||
		struct osmo_gsup_peer_id vlr; \
 | 
			
		||||
		OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_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)); \
 | 
			
		||||
		struct osmo_gsup_peer_id vlr; \
 | 
			
		||||
		OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_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)
 | 
			
		||||
@@ -1024,13 +1026,9 @@ int main(int argc, char **argv)
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(ctx, &hlr_log_info);
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target,
 | 
			
		||||
				cmdline_opts.verbose ?
 | 
			
		||||
					LOG_FILENAME_BASENAME :
 | 
			
		||||
					LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
 | 
			
		||||
	log_set_print_timestamp(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_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ struct hlr_subscriber {
 | 
			
		||||
  .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
 | 
			
		||||
 | 
			
		||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
			
		||||
@@ -36,10 +36,10 @@ struct hlr_subscriber {
 | 
			
		||||
  .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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
			
		||||
@@ -48,10 +48,10 @@ struct hlr_subscriber {
 | 
			
		||||
  .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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,9 @@ OsmoHLR# subscriber imsi 123456789012345 create
 | 
			
		||||
    ID: 1
 | 
			
		||||
    IMSI: 123456789012345
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 123456789012345 update msisdn 098765432109876
 | 
			
		||||
% Updated subscriber IMSI='123456789012345' to MSISDN='098765432109876'
 | 
			
		||||
OsmoHLR# subscriber imsi 123456789012345 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
 | 
			
		||||
@@ -13,11 +16,17 @@ OsmoHLR# subscriber imsi 111111111 create
 | 
			
		||||
    ID: 2
 | 
			
		||||
    IMSI: 111111111
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 222222222 create
 | 
			
		||||
% Created subscriber 222222222
 | 
			
		||||
    ID: 3
 | 
			
		||||
    IMSI: 222222222
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 222222222 update msisdn 22222
 | 
			
		||||
% Updated subscriber IMSI='222222222' to MSISDN='22222'
 | 
			
		||||
OsmoHLR# subscriber imsi 333333 create
 | 
			
		||||
@@ -25,6 +34,9 @@ OsmoHLR# subscriber imsi 333333 create
 | 
			
		||||
    ID: 4
 | 
			
		||||
    IMSI: 333333
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 333333 update msisdn 3
 | 
			
		||||
% Updated subscriber IMSI='333333' to MSISDN='3'
 | 
			
		||||
OsmoHLR# subscriber imsi 333333 update aud2g comp128v2 ki 33333333333333333333333333333333
 | 
			
		||||
@@ -33,6 +45,9 @@ OsmoHLR# subscriber imsi 444444444444444 create
 | 
			
		||||
    ID: 5
 | 
			
		||||
    IMSI: 444444444444444
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 444444444444444 update msisdn 4444
 | 
			
		||||
% Updated subscriber IMSI='444444444444444' to MSISDN='4444'
 | 
			
		||||
OsmoHLR# subscriber imsi 444444444444444 update aud3g milenage k 44444444444444444444444444444444 op 44444444444444444444444444444444
 | 
			
		||||
@@ -41,6 +56,9 @@ OsmoHLR# subscriber imsi 5555555 create
 | 
			
		||||
    ID: 6
 | 
			
		||||
    IMSI: 5555555
 | 
			
		||||
    MSISDN: none
 | 
			
		||||
    GERAN-A: allowed
 | 
			
		||||
    UTRAN-Iu: allowed
 | 
			
		||||
    EUTRAN-SGs: allowed
 | 
			
		||||
OsmoHLR# subscriber imsi 5555555 update msisdn 55555555555555
 | 
			
		||||
% Updated subscriber IMSI='5555555' to MSISDN='55555555555555'
 | 
			
		||||
OsmoHLR# subscriber imsi 5555555 update aud2g xor ki 55555555555555555555555555555555
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,8 @@ 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 5
 | 
			
		||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6
 | 
			
		||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 7
 | 
			
		||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 8
 | 
			
		||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
			
		||||
 | 
			
		||||
Resulting db:
 | 
			
		||||
@@ -133,6 +135,8 @@ id|INTEGER|0||1
 | 
			
		||||
imei|VARCHAR(14)|0||0
 | 
			
		||||
imeisv|VARCHAR|0||0
 | 
			
		||||
imsi|VARCHAR(15)|1||0
 | 
			
		||||
last_lu_rat_cs|TEXT|0|NULL|0
 | 
			
		||||
last_lu_rat_ps|TEXT|0|NULL|0
 | 
			
		||||
last_lu_seen|TIMESTAMP|0|NULL|0
 | 
			
		||||
last_lu_seen_ps|TIMESTAMP|0|NULL|0
 | 
			
		||||
lmsi|INTEGER|0||0
 | 
			
		||||
@@ -152,13 +156,13 @@ vlr_number|VARCHAR(15)|0||0
 | 
			
		||||
vlr_via_proxy|VARCHAR|0||0
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
||1|||123456789012345||||0|0||098765432109876|1|1|||||||MSC-1|
 | 
			
		||||
||2|||111111111||||1|0|||1|1||||||||
 | 
			
		||||
||3|||222222222||||0|1||22222|1|1||||||||
 | 
			
		||||
||4|||333333||||0|0||3|0|1||||||||
 | 
			
		||||
||5|||444444444444444||||0|0||4444|1|0||||||||
 | 
			
		||||
||6|||5555555||||0|0||55555555555555|0|0||||||||
 | 
			
		||||
ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_rat_cs|last_lu_rat_ps|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
 | 
			
		||||
||1|||123456789012345||||||0|0||098765432109876|1|1|||||||MSC-1|
 | 
			
		||||
||2|||111111111||||||1|0|||1|1||||||||
 | 
			
		||||
||3|||222222222||||||0|1||22222|1|1||||||||
 | 
			
		||||
||4|||333333||||||0|0||3|0|1||||||||
 | 
			
		||||
||5|||444444444444444||||||0|0||4444|1|0||||||||
 | 
			
		||||
||6|||5555555||||||0|0||55555555555555|0|0||||||||
 | 
			
		||||
 | 
			
		||||
Table: subscriber_apn
 | 
			
		||||
name|type|notnull|dflt_value|pk
 | 
			
		||||
@@ -174,10 +178,18 @@ subscriber_id|INTEGER|0||0
 | 
			
		||||
 | 
			
		||||
Table subscriber_multi_msisdn contents:
 | 
			
		||||
 | 
			
		||||
Table: subscriber_rat
 | 
			
		||||
name|type|notnull|dflt_value|pk
 | 
			
		||||
allowed|BOOLEAN|1|0|0
 | 
			
		||||
rat|TEXT|1||0
 | 
			
		||||
subscriber_id|INTEGER|0||0
 | 
			
		||||
 | 
			
		||||
Table subscriber_rat contents:
 | 
			
		||||
 | 
			
		||||
Verify that osmo-hlr can open it:
 | 
			
		||||
osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg
 | 
			
		||||
rc = 0
 | 
			
		||||
DMAIN hlr starting
 | 
			
		||||
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 8
 | 
			
		||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ EXTRA_DIST = \
 | 
			
		||||
	gsup_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	gsup_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, "gsup_test");
 | 
			
		||||
	osmo_init_logging2(ctx, &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_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								tests/gsup_server/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tests/gsup_server/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	gsup_server_test.ok \
 | 
			
		||||
	gsup_server_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	gsup_server_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gsup_server_test_SOURCES = \
 | 
			
		||||
	gsup_server_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gsup_server_test_LDADD = \
 | 
			
		||||
	$(top_srcdir)/src/gsup_server.c \
 | 
			
		||||
	$(top_srcdir)/src/gsup_router.c \
 | 
			
		||||
	$(top_srcdir)/src/gsup_send.c \
 | 
			
		||||
	$(top_srcdir)/src/gsupclient/gsup_peer_id.c \
 | 
			
		||||
	$(top_srcdir)/src/gsupclient/gsup_req.c \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
.PHONY: update_exp
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/gsup_server_test >"$(srcdir)/gsup_server_test.ok" 2>"$(srcdir)/gsup_server_test.err"
 | 
			
		||||
							
								
								
									
										145
									
								
								tests/gsup_server/gsup_server_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								tests/gsup_server/gsup_server_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU 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 <stdio.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/hlr/gsup_server.h>
 | 
			
		||||
 | 
			
		||||
#define comment_start() printf("\n===== %s\n", __func__)
 | 
			
		||||
#define comment_end() printf("===== %s: SUCCESS\n\n", __func__)
 | 
			
		||||
#define btw(fmt, args...) printf("\n" fmt "\n", ## args)
 | 
			
		||||
 | 
			
		||||
#define VERBOSE_ASSERT(val, expect_op, fmt) \
 | 
			
		||||
	do { \
 | 
			
		||||
		printf(#val " == " fmt "\n", (val)); \
 | 
			
		||||
		OSMO_ASSERT((val) expect_op); \
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
void osmo_gsup_server_add_conn(struct llist_head *clients,
 | 
			
		||||
			       struct osmo_gsup_conn *conn);
 | 
			
		||||
 | 
			
		||||
static void test_add_conn(void)
 | 
			
		||||
{
 | 
			
		||||
	struct llist_head _list;
 | 
			
		||||
	struct llist_head *clients = &_list;
 | 
			
		||||
	struct osmo_gsup_conn conn_inst[23] = {};
 | 
			
		||||
	struct osmo_gsup_conn *conn;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	comment_start();
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(clients);
 | 
			
		||||
 | 
			
		||||
	btw("Add 10 items");
 | 
			
		||||
	for (i = 0; i < 10; i++) {
 | 
			
		||||
		osmo_gsup_server_add_conn(clients, &conn_inst[i]);
 | 
			
		||||
		printf("conn_inst[%u].auc_3g_ind == %u\n", i, conn_inst[i].auc_3g_ind);
 | 
			
		||||
		OSMO_ASSERT(clients->next == &conn_inst[0].list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("Expecting a list of 0..9");
 | 
			
		||||
	i = 0;
 | 
			
		||||
	llist_for_each_entry(conn, clients, list) {
 | 
			
		||||
		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
 | 
			
		||||
		OSMO_ASSERT(conn->auc_3g_ind == i);
 | 
			
		||||
		OSMO_ASSERT(conn == &conn_inst[i]);
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("Punch two holes in the sequence in arbitrary order,"
 | 
			
		||||
	    " a larger one from 2..4 and a single one at 7.");
 | 
			
		||||
	llist_del(&conn_inst[4].list);
 | 
			
		||||
	llist_del(&conn_inst[2].list);
 | 
			
		||||
	llist_del(&conn_inst[3].list);
 | 
			
		||||
	llist_del(&conn_inst[7].list);
 | 
			
		||||
 | 
			
		||||
	btw("Expecting a list of 0,1, 5,6, 8,9");
 | 
			
		||||
	i = 0;
 | 
			
		||||
	llist_for_each_entry(conn, clients, list) {
 | 
			
		||||
		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("Add conns, expecting them to take the open slots");
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[12]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[12].auc_3g_ind, == 2, "%u");
 | 
			
		||||
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[13]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[13].auc_3g_ind, == 3, "%u");
 | 
			
		||||
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[14]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[14].auc_3g_ind, == 4, "%u");
 | 
			
		||||
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[17]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[17].auc_3g_ind, == 7, "%u");
 | 
			
		||||
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[18]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[18].auc_3g_ind, == 10, "%u");
 | 
			
		||||
 | 
			
		||||
	btw("Expecting a list of 0..10");
 | 
			
		||||
	i = 0;
 | 
			
		||||
	llist_for_each_entry(conn, clients, list) {
 | 
			
		||||
		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
 | 
			
		||||
		OSMO_ASSERT(conn->auc_3g_ind == i);
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("Does it also work for the first item?");
 | 
			
		||||
	llist_del(&conn_inst[0].list);
 | 
			
		||||
 | 
			
		||||
	btw("Expecting a list of 1..10");
 | 
			
		||||
	i = 0;
 | 
			
		||||
	llist_for_each_entry(conn, clients, list) {
 | 
			
		||||
		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
 | 
			
		||||
		OSMO_ASSERT(conn->auc_3g_ind == i + 1);
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("Add another conn, should take auc_3g_ind == 0");
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[20]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[20].auc_3g_ind, == 0, "%u");
 | 
			
		||||
 | 
			
		||||
	btw("Expecting a list of 0..10");
 | 
			
		||||
	i = 0;
 | 
			
		||||
	llist_for_each_entry(conn, clients, list) {
 | 
			
		||||
		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
 | 
			
		||||
		OSMO_ASSERT(conn->auc_3g_ind == i);
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	btw("If a client reconnects, it will (likely) get the same auc_3g_ind");
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
 | 
			
		||||
	llist_del(&conn_inst[5].list);
 | 
			
		||||
	conn_inst[5].auc_3g_ind = 423;
 | 
			
		||||
	osmo_gsup_server_add_conn(clients, &conn_inst[5]);
 | 
			
		||||
	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
 | 
			
		||||
 | 
			
		||||
	comment_end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	printf("test_gsup_server.c\n");
 | 
			
		||||
 | 
			
		||||
	test_add_conn();
 | 
			
		||||
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/gsup_server/gsup_server_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/gsup_server/gsup_server_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										94
									
								
								tests/gsup_server/gsup_server_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								tests/gsup_server/gsup_server_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
test_gsup_server.c
 | 
			
		||||
 | 
			
		||||
===== test_add_conn
 | 
			
		||||
 | 
			
		||||
Add 10 items
 | 
			
		||||
conn_inst[0].auc_3g_ind == 0
 | 
			
		||||
conn_inst[1].auc_3g_ind == 1
 | 
			
		||||
conn_inst[2].auc_3g_ind == 2
 | 
			
		||||
conn_inst[3].auc_3g_ind == 3
 | 
			
		||||
conn_inst[4].auc_3g_ind == 4
 | 
			
		||||
conn_inst[5].auc_3g_ind == 5
 | 
			
		||||
conn_inst[6].auc_3g_ind == 6
 | 
			
		||||
conn_inst[7].auc_3g_ind == 7
 | 
			
		||||
conn_inst[8].auc_3g_ind == 8
 | 
			
		||||
conn_inst[9].auc_3g_ind == 9
 | 
			
		||||
 | 
			
		||||
Expecting a list of 0..9
 | 
			
		||||
conn[0].auc_3g_ind == 0
 | 
			
		||||
conn[1].auc_3g_ind == 1
 | 
			
		||||
conn[2].auc_3g_ind == 2
 | 
			
		||||
conn[3].auc_3g_ind == 3
 | 
			
		||||
conn[4].auc_3g_ind == 4
 | 
			
		||||
conn[5].auc_3g_ind == 5
 | 
			
		||||
conn[6].auc_3g_ind == 6
 | 
			
		||||
conn[7].auc_3g_ind == 7
 | 
			
		||||
conn[8].auc_3g_ind == 8
 | 
			
		||||
conn[9].auc_3g_ind == 9
 | 
			
		||||
 | 
			
		||||
Punch two holes in the sequence in arbitrary order, a larger one from 2..4 and a single one at 7.
 | 
			
		||||
 | 
			
		||||
Expecting a list of 0,1, 5,6, 8,9
 | 
			
		||||
conn[0].auc_3g_ind == 0
 | 
			
		||||
conn[1].auc_3g_ind == 1
 | 
			
		||||
conn[2].auc_3g_ind == 5
 | 
			
		||||
conn[3].auc_3g_ind == 6
 | 
			
		||||
conn[4].auc_3g_ind == 8
 | 
			
		||||
conn[5].auc_3g_ind == 9
 | 
			
		||||
 | 
			
		||||
Add conns, expecting them to take the open slots
 | 
			
		||||
conn_inst[12].auc_3g_ind == 2
 | 
			
		||||
conn_inst[13].auc_3g_ind == 3
 | 
			
		||||
conn_inst[14].auc_3g_ind == 4
 | 
			
		||||
conn_inst[17].auc_3g_ind == 7
 | 
			
		||||
conn_inst[18].auc_3g_ind == 10
 | 
			
		||||
 | 
			
		||||
Expecting a list of 0..10
 | 
			
		||||
conn[0].auc_3g_ind == 0
 | 
			
		||||
conn[1].auc_3g_ind == 1
 | 
			
		||||
conn[2].auc_3g_ind == 2
 | 
			
		||||
conn[3].auc_3g_ind == 3
 | 
			
		||||
conn[4].auc_3g_ind == 4
 | 
			
		||||
conn[5].auc_3g_ind == 5
 | 
			
		||||
conn[6].auc_3g_ind == 6
 | 
			
		||||
conn[7].auc_3g_ind == 7
 | 
			
		||||
conn[8].auc_3g_ind == 8
 | 
			
		||||
conn[9].auc_3g_ind == 9
 | 
			
		||||
conn[10].auc_3g_ind == 10
 | 
			
		||||
 | 
			
		||||
Does it also work for the first item?
 | 
			
		||||
 | 
			
		||||
Expecting a list of 1..10
 | 
			
		||||
conn[0].auc_3g_ind == 1
 | 
			
		||||
conn[1].auc_3g_ind == 2
 | 
			
		||||
conn[2].auc_3g_ind == 3
 | 
			
		||||
conn[3].auc_3g_ind == 4
 | 
			
		||||
conn[4].auc_3g_ind == 5
 | 
			
		||||
conn[5].auc_3g_ind == 6
 | 
			
		||||
conn[6].auc_3g_ind == 7
 | 
			
		||||
conn[7].auc_3g_ind == 8
 | 
			
		||||
conn[8].auc_3g_ind == 9
 | 
			
		||||
conn[9].auc_3g_ind == 10
 | 
			
		||||
 | 
			
		||||
Add another conn, should take auc_3g_ind == 0
 | 
			
		||||
conn_inst[20].auc_3g_ind == 0
 | 
			
		||||
 | 
			
		||||
Expecting a list of 0..10
 | 
			
		||||
conn[0].auc_3g_ind == 0
 | 
			
		||||
conn[1].auc_3g_ind == 1
 | 
			
		||||
conn[2].auc_3g_ind == 2
 | 
			
		||||
conn[3].auc_3g_ind == 3
 | 
			
		||||
conn[4].auc_3g_ind == 4
 | 
			
		||||
conn[5].auc_3g_ind == 5
 | 
			
		||||
conn[6].auc_3g_ind == 6
 | 
			
		||||
conn[7].auc_3g_ind == 7
 | 
			
		||||
conn[8].auc_3g_ind == 8
 | 
			
		||||
conn[9].auc_3g_ind == 9
 | 
			
		||||
conn[10].auc_3g_ind == 10
 | 
			
		||||
 | 
			
		||||
If a client reconnects, it will (likely) get the same auc_3g_ind
 | 
			
		||||
conn_inst[5].auc_3g_ind == 5
 | 
			
		||||
conn_inst[5].auc_3g_ind == 5
 | 
			
		||||
===== test_add_conn: SUCCESS
 | 
			
		||||
 | 
			
		||||
Done
 | 
			
		||||
@@ -24,6 +24,7 @@ EXTRA_DIST = \
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
	mdns_test \
 | 
			
		||||
	mslookup_client_mdns_test \
 | 
			
		||||
	mslookup_client_test \
 | 
			
		||||
	mslookup_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -44,6 +45,14 @@ mslookup_client_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mslookup_client_mdns_test_SOURCES = \
 | 
			
		||||
	mslookup_client_mdns_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
mslookup_client_mdns_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/mslookup/libosmo-mslookup.la \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mdns_test_SOURCES = \
 | 
			
		||||
	mdns_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -52,18 +61,6 @@ mdns_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
if ENABLE_MSLOOKUP_CLIENT_MDNS_TEST
 | 
			
		||||
check_PROGRAMS += mslookup_client_mdns_test
 | 
			
		||||
 | 
			
		||||
mslookup_client_mdns_test_SOURCES = \
 | 
			
		||||
	mslookup_client_mdns_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
mslookup_client_mdns_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/mslookup/libosmo-mslookup.la \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
.PHONY: update_exp
 | 
			
		||||
update_exp:
 | 
			
		||||
	for i in $(check_PROGRAMS); do \
 | 
			
		||||
 
 | 
			
		||||
@@ -583,7 +583,7 @@ int main()
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 0, "main");
 | 
			
		||||
	osmo_init_logging2(ctx, NULL);
 | 
			
		||||
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_level(osmo_stderr_target, 1);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
@@ -190,50 +189,16 @@ static void test_server_client()
 | 
			
		||||
	client_stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool is_multicast_enabled()
 | 
			
		||||
{
 | 
			
		||||
	bool ret = true;
 | 
			
		||||
	struct addrinfo *ai;
 | 
			
		||||
	int sock;
 | 
			
		||||
	struct addrinfo hints = {0};
 | 
			
		||||
	struct ip_mreq multicast_req = {0};
 | 
			
		||||
	in_addr_t iface = INADDR_ANY;
 | 
			
		||||
 | 
			
		||||
	hints.ai_family = PF_UNSPEC;
 | 
			
		||||
	hints.ai_socktype = SOCK_DGRAM;
 | 
			
		||||
	hints.ai_flags = (AI_PASSIVE | AI_NUMERICHOST);
 | 
			
		||||
	assert(getaddrinfo("239.192.23.42", "4266", &hints, &ai) == 0);
 | 
			
		||||
 | 
			
		||||
	sock = socket(ai->ai_family, ai->ai_socktype, 0);
 | 
			
		||||
	assert(sock != -1);
 | 
			
		||||
	assert(setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface)) != -1);
 | 
			
		||||
 | 
			
		||||
	memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ai->ai_addr))->sin_addr,
 | 
			
		||||
	       sizeof(multicast_req.imr_multiaddr));
 | 
			
		||||
	multicast_req.imr_interface.s_addr = htonl(INADDR_ANY);
 | 
			
		||||
 | 
			
		||||
	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req)) == -1)
 | 
			
		||||
		ret = false;
 | 
			
		||||
 | 
			
		||||
	freeaddrinfo(ai);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Run all tests
 | 
			
		||||
 */
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
	if (!is_multicast_enabled()) {
 | 
			
		||||
		fprintf(stderr, "ERROR: multicast is disabled! (OS#4361)");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_enable_null_tracking();
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, "main");
 | 
			
		||||
	osmo_init_logging2(ctx, NULL);
 | 
			
		||||
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_level(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -174,7 +174,7 @@ int main()
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, "main");
 | 
			
		||||
	osmo_init_logging2(ctx, NULL);
 | 
			
		||||
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_level(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ int main()
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, "main");
 | 
			
		||||
	osmo_init_logging2(ctx, NULL);
 | 
			
		||||
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_level(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,14 +13,9 @@ OsmoHLR> ?
 | 
			
		||||
OsmoHLR> list
 | 
			
		||||
...
 | 
			
		||||
  show gsup-connections
 | 
			
		||||
  show subscribers all
 | 
			
		||||
  show subscribers (imei|imsi|msisdn) FILTER
 | 
			
		||||
  show subscribers (cs|ps) (on|off)
 | 
			
		||||
  show subscribers last-seen
 | 
			
		||||
  subscriber (imsi|msisdn|id|imei) IDENT show
 | 
			
		||||
  show subscriber (imsi|msisdn|id|imei) IDENT
 | 
			
		||||
  show mslookup services
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoHLR> enable
 | 
			
		||||
OsmoHLR# ?
 | 
			
		||||
@@ -32,13 +27,10 @@ OsmoHLR(config)# ?
 | 
			
		||||
...
 | 
			
		||||
  hlr       Configure the HLR
 | 
			
		||||
  mslookup  Configure Distributed GSM mslookup
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config)# list
 | 
			
		||||
...
 | 
			
		||||
  hlr
 | 
			
		||||
  mslookup
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config)# hlr
 | 
			
		||||
OsmoHLR(config-hlr)# ?
 | 
			
		||||
@@ -55,7 +47,7 @@ OsmoHLR(config-hlr)# list
 | 
			
		||||
  database PATH
 | 
			
		||||
  euse NAME
 | 
			
		||||
  no euse NAME
 | 
			
		||||
  ussd route prefix PREFIX internal (own-msisdn|own-imsi|test-idle)
 | 
			
		||||
  ussd route prefix PREFIX internal (own-msisdn|own-imsi)
 | 
			
		||||
  ussd route prefix PREFIX external EUSE
 | 
			
		||||
  no ussd route prefix PREFIX
 | 
			
		||||
  ussd default-route external EUSE
 | 
			
		||||
@@ -97,7 +89,7 @@ log stderr
 | 
			
		||||
 logging level main notice
 | 
			
		||||
 logging level db notice
 | 
			
		||||
 logging level auc notice
 | 
			
		||||
 logging level ss notice
 | 
			
		||||
 logging level ss info
 | 
			
		||||
 logging level mslookup notice
 | 
			
		||||
 logging level lu notice
 | 
			
		||||
 logging level dgsm notice
 | 
			
		||||
@@ -107,7 +99,6 @@ hlr
 | 
			
		||||
 database hlr_vty_test.db
 | 
			
		||||
 gsup
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  ipa-name unnamed-HLR
 | 
			
		||||
 ussd route prefix *#100# internal own-msisdn
 | 
			
		||||
 ussd route prefix *#101# internal own-imsi
 | 
			
		||||
end
 | 
			
		||||
@@ -125,7 +116,7 @@ OsmoHLR(config-mslookup)# list
 | 
			
		||||
...
 | 
			
		||||
  mdns bind [IP] [<1-65535>]
 | 
			
		||||
  mdns domain-suffix DOMAIN_SUFFIX
 | 
			
		||||
  no mdns bind
 | 
			
		||||
  no mdns
 | 
			
		||||
  server
 | 
			
		||||
  no server
 | 
			
		||||
  client
 | 
			
		||||
@@ -164,11 +155,11 @@ OsmoHLR(config-mslookup-server)# list
 | 
			
		||||
...
 | 
			
		||||
  mdns bind [IP] [<1-65535>]
 | 
			
		||||
  mdns domain-suffix DOMAIN_SUFFIX
 | 
			
		||||
  no mdns bind
 | 
			
		||||
  no mdns
 | 
			
		||||
  service NAME at IP <1-65535>
 | 
			
		||||
  no service NAME
 | 
			
		||||
  no service NAME at IP <1-65535>
 | 
			
		||||
  msc ipa-name .IPA_NAME
 | 
			
		||||
  msc .UNIT_NAME
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-server)# mdns ?
 | 
			
		||||
  bind           Configure where the mDNS server listens for mslookup requests
 | 
			
		||||
@@ -205,11 +196,9 @@ OsmoHLR(config-mslookup-server)# no service foo at 1.2.3.4 ?
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc?
 | 
			
		||||
  msc  Configure services for individual local MSCs
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ?
 | 
			
		||||
  ipa-name  Identify locally connected MSC by IPA Unit Name
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ipa-name ?
 | 
			
		||||
  IPA_NAME  IPA Unit Name of the local MSC to configure
 | 
			
		||||
  UNIT_NAME  IPA Unit Name of the local MSC to configure
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ipa-name MSC-1
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc MSC-1
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# ?
 | 
			
		||||
...
 | 
			
		||||
  service  Configure addresses of local services, as sent in replies to remote mslookup requests.
 | 
			
		||||
@@ -257,7 +246,7 @@ OsmoHLR(config-mslookup-client)# list
 | 
			
		||||
  timeout <1-100000>
 | 
			
		||||
  mdns bind [IP] [<1-65535>]
 | 
			
		||||
  mdns domain-suffix DOMAIN_SUFFIX
 | 
			
		||||
  no mdns bind
 | 
			
		||||
  no mdns
 | 
			
		||||
  gateway-proxy IP [<1-65535>]
 | 
			
		||||
  no gateway-proxy
 | 
			
		||||
 | 
			
		||||
@@ -316,13 +305,13 @@ OsmoHLR(config-mslookup-server)# service qwert at qwert 1234
 | 
			
		||||
OsmoHLR(config-mslookup-server)# service foo.bar at 123.45.67.89 1011
 | 
			
		||||
OsmoHLR(config-mslookup-server)# service baz.bar at 121.31.41.5 1617
 | 
			
		||||
OsmoHLR(config-mslookup-server)# service baz.bar at a:b:c::d 1819
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-23
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-23
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service foo.bar at 76.54.32.10 1234
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 12.11.10.98 7654
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 999:999:999::999 9999
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at dd:cc:bb::a 3210
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# exit
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-42
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-42
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service foo.bar at 1.1.1.1 1111
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2.2.2.2 2222
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2222:2222:2222::2 2222
 | 
			
		||||
@@ -331,12 +320,12 @@ Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1
 | 
			
		||||
service foo.bar at 123.45.67.89 1011
 | 
			
		||||
service baz.bar at 121.31.41.5 1617
 | 
			
		||||
service baz.bar at a:b:c::d 1819
 | 
			
		||||
msc ipa-name MSC-1
 | 
			
		||||
msc ipa-name msc-901-70-23
 | 
			
		||||
msc MSC-1
 | 
			
		||||
msc msc-901-70-23
 | 
			
		||||
 service foo.bar at 76.54.32.10 1234
 | 
			
		||||
 service baz.bar at 12.11.10.98 7654
 | 
			
		||||
 service baz.bar at dd:cc:bb::a 3210
 | 
			
		||||
msc ipa-name msc-901-70-42
 | 
			
		||||
msc msc-901-70-42
 | 
			
		||||
 service foo.bar at 1.1.1.1 1111
 | 
			
		||||
 service baz.bar at 2.2.2.2 2222
 | 
			
		||||
 service baz.bar at 2222:2222:2222::2 2222
 | 
			
		||||
@@ -360,14 +349,14 @@ mslookup
 | 
			
		||||
  service baz.bar at 2222:2222:2222::2 2222
 | 
			
		||||
 client
 | 
			
		||||
  gateway-proxy 1.2.3.4 4222
 | 
			
		||||
  mdns bind 239.192.23.42 4266
 | 
			
		||||
  mdns to 239.192.23.42 4266
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# no service baz.bar
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# no service asdf
 | 
			
		||||
% mslookup server: cannot remove service 'asdf'
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# exit
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-23
 | 
			
		||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-23
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# no service baz.bar at dd:cc:bb::a 3210
 | 
			
		||||
% mslookup server: cannot remove service 'baz.bar' to dd:cc:bb::a 3210
 | 
			
		||||
OsmoHLR(config-mslookup-server-msc)# no service asdf at asdf asdf
 | 
			
		||||
@@ -390,11 +379,11 @@ OsmoHLR(config-mslookup-client)# do show mslookup services
 | 
			
		||||
Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1:4222
 | 
			
		||||
service foo.bar at 123.45.67.89 1011
 | 
			
		||||
service baz.bar at 121.31.41.5 1617
 | 
			
		||||
msc ipa-name MSC-1
 | 
			
		||||
msc ipa-name msc-901-70-23
 | 
			
		||||
msc MSC-1
 | 
			
		||||
msc msc-901-70-23
 | 
			
		||||
 service foo.bar at 76.54.32.10 1234
 | 
			
		||||
 service baz.bar at 12.11.10.98 7654
 | 
			
		||||
msc ipa-name msc-901-70-42
 | 
			
		||||
msc msc-901-70-42
 | 
			
		||||
 service foo.bar at 1.1.1.1 1111
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-client)# show running-config
 | 
			
		||||
@@ -411,7 +400,7 @@ mslookup
 | 
			
		||||
 msc msc-901-70-42
 | 
			
		||||
  service foo.bar at 1.1.1.1 1111
 | 
			
		||||
 client
 | 
			
		||||
  mdns bind 239.192.23.42 4266
 | 
			
		||||
  mdns to 239.192.23.42 4266
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-client)# exit
 | 
			
		||||
@@ -422,11 +411,11 @@ Local GSUP HLR address returned in mslookup responses for local IMSIs: 23.42.17.
 | 
			
		||||
service foo.bar at 123.45.67.89 1011
 | 
			
		||||
service baz.bar at 121.31.41.5 1617
 | 
			
		||||
service gsup.hlr at 23.42.17.11 4223
 | 
			
		||||
msc ipa-name MSC-1
 | 
			
		||||
msc ipa-name msc-901-70-23
 | 
			
		||||
msc MSC-1
 | 
			
		||||
msc msc-901-70-23
 | 
			
		||||
 service foo.bar at 76.54.32.10 1234
 | 
			
		||||
 service baz.bar at 12.11.10.98 7654
 | 
			
		||||
msc ipa-name msc-901-70-42
 | 
			
		||||
msc msc-901-70-42
 | 
			
		||||
 service foo.bar at 1.1.1.1 1111
 | 
			
		||||
 | 
			
		||||
OsmoHLR(config-mslookup-server)# show running-config
 | 
			
		||||
@@ -444,5 +433,5 @@ mslookup
 | 
			
		||||
 msc msc-901-70-42
 | 
			
		||||
  service foo.bar at 1.1.1.1 1111
 | 
			
		||||
 client
 | 
			
		||||
  mdns bind 239.192.23.42 4266
 | 
			
		||||
  mdns to 239.192.23.42 4266
 | 
			
		||||
...
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user