mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-11-04 06:03:28 +00:00 
			
		
		
		
	Compare commits
	
		
			75 Commits
		
	
	
		
			35c3
			...
			laforge/cc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a5e9ca9045 | ||
| 
						 | 
					94c38a9e03 | ||
| 
						 | 
					05e888ed7c | ||
| 
						 | 
					9eb10efb23 | ||
| 
						 | 
					63450d990b | ||
| 
						 | 
					d2d9f35835 | ||
| 
						 | 
					80b7fe759f | ||
| 
						 | 
					b984a6c995 | ||
| 
						 | 
					3896888468 | ||
| 
						 | 
					e9dd9c6282 | ||
| 
						 | 
					277b2d642a | ||
| 
						 | 
					f1949f7b99 | ||
| 
						 | 
					f302de93dd | ||
| 
						 | 
					28f0774e34 | ||
| 
						 | 
					b07f33df41 | ||
| 
						 | 
					8b860e54be | ||
| 
						 | 
					9cf0030b6a | ||
| 
						 | 
					b9b224c7bd | ||
| 
						 | 
					e49391bfc4 | ||
| 
						 | 
					fbd736ef37 | ||
| 
						 | 
					dc30154fdf | ||
| 
						 | 
					37642177f9 | ||
| 
						 | 
					6401b90574 | ||
| 
						 | 
					5b5cac7e94 | ||
| 
						 | 
					937f583a7e | ||
| 
						 | 
					4ca7f6a17e | ||
| 
						 | 
					b64cb27003 | ||
| 
						 | 
					3b33b01fb0 | ||
| 
						 | 
					78abea6a0e | ||
| 
						 | 
					9ac494f486 | ||
| 
						 | 
					d62d401d07 | ||
| 
						 | 
					103c11bd24 | ||
| 
						 | 
					63de00cfc1 | ||
| 
						 | 
					1a1398ed54 | ||
| 
						 | 
					a8253a54ba | ||
| 
						 | 
					29f371fddf | ||
| 
						 | 
					2e403d6c3f | ||
| 
						 | 
					c41572330d | ||
| 
						 | 
					c7f1787c18 | ||
| 
						 | 
					c13599dc69 | ||
| 
						 | 
					6b73fd9678 | ||
| 
						 | 
					cd2af5ead7 | ||
| 
						 | 
					e21b45aecd | ||
| 
						 | 
					5857c595b3 | ||
| 
						 | 
					d9724f4298 | ||
| 
						 | 
					c69a18bb3d | ||
| 
						 | 
					8625cdaf2a | ||
| 
						 | 
					609978d0ab | ||
| 
						 | 
					28f0af872e | ||
| 
						 | 
					9f6e558215 | ||
| 
						 | 
					633fe291f5 | ||
| 
						 | 
					7d53ae1db8 | ||
| 
						 | 
					95abc2be17 | ||
| 
						 | 
					f1fe94c8ca | ||
| 
						 | 
					f7d3251d9a | ||
| 
						 | 
					3cf87fe22c | ||
| 
						 | 
					ee7c0cb8d9 | ||
| 
						 | 
					c5044cfd80 | ||
| 
						 | 
					20ddfdbc53 | ||
| 
						 | 
					227834b6bc | ||
| 
						 | 
					44a2180009 | ||
| 
						 | 
					f9cf180ebe | ||
| 
						 | 
					02078b7d91 | ||
| 
						 | 
					ef64b231dc | ||
| 
						 | 
					851814aa7c | ||
| 
						 | 
					81db389fd4 | ||
| 
						 | 
					7943e26938 | ||
| 
						 | 
					e0c6fe5921 | ||
| 
						 | 
					f58f44543f | ||
| 
						 | 
					15f624ec53 | ||
| 
						 | 
					d4e0e4d503 | ||
| 
						 | 
					2dc7d960a1 | ||
| 
						 | 
					52c4aa09b2 | ||
| 
						 | 
					66106c0992 | ||
| 
						 | 
					783ac81b9c | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,6 +2,8 @@
 | 
				
			|||||||
*.lo
 | 
					*.lo
 | 
				
			||||||
*.la
 | 
					*.la
 | 
				
			||||||
*.db
 | 
					*.db
 | 
				
			||||||
 | 
					*.db-shm
 | 
				
			||||||
 | 
					*.db-wal
 | 
				
			||||||
*.pyc
 | 
					*.pyc
 | 
				
			||||||
.*.sw?
 | 
					.*.sw?
 | 
				
			||||||
.version
 | 
					.version
 | 
				
			||||||
@@ -38,6 +40,7 @@ src/gsupclient/gsup-test-client
 | 
				
			|||||||
tests/atconfig
 | 
					tests/atconfig
 | 
				
			||||||
tests/testsuite
 | 
					tests/testsuite
 | 
				
			||||||
tests/testsuite.log
 | 
					tests/testsuite.log
 | 
				
			||||||
 | 
					tests/testsuite.dir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tests/auc/auc_3g_test
 | 
					tests/auc/auc_3g_test
 | 
				
			||||||
tests/auc/auc_ts_55_205_test_sets.c
 | 
					tests/auc/auc_ts_55_205_test_sets.c
 | 
				
			||||||
@@ -46,6 +49,7 @@ tests/auc/auc_test
 | 
				
			|||||||
tests/gsup_server/gsup_server_test
 | 
					tests/gsup_server/gsup_server_test
 | 
				
			||||||
tests/gsup/gsup_test
 | 
					tests/gsup/gsup_test
 | 
				
			||||||
tests/db/db_test
 | 
					tests/db/db_test
 | 
				
			||||||
 | 
					tests/hlr_vty_test.db*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# manuals
 | 
					# manuals
 | 
				
			||||||
doc/manuals/*.html
 | 
					doc/manuals/*.html
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								configure.ac
									
									
									
									
									
								
							@@ -34,11 +34,11 @@ PKG_PROG_PKG_CONFIG([0.20])
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
 | 
					PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.5.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
 | 
					PKG_CHECK_MODULES(SQLITE3, sqlite3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,6 +59,21 @@ then
 | 
				
			|||||||
	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
 | 
						CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AC_ARG_ENABLE([sqlite_talloc],
 | 
				
			||||||
 | 
							AC_HELP_STRING([--enable-sqlite-talloc],
 | 
				
			||||||
 | 
									[Configure SQLite3 to use talloc memory allocator [default=no]]),
 | 
				
			||||||
 | 
							[sqlite_talloc="$enableval"],[sqlite_talloc="no"])
 | 
				
			||||||
 | 
					if test "x$sqlite_talloc" = "xyes" ; then
 | 
				
			||||||
 | 
						# Older versions of SQLite3 (at least 3.8.2) become unstable with talloc.
 | 
				
			||||||
 | 
						# Feel free to relax to 3.24.0 > VER > 3.8.2 if it works for you.
 | 
				
			||||||
 | 
						# FIXME: PKG_CHECK_MODULES() may return cached result here!
 | 
				
			||||||
 | 
						PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.24.0)
 | 
				
			||||||
 | 
						AC_DEFINE([SQLITE_USE_TALLOC], 1, [Use talloc for SQLite3])
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					AC_MSG_CHECKING([whether to use talloc for SQLite3])
 | 
				
			||||||
 | 
					AC_MSG_RESULT([$sqlite_talloc])
 | 
				
			||||||
 | 
					AM_CONDITIONAL([DB_SQLITE_DEBUG], [test "x$sqlite_talloc" = "xyes"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AC_ARG_ENABLE(werror,
 | 
					AC_ARG_ENABLE(werror,
 | 
				
			||||||
	[AS_HELP_STRING(
 | 
						[AS_HELP_STRING(
 | 
				
			||||||
		[--enable-werror],
 | 
							[--enable-werror],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,4 +58,5 @@ if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
				
			|||||||
	make -C "$base/doc/manuals" publish
 | 
						make -C "$base/doc/manuals" publish
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$MAKE maintainer-clean
 | 
				
			||||||
osmo-clean-workspace.sh
 | 
					osmo-clean-workspace.sh
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom Home Location Register (OsmoHLR)
 | 
					Description=Osmocom Home Location Register (OsmoHLR)
 | 
				
			||||||
 | 
					Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					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
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										138
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										138
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,141 @@
 | 
				
			|||||||
 | 
					osmo-hlr (1.1.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Oliver Smith ]
 | 
				
			||||||
 | 
					  * docs: running: document --db-upgrade
 | 
				
			||||||
 | 
					  * Add IMEI column to subscriber table
 | 
				
			||||||
 | 
					  * Optionally store IMEI in subscriber table
 | 
				
			||||||
 | 
					  * VTY tests: fill DB before running test
 | 
				
			||||||
 | 
					  * VTY: integrate IMEI
 | 
				
			||||||
 | 
					  * hlr.c: replace deprecated osmo_gsup_get_err_msg_type()
 | 
				
			||||||
 | 
					  * hlr.c: move hlr_ctx to the top
 | 
				
			||||||
 | 
					  * tests: use -no-install libtool flag to avoid ./lt-* scripts
 | 
				
			||||||
 | 
					  * Cosmetic: gsup_route_find: comment addr, addrlen
 | 
				
			||||||
 | 
					  * USSD: save MO USSD's originating MSC's vlr_number
 | 
				
			||||||
 | 
					  * USSD: don't use gsm0480_msgb_alloc_name()
 | 
				
			||||||
 | 
					  * hlr.c: forward GSUP messages between clients
 | 
				
			||||||
 | 
					  * db_hlr.c: db_subscr_create(): add flags argument
 | 
				
			||||||
 | 
					  * db_hlr.c: add db_subscr_exists_by_imsi()
 | 
				
			||||||
 | 
					  * Create subscribers on demand
 | 
				
			||||||
 | 
					  * Document subscribers create on demand feature
 | 
				
			||||||
 | 
					  * debian: create -doc subpackage with pdf manuals
 | 
				
			||||||
 | 
					  * db_test: set timezone to work around mktime bug
 | 
				
			||||||
 | 
					  * db_hlr: zero-initialize "struct tm"
 | 
				
			||||||
 | 
					  * rx_check_imei_req(): fix IMEI bounds checking
 | 
				
			||||||
 | 
					  * contrib/jenkins.sh: run "make maintainer-clean"
 | 
				
			||||||
 | 
					  * VTY: add subscriber update network-access-mode
 | 
				
			||||||
 | 
					  * manuals: improve subscribers create on demand
 | 
				
			||||||
 | 
					  * gitignore: ignore everything generated in db_test
 | 
				
			||||||
 | 
					  * db_auc.c: verify hex key sizes read from DB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Max ]
 | 
				
			||||||
 | 
					  * Log ip:port when adding GSUP routes
 | 
				
			||||||
 | 
					  * Add link to project wiki to .service file
 | 
				
			||||||
 | 
					  * Enable statsd support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Vadim Yanitskiy ]
 | 
				
			||||||
 | 
					  * hlr.c: properly terminate the process on SIGTERM
 | 
				
			||||||
 | 
					  * hlr.c: fix: also store the session state in read_cb_forward()
 | 
				
			||||||
 | 
					  * hlr.c: fix: properly print the original message type in read_cb_forward()
 | 
				
			||||||
 | 
					  * hlr.c: check the presence of msgb->l2h in read_cb()
 | 
				
			||||||
 | 
					  * hlr.c: fix possible msgb memleaks in read_cb()
 | 
				
			||||||
 | 
					  * db_hlr.c: add db_subscr_exists_by_msisdn()
 | 
				
			||||||
 | 
					  * src/db.h: use GSM23003_MSISDN_MAX_DIGITS for MSISDN buffer size
 | 
				
			||||||
 | 
					  * src/hlr.c: fix deprecation warning: use gsm48_decode_bcd_number2()
 | 
				
			||||||
 | 
					  * hlr_ussd.c: fix: properly pass invokeID in handle_ussd_own_msisdn()
 | 
				
			||||||
 | 
					  * hlr_ussd.c: rx_proc_ss_req(): fix NULL pointer dereference
 | 
				
			||||||
 | 
					  * build: fix mess with 'db_test_SOURCES' and 'db_test_LDADD'
 | 
				
			||||||
 | 
					  * tests/db_test: close the database when test is finished
 | 
				
			||||||
 | 
					  * src/db.c: integrate SQLite3 with talloc allocator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Neels Hofmeyr ]
 | 
				
			||||||
 | 
					  * USSD: fix routing to multiple MSC
 | 
				
			||||||
 | 
					  * fix error logging for GSUP route
 | 
				
			||||||
 | 
					  * add missing error log: invalid IMSI
 | 
				
			||||||
 | 
					  * osmo-hlr: allow configuring db path from cfg file
 | 
				
			||||||
 | 
					  * use new OSMO_IMSI_BUF_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Daniel Willmann ]
 | 
				
			||||||
 | 
					  * manuals: Add script to update vty/counter documentation from docker
 | 
				
			||||||
 | 
					  * manuals: Update vty documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Pau Espin Pedrol ]
 | 
				
			||||||
 | 
					  * Remove undefined param passed to logging_vty_add_cmds
 | 
				
			||||||
 | 
					  * configure.ac: Require libosmocore 1.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 07 Aug 2019 16:14:23 +0200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo-hlr (1.0.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Stefan Sperling ]
 | 
				
			||||||
 | 
					  * move creation of insert subscriber data messages to a common function
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Harald Welte ]
 | 
				
			||||||
 | 
					  * Return proper GSUP error in case of too short IMSI
 | 
				
			||||||
 | 
					  * disable blind subscriber insertion into every VLR/SGSN
 | 
				
			||||||
 | 
					  * gsup_server: Add "priv" pointer and make it point to 'struct hlr'
 | 
				
			||||||
 | 
					  * move osmo_gsup_addr_send() declaration from luop.h to gsup_router.h
 | 
				
			||||||
 | 
					  * gsup_router: Use "#pragma once" and add missing #includes
 | 
				
			||||||
 | 
					  * Add "show gsup-connections" VTY command
 | 
				
			||||||
 | 
					  * import gsup_client.c as new libosmo-gsup-client
 | 
				
			||||||
 | 
					  * gsup_client: rename gsup_client_* to osmo_gsup_client_*
 | 
				
			||||||
 | 
					  * GSUP: Log GSUP route add/remove
 | 
				
			||||||
 | 
					  * hlr: Export + Declare global g_hlr symbol
 | 
				
			||||||
 | 
					  * USSD: Add Core USSD handling + VTY routing config to HLR
 | 
				
			||||||
 | 
					  * USSD: Add basic dispatch + decode of GSUP-encapsulated SS/USSD
 | 
				
			||||||
 | 
					  * hlr_ussd: Introduce LOGPSS() macro
 | 
				
			||||||
 | 
					  * USSD: Send ReturnError component if USSD Code unknown / EUSE disconnected
 | 
				
			||||||
 | 
					  * USSD: Further unification of log output; Use LOGPSS when possible
 | 
				
			||||||
 | 
					  * osmo-hlr.cfg: Don't enable DEBUG logging by default
 | 
				
			||||||
 | 
					  * USSD: Add new "DSS" logging category and use it appropriately
 | 
				
			||||||
 | 
					  * USSD: fix null-pointer deref in "default-route" vty/config cmd
 | 
				
			||||||
 | 
					  * Add osmo-euse-demo as minimalistic test of a External USSD (EUSE) handler
 | 
				
			||||||
 | 
					  * USSD: Add support for internal USSD handlers
 | 
				
			||||||
 | 
					  * debian: Add sub-package for libosmo-gsup-client
 | 
				
			||||||
 | 
					  * pkg-config: Fix libosmo-gsup-client pkg-config file
 | 
				
			||||||
 | 
					  * gitignore: Add .tarball-version
 | 
				
			||||||
 | 
					  * debian: Make libosmo-gsup-client-dev depend on libosmo-gsup-client0
 | 
				
			||||||
 | 
					  * USSD: Fix "ussd default-route"
 | 
				
			||||||
 | 
					  * libosmo-gsup-client: License is GPLv2-or-later
 | 
				
			||||||
 | 
					  * osmo-hlr.cfg: Ensure well-formed config file example
 | 
				
			||||||
 | 
					  * test_nodes.vty: Since libosmocore 1.0.0, we only have one space
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Martin Hauke ]
 | 
				
			||||||
 | 
					  * sql/Makefile.am: Make docsdir completely configurable
 | 
				
			||||||
 | 
					  * debian: Fix typo in package description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Pau Espin Pedrol ]
 | 
				
			||||||
 | 
					  * debian: Avoid installing duplicate cfg file in /etc
 | 
				
			||||||
 | 
					  * sql/Makefile: Install hlr_data.sql as example together with hlr.sql
 | 
				
			||||||
 | 
					  * sql/Makefile: Install sql files under doc/.../sql subdir
 | 
				
			||||||
 | 
					  * sql/Makefile: Create empty /var/lib/osmocom directory at install time
 | 
				
			||||||
 | 
					  * Install systemd services with autotools
 | 
				
			||||||
 | 
					  * Move doc/Makefile.am to doc/examples/Makefile.am
 | 
				
			||||||
 | 
					  * Install sample cfg file to /etc/osmocom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Vadim Yanitskiy ]
 | 
				
			||||||
 | 
					  * hlr.c: move deinitialization code from SIGINT handler
 | 
				
			||||||
 | 
					  * hlr.c: free root talloc context on exit
 | 
				
			||||||
 | 
					  * hlr.c: track the use of talloc NULL memory contexts
 | 
				
			||||||
 | 
					  * src/db.c: fix: make sure the database is properly closed
 | 
				
			||||||
 | 
					  * src/db.c: don't ignore the result of db_bootstrap()
 | 
				
			||||||
 | 
					  * hlr_vty_subscr.c: fix subscriber creation command help
 | 
				
			||||||
 | 
					  * Update .gitignore: add missing build products
 | 
				
			||||||
 | 
					  * tests/Makefile.am: also remove temporary sqlite files
 | 
				
			||||||
 | 
					  * hlr_ussd.h: add #pragma once include guard
 | 
				
			||||||
 | 
					  * hlr_ussd.h: use proper libc headers
 | 
				
			||||||
 | 
					  * Update .gitignore: ignore osmo-euse-demo
 | 
				
			||||||
 | 
					  * hlr_ussd.h: drop meaningless forward declaration
 | 
				
			||||||
 | 
					  * USSD/hlr_vty.c: print error if EUSE is not found
 | 
				
			||||||
 | 
					  * hlr_ussd.c: fix: properly print a EUSE / IUSE name
 | 
				
			||||||
 | 
					  * hlr_ussd.c: avoid using CR and NL in IUSE responses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Neels Hofmeyr ]
 | 
				
			||||||
 | 
					  * fix build: adjust test_nodes.vty to logging change
 | 
				
			||||||
 | 
					  * tweak example config
 | 
				
			||||||
 | 
					  * make: always allow running python tests manually
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Harald Welte <laforge@gnumonks.org>  Sun, 20 Jan 2019 19:29:58 +0100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo-hlr (0.2.1) unstable; urgency=medium
 | 
					osmo-hlr (0.2.1) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [ Neels Hofmeyr ]
 | 
					  [ Neels Hofmeyr ]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -12,7 +12,8 @@ Build-Depends: debhelper (>= 9),
 | 
				
			|||||||
               libosmo-abis-dev,
 | 
					               libosmo-abis-dev,
 | 
				
			||||||
               libosmo-netif-dev,
 | 
					               libosmo-netif-dev,
 | 
				
			||||||
               libsqlite3-dev,
 | 
					               libsqlite3-dev,
 | 
				
			||||||
               sqlite3
 | 
					               sqlite3,
 | 
				
			||||||
 | 
					               osmo-gsm-manuals-dev
 | 
				
			||||||
Standards-Version: 3.9.6
 | 
					Standards-Version: 3.9.6
 | 
				
			||||||
Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
 | 
					Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
 | 
				
			||||||
Vcs-Git: git://git.osmocom.org/osmo-hlr
 | 
					Vcs-Git: git://git.osmocom.org/osmo-hlr
 | 
				
			||||||
@@ -57,3 +58,12 @@ Description: Development headers of Osmocom GSUP client library
 | 
				
			|||||||
  and External USSD Entities (EUSEs) using this library to implement clients.
 | 
					  and External USSD Entities (EUSEs) using this library to implement clients.
 | 
				
			||||||
  .
 | 
					  .
 | 
				
			||||||
  This package contains the development headers.
 | 
					  This package contains the development headers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Package: osmo-hlr-doc
 | 
				
			||||||
 | 
					Architecture: all
 | 
				
			||||||
 | 
					Section: doc
 | 
				
			||||||
 | 
					Priority: optional
 | 
				
			||||||
 | 
					Depends: ${misc:Depends}
 | 
				
			||||||
 | 
					Description: ${misc:Package} PDF documentation
 | 
				
			||||||
 | 
					 Various manuals: user manual, VTY reference manual and/or
 | 
				
			||||||
 | 
					 protocol/interface manuals.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								debian/osmo-hlr-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-hlr-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					usr/share/doc/osmo-hlr-doc/*.pdf
 | 
				
			||||||
							
								
								
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -17,4 +17,8 @@ override_dh_auto_test:
 | 
				
			|||||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
						dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
override_dh_auto_configure:
 | 
					override_dh_auto_configure:
 | 
				
			||||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
 | 
						dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
 | 
				
			||||||
 | 
					override_dh_compress:
 | 
				
			||||||
 | 
						dh_compress -X.pdf
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,3 +24,8 @@ hlr
 | 
				
			|||||||
  bind ip 127.0.0.1
 | 
					  bind ip 127.0.0.1
 | 
				
			||||||
 ussd route prefix *#100# internal own-msisdn
 | 
					 ussd route prefix *#100# internal own-msisdn
 | 
				
			||||||
 ussd route prefix *#101# internal own-imsi
 | 
					 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ EXTRA_DIST = example_subscriber_add_update_delete.vty \
 | 
				
			|||||||
    osmohlr-usermanual.adoc \
 | 
					    osmohlr-usermanual.adoc \
 | 
				
			||||||
    osmohlr-usermanual-docinfo.xml \
 | 
					    osmohlr-usermanual-docinfo.xml \
 | 
				
			||||||
    osmohlr-vty-reference.xml \
 | 
					    osmohlr-vty-reference.xml \
 | 
				
			||||||
 | 
					    regen_doc.sh \
 | 
				
			||||||
    chapters \
 | 
					    chapters \
 | 
				
			||||||
    vty
 | 
					    vty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,6 +16,7 @@ if BUILD_MANUALS
 | 
				
			|||||||
  VTY_REFERENCE = osmohlr-vty-reference.xml
 | 
					  VTY_REFERENCE = osmohlr-vty-reference.xml
 | 
				
			||||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
					  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  OSMO_REPOSITORY = osmo-hlr
 | 
				
			||||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
 | 
					  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,24 +5,27 @@ arguments:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
=== SYNOPSIS
 | 
					=== SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*osmo-hlr* [-h|-V] [-d 'DBGMASK'] [-D] [-c 'CONFIGFILE'] [-s] [-T] [-e 'LOGLEVEL'] [-l 'DATABASE']
 | 
					*osmo-hlr* [-h] [-c 'CONFIGFILE'] [-l 'DATABASE'] [-d 'DBGMASK'] [-D] [-s] [-T] [-e 'LOGLEVEL'] [-U] [-V]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
=== OPTIONS
 | 
					=== OPTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Keep the order the same as in osmo-hlr --help!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*-h, --help*::
 | 
					*-h, --help*::
 | 
				
			||||||
	Print a short help message about the supported options
 | 
						Print a short help message about the supported options
 | 
				
			||||||
*-V, --version*::
 | 
					*-c, --config-file 'CONFIGFILE'*::
 | 
				
			||||||
	Print the compile-time version number of the OsmoBTS program
 | 
						Specify the file and path name of the configuration file to be
 | 
				
			||||||
 | 
						used. If none is specified, use `osmo-hlr.cfg` in the current
 | 
				
			||||||
 | 
						working directory.
 | 
				
			||||||
 | 
					*-l, --database 'DATABASE'*::
 | 
				
			||||||
 | 
						Specify the file name of the SQLite3 database to use as HLR/AUC
 | 
				
			||||||
 | 
						storage
 | 
				
			||||||
*-d, --debug 'DBGMASK','DBGLEVELS'*::
 | 
					*-d, --debug 'DBGMASK','DBGLEVELS'*::
 | 
				
			||||||
	Set the log subsystems and levels for logging to stderr. This
 | 
						Set the log subsystems and levels for logging to stderr. This
 | 
				
			||||||
	has mostly been superseded by VTY-based logging configuration,
 | 
						has mostly been superseded by VTY-based logging configuration,
 | 
				
			||||||
	see <<logging>> for further information.
 | 
						see <<logging>> for further information.
 | 
				
			||||||
*-D, --daemonize*::
 | 
					*-D, --daemonize*::
 | 
				
			||||||
	Fork the process as a daemon into background.
 | 
						Fork the process as a daemon into background.
 | 
				
			||||||
*-c, --config-file 'CONFIGFILE'*::
 | 
					 | 
				
			||||||
	Specify the file and path name of the configuration file to be
 | 
					 | 
				
			||||||
	used. If none is specified, use `osmo-hlr.cfg` in the current
 | 
					 | 
				
			||||||
	working directory.
 | 
					 | 
				
			||||||
*-s, --disable-color*::
 | 
					*-s, --disable-color*::
 | 
				
			||||||
	Disable colors for logging to stderr. This has mostly been
 | 
						Disable colors for logging to stderr. This has mostly been
 | 
				
			||||||
	deprecated by VTY based logging configuration, see <<logging>>
 | 
						deprecated by VTY based logging configuration, see <<logging>>
 | 
				
			||||||
@@ -35,9 +38,13 @@ arguments:
 | 
				
			|||||||
	Set the global log level for logging to stderr. This has mostly
 | 
						Set the global log level for logging to stderr. This has mostly
 | 
				
			||||||
	been deprecated by VTY based logging configuration, see
 | 
						been deprecated by VTY based logging configuration, see
 | 
				
			||||||
	<<logging>> for more information.
 | 
						<<logging>> for more information.
 | 
				
			||||||
*-l, --database 'DATABASE'*::
 | 
					*-U, --db-upgrade*::
 | 
				
			||||||
	Specify the file name of the SQLite3 database to use as HLR/AUC
 | 
						Allow HLR database schema upgrades. If OsmoHLR was updated and
 | 
				
			||||||
	storage
 | 
						requires a newer database schema, it will refuse to start unless
 | 
				
			||||||
 | 
						this option is specified. The updated database can not be
 | 
				
			||||||
 | 
						downgraded, make backups as necessary.
 | 
				
			||||||
 | 
					*-V, --version*::
 | 
				
			||||||
 | 
						Print the compile-time version number of the OsmoHLR program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
=== Bootstrap the Database
 | 
					=== Bootstrap the Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,3 +67,63 @@ transceiving only RAND and SRES, may be applicable. (See 3GPP TS 33.102, chapter
 | 
				
			|||||||
|ms_purged_ps|1|3GPP TS 23.008 chapter 2.7.6
 | 
					|ms_purged_ps|1|3GPP TS 23.008 chapter 2.7.6
 | 
				
			||||||
|===
 | 
					|===
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=== Configuring the Subscribers Create on Demand Feature
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usually a HLR will only allow mobile equipment (ME) on the network, if the HLR
 | 
				
			||||||
 | 
					has a subscriber entry with the ME's IMSI. But OsmoHLR can also be configured to
 | 
				
			||||||
 | 
					automatically create new entries for new IMSIs, with the
 | 
				
			||||||
 | 
					`subscriber-create-on-demand` VTY option. The obvious use case is creating the
 | 
				
			||||||
 | 
					new subscriber entry and then allowing the ME to use both the CS
 | 
				
			||||||
 | 
					(Circuit Switched) and PS (Packet Switched) NAM (Network Access Mode).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.osmo-hlr.cfg
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 5 cs+ps
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On the other hand, operators might only want to give network access to IMSIs, of
 | 
				
			||||||
 | 
					which they know the owner. In order to do that, one can set the default NAM to
 | 
				
			||||||
 | 
					`none` and manually approve new subscribers by changing the NAM (e.g. over the
 | 
				
			||||||
 | 
					VTY, see the example below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Oftentimes it is hard to know, which IMSI belongs to which ME, but the IMEI is
 | 
				
			||||||
 | 
					readily available. If you configure your MSC to send IMEI checking requests to
 | 
				
			||||||
 | 
					the HLR, before sending location update requests, the subscribers created on
 | 
				
			||||||
 | 
					demand can also have the IMEI stored in the HLR database. With OsmoMSC, this
 | 
				
			||||||
 | 
					is done by writing `check-imei-rqd early` in the `msc` section of osmo-msc.cfg.
 | 
				
			||||||
 | 
					Then enable storing the IMEI when receiving check IMEI requests with
 | 
				
			||||||
 | 
					`store-imei` in the OsmoHLR configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.osmo-msc.cfg
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					msc
 | 
				
			||||||
 | 
					 check-imei-rqd early
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.osmo-hlr.cfg
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 5 none
 | 
				
			||||||
 | 
					 store-imei
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Enabling CS and PS NAM via VTY for a known IMEI
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					OsmoHLR> enable
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imei 35761300444848 show
 | 
				
			||||||
 | 
					    ID: 1
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: 58192 <1>
 | 
				
			||||||
 | 
					    IMEI: 35761300444848
 | 
				
			||||||
 | 
					    CS disabled <2>
 | 
				
			||||||
 | 
					    PS disabled <2>
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imei 35761300444848 update network-access-mode cs+ps
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imei 35761300444848 show
 | 
				
			||||||
 | 
					    ID: 1
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: 58192
 | 
				
			||||||
 | 
					    IMEI: 35761300444848
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> Randomly generated 5 digit MSISDN
 | 
				
			||||||
 | 
					<2> Disabled CS and PS NAM prevent the subscriber from accessing the network
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					#!/bin/sh -x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ -z "$DOCKER_PLAYGROUND" ]; then
 | 
				
			||||||
 | 
						echo "You need to set DOCKER_PLAYGROUND"
 | 
				
			||||||
 | 
						exit 1
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SCRIPT=$(realpath "$0")
 | 
				
			||||||
 | 
					MANUAL_DIR=$(dirname "$SCRIPT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cd "$DOCKER_PLAYGROUND/scripts" || exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OSMO_HLR_BRANCH=$COMMIT ./regen_doc.sh osmo-hlr 4258 \
 | 
				
			||||||
 | 
						"$MANUAL_DIR/chapters/counters_generated.adoc" \
 | 
				
			||||||
 | 
						"$MANUAL_DIR/vty/hlr_vty_reference.xml"
 | 
				
			||||||
@@ -187,7 +187,7 @@
 | 
				
			|||||||
        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
 | 
					        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf) (debug|info|notice|error|fatal)'>
 | 
					    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='logging' doc='Configure logging' />
 | 
					        <param name='logging' doc='Configure logging' />
 | 
				
			||||||
        <param name='level' doc='Set the log level for a specified category' />
 | 
					        <param name='level' doc='Set the log level for a specified category' />
 | 
				
			||||||
@@ -213,6 +213,7 @@
 | 
				
			|||||||
        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
					        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
				
			||||||
        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
					        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
				
			||||||
        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
					        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
				
			||||||
 | 
					        <param name='lrspro' doc='Remote SIM protocol' />
 | 
				
			||||||
        <param name='debug' doc='Log debug messages and higher levels' />
 | 
					        <param name='debug' doc='Log debug messages and higher levels' />
 | 
				
			||||||
        <param name='info' doc='Log informational messages and higher levels' />
 | 
					        <param name='info' doc='Log informational messages and higher levels' />
 | 
				
			||||||
        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
					        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
				
			||||||
@@ -302,22 +303,63 @@
 | 
				
			|||||||
        <param name='REGEXP' doc='Regular expression' />
 | 
					        <param name='REGEXP' doc='Regular expression' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show stats'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Show statistical values' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show stats level (global|peer|subscriber)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Show statistical values' />
 | 
				
			||||||
 | 
					        <param name='level' doc='Set the maximum group level' />
 | 
				
			||||||
 | 
					        <param name='global' doc='Show global groups only' />
 | 
				
			||||||
 | 
					        <param name='peer' doc='Show global and network peer related groups' />
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Show global, peer, and subscriber groups' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show asciidoc counters'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='asciidoc' doc='Asciidoc generation' />
 | 
				
			||||||
 | 
					        <param name='counters' doc='Generate table of all registered counters' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show rate-counters'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='rate-counters' doc='Show all rate counters' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
    <command id='show gsup-connections'>
 | 
					    <command id='show gsup-connections'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='show' doc='Show running system information' />
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
        <param name='gsup-connections' doc='GSUP Connections from VLRs, SGSNs, EUSEs' />
 | 
					        <param name='gsup-connections' doc='GSUP Connections from VLRs, SGSNs, EUSEs' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT show'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT show'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='show' doc='Show subscriber information' />
 | 
					        <param name='show' doc='Show subscriber information' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show subscriber (imsi|msisdn|id|imei) IDENT'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
  </node>
 | 
					  </node>
 | 
				
			||||||
  <node id='enable'>
 | 
					  <node id='enable'>
 | 
				
			||||||
    <name>enable</name>
 | 
					    <name>enable</name>
 | 
				
			||||||
@@ -486,7 +528,7 @@
 | 
				
			|||||||
        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
 | 
					        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf) (debug|info|notice|error|fatal)'>
 | 
					    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='logging' doc='Configure logging' />
 | 
					        <param name='logging' doc='Configure logging' />
 | 
				
			||||||
        <param name='level' doc='Set the log level for a specified category' />
 | 
					        <param name='level' doc='Set the log level for a specified category' />
 | 
				
			||||||
@@ -512,6 +554,7 @@
 | 
				
			|||||||
        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
					        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
				
			||||||
        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
					        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
				
			||||||
        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
					        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
				
			||||||
 | 
					        <param name='lrspro' doc='Remote SIM protocol' />
 | 
				
			||||||
        <param name='debug' doc='Log debug messages and higher levels' />
 | 
					        <param name='debug' doc='Log debug messages and higher levels' />
 | 
				
			||||||
        <param name='info' doc='Log informational messages and higher levels' />
 | 
					        <param name='info' doc='Log informational messages and higher levels' />
 | 
				
			||||||
        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
					        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
				
			||||||
@@ -521,8 +564,7 @@
 | 
				
			|||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='logging level set-all (debug|info|notice|error|fatal)'>
 | 
					    <command id='logging level set-all (debug|info|notice|error|fatal)'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='logging' doc='Configure logging' />
 | 
					        <param name='logging' doc='Configure logging' />        <param name='level' doc='Set the log level for a specified category' />
 | 
				
			||||||
        <param name='level' doc='Set the log level for a specified category' />
 | 
					 | 
				
			||||||
        <param name='set-all' doc='Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period.' />
 | 
					        <param name='set-all' doc='Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period.' />
 | 
				
			||||||
        <param name='debug' doc='Log debug messages and higher levels' />
 | 
					        <param name='debug' doc='Log debug messages and higher levels' />
 | 
				
			||||||
        <param name='info' doc='Log informational messages and higher levels' />
 | 
					        <param name='info' doc='Log informational messages and higher levels' />
 | 
				
			||||||
@@ -601,22 +643,63 @@
 | 
				
			|||||||
        <param name='REGEXP' doc='Regular expression' />
 | 
					        <param name='REGEXP' doc='Regular expression' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show stats'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Show statistical values' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show stats level (global|peer|subscriber)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Show statistical values' />
 | 
				
			||||||
 | 
					        <param name='level' doc='Set the maximum group level' />
 | 
				
			||||||
 | 
					        <param name='global' doc='Show global groups only' />
 | 
				
			||||||
 | 
					        <param name='peer' doc='Show global and network peer related groups' />
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Show global, peer, and subscriber groups' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show asciidoc counters'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='asciidoc' doc='Asciidoc generation' />
 | 
				
			||||||
 | 
					        <param name='counters' doc='Generate table of all registered counters' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show rate-counters'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='rate-counters' doc='Show all rate counters' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
    <command id='show gsup-connections'>
 | 
					    <command id='show gsup-connections'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='show' doc='Show running system information' />
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
        <param name='gsup-connections' doc='GSUP Connections from VLRs, SGSNs, EUSEs' />
 | 
					        <param name='gsup-connections' doc='GSUP Connections from VLRs, SGSNs, EUSEs' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT show'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT show'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='show' doc='Show subscriber information' />
 | 
					        <param name='show' doc='Show subscriber information' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='show subscriber (imsi|msisdn|id|imei) IDENT'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='show' doc='Show running system information' />
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber imsi IDENT create'>
 | 
					    <command id='subscriber imsi IDENT create'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
@@ -625,47 +708,52 @@
 | 
				
			|||||||
        <param name='create' doc='Create subscriber by IMSI' />
 | 
					        <param name='create' doc='Create subscriber by IMSI' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT delete'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT delete'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='delete' doc='Delete subscriber from database' />
 | 
					        <param name='delete' doc='Delete subscriber from database' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT update msisdn MSISDN'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update msisdn (none|MSISDN)'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='update' doc='Set or update subscriber data' />
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
        <param name='msisdn' doc='Set MSISDN (phone number) of the subscriber' />
 | 
					        <param name='msisdn' doc='Set MSISDN (phone number) of the subscriber' />
 | 
				
			||||||
 | 
					        <param name='none' doc='Remove MSISDN (phone number)' />
 | 
				
			||||||
        <param name='MSISDN' doc='New MSISDN (phone number)' />
 | 
					        <param name='MSISDN' doc='New MSISDN (phone number)' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT update aud2g none'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update aud2g none'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='update' doc='Set or update subscriber data' />
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
        <param name='aud2g' doc='Set 2G authentication data' />
 | 
					        <param name='aud2g' doc='Set 2G authentication data' />
 | 
				
			||||||
        <param name='none' doc='Delete 2G authentication data' />
 | 
					        <param name='none' doc='Delete 2G authentication data' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='update' doc='Set or update subscriber data' />
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
        <param name='aud2g' doc='Set 2G authentication data' />
 | 
					        <param name='aud2g' doc='Set 2G authentication data' />
 | 
				
			||||||
        <param name='comp128v1' doc='Use COMP128v1 algorithm' />
 | 
					        <param name='comp128v1' doc='Use COMP128v1 algorithm' />
 | 
				
			||||||
@@ -676,25 +764,27 @@
 | 
				
			|||||||
        <param name='KI' doc='Ki as 32 hexadecimal characters' />
 | 
					        <param name='KI' doc='Ki as 32 hexadecimal characters' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT update aud3g none'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update aud3g none'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='update' doc='Set or update subscriber data' />
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
        <param name='aud3g' doc='Set UMTS authentication data (3G, and 2G with UMTS AKA)' />
 | 
					        <param name='aud3g' doc='Set UMTS authentication data (3G, and 2G with UMTS AKA)' />
 | 
				
			||||||
        <param name='none' doc='Delete 3G authentication data' />
 | 
					        <param name='none' doc='Delete 3G authentication data' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='subscriber (imsi|msisdn|id) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]'>
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='subscriber' doc='Subscriber management commands' />
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
        <param name='id' doc='Identify subscriber by database ID' />
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
        <param name='IDENT' doc='IMSI/MSISDN/ID of the subscriber' />
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
        <param name='update' doc='Set or update subscriber data' />
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
        <param name='aud3g' doc='Set UMTS authentication data (3G, and 2G with UMTS AKA)' />
 | 
					        <param name='aud3g' doc='Set UMTS authentication data (3G, and 2G with UMTS AKA)' />
 | 
				
			||||||
        <param name='milenage' doc='Use Milenage algorithm' />
 | 
					        <param name='milenage' doc='Use Milenage algorithm' />
 | 
				
			||||||
@@ -707,6 +797,36 @@
 | 
				
			|||||||
        <param name='[<0-28>]' doc='IND bit length value (default: 5)' />
 | 
					        <param name='[<0-28>]' doc='IND bit length value (default: 5)' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update imei (none|IMEI)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
 | 
					        <param name='imei' doc='Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)' />
 | 
				
			||||||
 | 
					        <param name='none' doc='Forget IMEI' />
 | 
				
			||||||
 | 
					        <param name='IMEI' doc='Set IMEI (use for debug only!)' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='subscriber (imsi|msisdn|id|imei) IDENT update network-access-mode (none|cs|ps|cs+ps)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Subscriber management commands' />
 | 
				
			||||||
 | 
					        <param name='imsi' doc='Identify subscriber by IMSI' />
 | 
				
			||||||
 | 
					        <param name='msisdn' doc='Identify subscriber by MSISDN (phone number)' />
 | 
				
			||||||
 | 
					        <param name='id' doc='Identify subscriber by database ID' />
 | 
				
			||||||
 | 
					        <param name='imei' doc='Identify subscriber by IMEI' />
 | 
				
			||||||
 | 
					        <param name='IDENT' doc='IMSI/MSISDN/ID/IMEI of the subscriber' />
 | 
				
			||||||
 | 
					        <param name='update' doc='Set or update subscriber data' />
 | 
				
			||||||
 | 
					        <param name='network-access-mode' doc='Set Network Access Mode (NAM) of the subscriber' />
 | 
				
			||||||
 | 
					        <param name='none' doc='Do not allow access to circuit switched or packet switched services' />
 | 
				
			||||||
 | 
					        <param name='cs' doc='Allow access to circuit switched services only' />
 | 
				
			||||||
 | 
					        <param name='ps' doc='Allow access to packet switched services only' />
 | 
				
			||||||
 | 
					        <param name='cs+ps' doc='Allow access to both circuit and packet switched services' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
  </node>
 | 
					  </node>
 | 
				
			||||||
  <node id='config'>
 | 
					  <node id='config'>
 | 
				
			||||||
    <name>config</name>
 | 
					    <name>config</name>
 | 
				
			||||||
@@ -883,7 +1003,8 @@
 | 
				
			|||||||
        <param name='user' doc='Generic facility' />
 | 
					        <param name='user' doc='Generic facility' />
 | 
				
			||||||
        <param name='uucp' doc='UUCP facility' />
 | 
					        <param name='uucp' doc='UUCP facility' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>    <command id='log syslog local <0-7>'>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='log syslog local <0-7>'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='log' doc='Configure logging sub-system' />
 | 
					        <param name='log' doc='Configure logging sub-system' />
 | 
				
			||||||
        <param name='syslog' doc='Logging via syslog' />
 | 
					        <param name='syslog' doc='Logging via syslog' />
 | 
				
			||||||
@@ -905,6 +1026,43 @@
 | 
				
			|||||||
        <param name='[HOSTNAME]' doc='Host name to send the GSMTAP logging to (UDP port 4729)' />
 | 
					        <param name='[HOSTNAME]' doc='Host name to send the GSMTAP logging to (UDP port 4729)' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='stats reporter statsd'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='stats' doc='Configure stats sub-system' />
 | 
				
			||||||
 | 
					        <param name='reporter' doc='Configure a stats reporter' />
 | 
				
			||||||
 | 
					        <param name='statsd' doc='Report to a STATSD server' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no stats reporter statsd'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Negate a command or set its defaults' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Configure stats sub-system' />
 | 
				
			||||||
 | 
					        <param name='reporter' doc='Configure a stats reporter' />
 | 
				
			||||||
 | 
					        <param name='statsd' doc='Report to a STATSD server' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='stats reporter log'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='stats' doc='Configure stats sub-system' />
 | 
				
			||||||
 | 
					        <param name='reporter' doc='Configure a stats reporter' />
 | 
				
			||||||
 | 
					        <param name='log' doc='Report to the logger' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no stats reporter log'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Negate a command or set its defaults' />
 | 
				
			||||||
 | 
					        <param name='stats' doc='Configure stats sub-system' />
 | 
				
			||||||
 | 
					        <param name='reporter' doc='Configure a stats reporter' />
 | 
				
			||||||
 | 
					        <param name='log' doc='Report to the logger' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='stats interval <1-65535>'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='stats' doc='Configure stats sub-system' />
 | 
				
			||||||
 | 
					        <param name='interval' doc='Set the reporting interval' />
 | 
				
			||||||
 | 
					        <param name='<1-65535>' doc='Interval in seconds' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
    <command id='hlr'>
 | 
					    <command id='hlr'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='hlr' doc='Configure the HLR' />
 | 
					        <param name='hlr' doc='Configure the HLR' />
 | 
				
			||||||
@@ -985,7 +1143,7 @@
 | 
				
			|||||||
        <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
 | 
					        <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf) (debug|info|notice|error|fatal)'>
 | 
					    <command id='logging level (main|db|auc|ss|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='logging' doc='Configure logging' />
 | 
					        <param name='logging' doc='Configure logging' />
 | 
				
			||||||
        <param name='level' doc='Set the log level for a specified category' />
 | 
					        <param name='level' doc='Set the log level for a specified category' />
 | 
				
			||||||
@@ -1011,6 +1169,7 @@
 | 
				
			|||||||
        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
					        <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
 | 
				
			||||||
        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
					        <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
 | 
				
			||||||
        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
					        <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
 | 
				
			||||||
 | 
					        <param name='lrspro' doc='Remote SIM protocol' />
 | 
				
			||||||
        <param name='debug' doc='Log debug messages and higher levels' />
 | 
					        <param name='debug' doc='Log debug messages and higher levels' />
 | 
				
			||||||
        <param name='info' doc='Log informational messages and higher levels' />
 | 
					        <param name='info' doc='Log informational messages and higher levels' />
 | 
				
			||||||
        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
					        <param name='notice' doc='Log noticeable messages and higher levels' />
 | 
				
			||||||
@@ -1051,6 +1210,75 @@
 | 
				
			|||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
  </node>
 | 
					  </node>
 | 
				
			||||||
 | 
					  <node id='config-stats'>
 | 
				
			||||||
 | 
					    <name>config-stats</name>
 | 
				
			||||||
 | 
					    <command id='local-ip ADDR'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='local-ip' doc='Set the IP address to which we bind locally' />
 | 
				
			||||||
 | 
					        <param name='ADDR' doc='IP Address' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no local-ip'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Negate a command or set its defaults' />
 | 
				
			||||||
 | 
					        <param name='local-ip' doc='Set the IP address to which we bind locally' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='remote-ip ADDR'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='remote-ip' doc='Set the remote IP address to which we connect' />
 | 
				
			||||||
 | 
					        <param name='ADDR' doc='IP Address' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='remote-port <1-65535>'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='remote-port' doc='Set the remote port to which we connect' />
 | 
				
			||||||
 | 
					        <param name='<1-65535>' doc='Remote port number' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='mtu <100-65535>'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='mtu' doc='Set the maximum packet size' />
 | 
				
			||||||
 | 
					        <param name='<100-65535>' doc='Size in byte' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no mtu'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Negate a command or set its defaults' />
 | 
				
			||||||
 | 
					        <param name='mtu' doc='Set the maximum packet size' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='prefix PREFIX'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='prefix' doc='Set the item name prefix' />
 | 
				
			||||||
 | 
					        <param name='PREFIX' doc='The prefix string' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no prefix'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Negate a command or set its defaults' />
 | 
				
			||||||
 | 
					        <param name='prefix' doc='Set the item name prefix' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='level (global|peer|subscriber)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='level' doc='Set the maximum group level' />
 | 
				
			||||||
 | 
					        <param name='global' doc='Report global groups only' />
 | 
				
			||||||
 | 
					        <param name='peer' doc='Report global and network peer related groups' />
 | 
				
			||||||
 | 
					        <param name='subscriber' doc='Report global, peer, and subscriber groups' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='enable'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='enable' doc='Enable the reporter' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='disable'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='disable' doc='Disable the reporter' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					  </node>
 | 
				
			||||||
  <node id='config-line'>
 | 
					  <node id='config-line'>
 | 
				
			||||||
    <name>config-line</name>
 | 
					    <name>config-line</name>
 | 
				
			||||||
    <command id='login'>
 | 
					    <command id='login'>
 | 
				
			||||||
@@ -1064,10 +1292,11 @@
 | 
				
			|||||||
        <param name='login' doc='Enable password checking' />
 | 
					        <param name='login' doc='Enable password checking' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
    <command id='bind A.B.C.D'>
 | 
					    <command id='bind A.B.C.D [<0-65535>]'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='bind' doc='Accept VTY telnet connections on local interface' />
 | 
					        <param name='bind' doc='Accept VTY telnet connections on local interface' />
 | 
				
			||||||
        <param name='A.B.C.D' doc='Local interface IP address (default: 127.0.0.1)' />
 | 
					        <param name='A.B.C.D' doc='Local interface IP address (default: 127.0.0.1)' />
 | 
				
			||||||
 | 
					        <param name='[<0-65535>]' doc='Local TCP port number' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
  </node>
 | 
					  </node>
 | 
				
			||||||
@@ -1087,6 +1316,12 @@
 | 
				
			|||||||
        <param name='gsup' doc='Configure GSUP options' />
 | 
					        <param name='gsup' doc='Configure GSUP options' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='database PATH'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='database' doc='Set the path to the HLR database file' />
 | 
				
			||||||
 | 
					        <param name='PATH' doc='Relative or absolute file system path to the database file (default is 'hlr.db')' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
    <command id='euse NAME'>
 | 
					    <command id='euse NAME'>
 | 
				
			||||||
      <params>
 | 
					      <params>
 | 
				
			||||||
        <param name='euse' doc='Configure a particular External USSD Entity' />
 | 
					        <param name='euse' doc='Configure a particular External USSD Entity' />
 | 
				
			||||||
@@ -1145,6 +1380,40 @@
 | 
				
			|||||||
        <param name='default-route' doc='Remove the default-route for all USSD to unknown destinations' />
 | 
					        <param name='default-route' doc='Remove the default-route for all USSD to unknown destinations' />
 | 
				
			||||||
      </params>
 | 
					      </params>
 | 
				
			||||||
    </command>
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='ncss-guard-timeout <0-255>'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='ncss-guard-timeout' doc='Set guard timer for NCSS (call independent SS) session activity' />
 | 
				
			||||||
 | 
					        <param name='<0-255>' doc='Guard timer value (sec.), or 0 to disable' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='store-imei'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='store-imei' doc='Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send Check IMEI requests (for OsmoMSC, you may want to set 'check-imei-rqd 1').' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no store-imei'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Do not save the IMEI in the database, when receiving Check IMEI requests.' />
 | 
				
			||||||
 | 
					        <param name='store-imei' doc='(null)' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='subscriber-create-on-demand' doc='Make a new record when a subscriber is first seen.' />
 | 
				
			||||||
 | 
					        <param name='no-msisdn' doc='Do not automatically assign MSISDN.' />
 | 
				
			||||||
 | 
					        <param name='<3-15>' doc='Length of an automatically assigned MSISDN.' />
 | 
				
			||||||
 | 
					        <param name='none' doc='Do not allow any NAM (Network Access Mode) by default.' />
 | 
				
			||||||
 | 
					        <param name='cs' doc='Allow access to circuit switched NAM by default.' />
 | 
				
			||||||
 | 
					        <param name='ps' doc='Allow access to packet switched NAM by default.' />
 | 
				
			||||||
 | 
					        <param name='cs+ps' doc='Allow access to circuit and packet switched NAM by default.' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
 | 
					    <command id='no subscriber-create-on-demand'>
 | 
				
			||||||
 | 
					      <params>
 | 
				
			||||||
 | 
					        <param name='no' doc='Do not make a new record when a subscriber is first seen.' />
 | 
				
			||||||
 | 
					        <param name='subscriber-create-on-demand' doc='(null)' />
 | 
				
			||||||
 | 
					      </params>
 | 
				
			||||||
 | 
					    </command>
 | 
				
			||||||
  </node>
 | 
					  </node>
 | 
				
			||||||
  <node id='config-hlr-gsup'>
 | 
					  <node id='config-hlr-gsup'>
 | 
				
			||||||
    <name>config-hlr-gsup</name>
 | 
					    <name>config-hlr-gsup</name>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								sql/hlr.sql
									
									
									
									
									
								
							@@ -5,8 +5,10 @@ CREATE TABLE subscriber (
 | 
				
			|||||||
	imsi		VARCHAR(15) UNIQUE NOT NULL,
 | 
						imsi		VARCHAR(15) UNIQUE NOT NULL,
 | 
				
			||||||
	-- Chapter 2.1.2
 | 
						-- Chapter 2.1.2
 | 
				
			||||||
	msisdn		VARCHAR(15) UNIQUE,
 | 
						msisdn		VARCHAR(15) UNIQUE,
 | 
				
			||||||
	-- Chapter 2.2.3: Most recent / current IMEI
 | 
						-- Chapter 2.2.3: Most recent / current IMEISV
 | 
				
			||||||
	imeisv		VARCHAR,
 | 
						imeisv		VARCHAR,
 | 
				
			||||||
 | 
						-- Chapter 2.1.9: Most recent / current IMEI
 | 
				
			||||||
 | 
						imei		VARCHAR(14),
 | 
				
			||||||
	-- Chapter 2.4.5
 | 
						-- Chapter 2.4.5
 | 
				
			||||||
	vlr_number	VARCHAR(15),
 | 
						vlr_number	VARCHAR(15),
 | 
				
			||||||
	-- Chapter 2.4.6
 | 
						-- Chapter 2.4.6
 | 
				
			||||||
@@ -40,7 +42,12 @@ CREATE TABLE subscriber (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	-- Timestamp of last location update seen from subscriber
 | 
						-- Timestamp of last location update seen from subscriber
 | 
				
			||||||
	-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
						-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
				
			||||||
	last_lu_seen TIMESTAMP default NULL
 | 
						last_lu_seen TIMESTAMP default NULL,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Last 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	TEXT default NULL
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE subscriber_apn (
 | 
					CREATE TABLE subscriber_apn (
 | 
				
			||||||
@@ -70,8 +77,17 @@ CREATE TABLE auc_3g (
 | 
				
			|||||||
	ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end
 | 
						ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- 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')) NOT NULL,	-- Radio Access Technology, see enum ran_type
 | 
				
			||||||
 | 
						allowed		BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
					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
 | 
					-- Set HLR database schema version number
 | 
				
			||||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
					-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
				
			||||||
PRAGMA user_version = 1;
 | 
					PRAGMA user_version = 4;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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;
 | 
				
			||||||
@@ -87,21 +87,6 @@ osmo_hlr_db_tool_LDADD = \
 | 
				
			|||||||
	$(SQLITE3_LIBS) \
 | 
						$(SQLITE3_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_test_SOURCES = \
 | 
					 | 
				
			||||||
	auc.c \
 | 
					 | 
				
			||||||
	db.c \
 | 
					 | 
				
			||||||
	db_auc.c \
 | 
					 | 
				
			||||||
	db_test.c \
 | 
					 | 
				
			||||||
	logging.c \
 | 
					 | 
				
			||||||
	rand_fake.c \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
db_test_LDADD = \
 | 
					 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
					 | 
				
			||||||
	$(LIBOSMOGSM_LIBS) \
 | 
					 | 
				
			||||||
	$(SQLITE3_LIBS) \
 | 
					 | 
				
			||||||
	$(NULL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
osmo_euse_demo_SOURCES = \
 | 
					osmo_euse_demo_SOURCES = \
 | 
				
			||||||
	osmo-euse-demo.c \
 | 
						osmo-euse-demo.c \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
@@ -112,6 +97,11 @@ osmo_euse_demo_LDADD = \
 | 
				
			|||||||
	$(LIBOSMOGSM_LIBS) \
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if DB_SQLITE_DEBUG
 | 
				
			||||||
 | 
					osmo_hlr_SOURCES += db_debug.c
 | 
				
			||||||
 | 
					osmo_hlr_db_tool_SOURCES += db_debug.c
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
 | 
					BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed
 | 
					db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,7 +95,7 @@ static bool get_subscriber(struct db_context *dbc,
 | 
				
			|||||||
		cmd->reply = "No such subscriber.";
 | 
							cmd->reply = "No such subscriber.";
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		cmd->reply = "An unknown error has occured during get_subscriber().";
 | 
							cmd->reply = "An unknown error has occurred during get_subscriber().";
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										182
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								src/db.c
									
									
									
									
									
								
							@@ -28,12 +28,13 @@
 | 
				
			|||||||
#include "db_bootstrap.h"
 | 
					#include "db_bootstrap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
					/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
				
			||||||
#define CURRENT_SCHEMA_VERSION	1
 | 
					#define CURRENT_SCHEMA_VERSION	4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SEL_COLUMNS \
 | 
					#define SEL_COLUMNS \
 | 
				
			||||||
	"id," \
 | 
						"id," \
 | 
				
			||||||
	"imsi," \
 | 
						"imsi," \
 | 
				
			||||||
	"msisdn," \
 | 
						"msisdn," \
 | 
				
			||||||
 | 
						"imei," \
 | 
				
			||||||
	"vlr_number," \
 | 
						"vlr_number," \
 | 
				
			||||||
	"sgsn_number," \
 | 
						"sgsn_number," \
 | 
				
			||||||
	"sgsn_address," \
 | 
						"sgsn_address," \
 | 
				
			||||||
@@ -44,14 +45,17 @@
 | 
				
			|||||||
	"lmsi," \
 | 
						"lmsi," \
 | 
				
			||||||
	"ms_purged_cs," \
 | 
						"ms_purged_cs," \
 | 
				
			||||||
	"ms_purged_ps," \
 | 
						"ms_purged_ps," \
 | 
				
			||||||
	"last_lu_seen"
 | 
						"last_lu_seen," \
 | 
				
			||||||
 | 
						"last_lu_rat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *stmt_sql[] = {
 | 
					static const char *stmt_sql[] = {
 | 
				
			||||||
	[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
						[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
				
			||||||
	[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 | 
						[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 | 
				
			||||||
	[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
 | 
						[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
 | 
				
			||||||
 | 
						[DB_STMT_SEL_BY_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei = ?",
 | 
				
			||||||
	[DB_STMT_UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = $number WHERE id = $subscriber_id",
 | 
						[DB_STMT_UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = $number WHERE id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number WHERE id = $subscriber_id",
 | 
						[DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number WHERE id = $subscriber_id",
 | 
				
			||||||
 | 
						[DB_STMT_UPD_IMEI_BY_IMSI] = "UPDATE subscriber SET imei = $imei WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_AUC_BY_IMSI] =
 | 
						[DB_STMT_AUC_BY_IMSI] =
 | 
				
			||||||
		"SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen"
 | 
							"SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen"
 | 
				
			||||||
		" FROM subscriber"
 | 
							" FROM subscriber"
 | 
				
			||||||
@@ -63,7 +67,7 @@ static const char *stmt_sql[] = {
 | 
				
			|||||||
	[DB_STMT_UPD_PURGE_PS_BY_IMSI] = "UPDATE subscriber SET ms_purged_ps = $val WHERE imsi = $imsi",
 | 
						[DB_STMT_UPD_PURGE_PS_BY_IMSI] = "UPDATE subscriber SET ms_purged_ps = $val WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_UPD_NAM_CS_BY_IMSI] = "UPDATE subscriber SET nam_cs = $val WHERE imsi = $imsi",
 | 
						[DB_STMT_UPD_NAM_CS_BY_IMSI] = "UPDATE subscriber SET nam_cs = $val WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_UPD_NAM_PS_BY_IMSI] = "UPDATE subscriber SET nam_ps = $val WHERE imsi = $imsi",
 | 
						[DB_STMT_UPD_NAM_PS_BY_IMSI] = "UPDATE subscriber SET nam_ps = $val WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi) VALUES ($imsi)",
 | 
						[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi, nam_cs, nam_ps) VALUES ($imsi, $nam_cs, $nam_ps)",
 | 
				
			||||||
	[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
 | 
						[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
 | 
						[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
 | 
				
			||||||
	[DB_STMT_DELETE_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = NULL WHERE imsi = $imsi",
 | 
						[DB_STMT_DELETE_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = NULL WHERE imsi = $imsi",
 | 
				
			||||||
@@ -75,7 +79,16 @@ static const char *stmt_sql[] = {
 | 
				
			|||||||
		"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
 | 
							"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)",
 | 
							" 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_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] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'), last_lu_rat = $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_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)
 | 
					static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
				
			||||||
@@ -290,6 +303,119 @@ db_upgrade_v1(struct db_context *dbc)
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int db_upgrade_v2(struct db_context *dbc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN imei VARCHAR(14) default NULL";
 | 
				
			||||||
 | 
						const char *set_schema_version_sql = "PRAGMA user_version = 2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_prepare_v2(dbc->db, update_stmt_sql, -1, &stmt, NULL);
 | 
				
			||||||
 | 
						if (rc != SQLITE_OK) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", update_stmt_sql);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_prepare_v2(dbc->db, set_schema_version_sql, -1, &stmt, NULL);
 | 
				
			||||||
 | 
						if (rc != SQLITE_OK) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", set_schema_version_sql);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE)
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int db_upgrade_v3(struct db_context *dbc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						const char *stmts[] = {
 | 
				
			||||||
 | 
							"CREATE TABLE subscriber_rat",
 | 
				
			||||||
 | 
							"CREATE UNIQUE INDEX idx_subscr_rat_flag",
 | 
				
			||||||
 | 
							NULL
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(stmts); i++) {
 | 
				
			||||||
 | 
							int rc;
 | 
				
			||||||
 | 
							int j;
 | 
				
			||||||
 | 
							const char *stmt_sql = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (stmts[i] != NULL) {
 | 
				
			||||||
 | 
								for (j = 0; j < ARRAY_SIZE(stmt_bootstrap_sql); j++) {
 | 
				
			||||||
 | 
									if (strstr(stmt_bootstrap_sql[j], stmts[i])) {
 | 
				
			||||||
 | 
										/* make sure we have a unique match, hence also not break; here */
 | 
				
			||||||
 | 
										OSMO_ASSERT(!stmt_sql);
 | 
				
			||||||
 | 
										stmt_sql = stmt_bootstrap_sql[j];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								stmt_sql = "PRAGMA user_version = 3";
 | 
				
			||||||
 | 
							OSMO_ASSERT(stmt_sql);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = sqlite3_prepare_v2(dbc->db, stmt_sql, -1, &stmt, NULL);
 | 
				
			||||||
 | 
							if (rc != SQLITE_OK) {
 | 
				
			||||||
 | 
								LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_sql);
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
							db_remove_reset(stmt);
 | 
				
			||||||
 | 
							sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
							if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
								LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 2: '%s'\n",
 | 
				
			||||||
 | 
								     stmt_sql);
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SQLITE_DONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int db_upgrade_v4(struct db_context *dbc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN last_lu_rat TEXT default NULL";
 | 
				
			||||||
 | 
						const char *set_schema_version_sql = "PRAGMA user_version = 4";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_prepare_v2(dbc->db, update_stmt_sql, -1, &stmt, NULL);
 | 
				
			||||||
 | 
						if (rc != SQLITE_OK) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", update_stmt_sql);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_prepare_v2(dbc->db, set_schema_version_sql, -1, &stmt, NULL);
 | 
				
			||||||
 | 
						if (rc != SQLITE_OK) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", set_schema_version_sql);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE)
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 4\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int db_get_user_version(struct db_context *dbc)
 | 
					static int db_get_user_version(struct db_context *dbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *user_version_sql = "PRAGMA user_version";
 | 
						const char *user_version_sql = "PRAGMA user_version";
 | 
				
			||||||
@@ -326,6 +452,17 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
	LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
 | 
						LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
 | 
				
			||||||
	LOGP(DDB, LOGL_INFO, "Running with SQLite3 lib version %s\n", sqlite3_libversion());
 | 
						LOGP(DDB, LOGL_INFO, "Running with SQLite3 lib version %s\n", sqlite3_libversion());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef SQLITE_USE_TALLOC
 | 
				
			||||||
 | 
						/* Configure SQLite3 to use talloc memory allocator */
 | 
				
			||||||
 | 
						rc = db_sqlite3_use_talloc(ctx);
 | 
				
			||||||
 | 
						if (rc == SQLITE_OK) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_NOTICE, "SQLite3 is configured to use talloc\n");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Failed to configure SQLite3 "
 | 
				
			||||||
 | 
							     "to use talloc, using default memory allocator\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbc->fname = talloc_strdup(dbc, fname);
 | 
						dbc->fname = talloc_strdup(dbc, fname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < 0xfffff; i++) {
 | 
						for (i = 0; i < 0xfffff; i++) {
 | 
				
			||||||
@@ -390,6 +527,8 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
	LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
 | 
						LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
 | 
						if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
 | 
				
			||||||
 | 
							int orig_version = version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (version) {
 | 
							switch (version) {
 | 
				
			||||||
		case 0:
 | 
							case 0:
 | 
				
			||||||
			rc = db_upgrade_v1(dbc);
 | 
								rc = db_upgrade_v1(dbc);
 | 
				
			||||||
@@ -400,21 +539,48 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			version = 1;
 | 
								version = 1;
 | 
				
			||||||
			/* fall through */
 | 
								/* fall through */
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								rc = db_upgrade_v2(dbc);
 | 
				
			||||||
 | 
								if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
									LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 2: (rc=%d) %s\n",
 | 
				
			||||||
 | 
									     rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
									goto out_free;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								version = 2;
 | 
				
			||||||
 | 
								/* fall through */
 | 
				
			||||||
 | 
							case 2:
 | 
				
			||||||
 | 
								rc = db_upgrade_v3(dbc);
 | 
				
			||||||
 | 
								if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
									LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 2: (rc=%d) %s\n",
 | 
				
			||||||
 | 
									     rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
									goto out_free;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								version = 3;
 | 
				
			||||||
 | 
								/* fall through */
 | 
				
			||||||
 | 
							case 3:
 | 
				
			||||||
 | 
								rc = db_upgrade_v4(dbc);
 | 
				
			||||||
 | 
								if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
									LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 4: (rc=%d) %s\n",
 | 
				
			||||||
 | 
									     rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
									goto out_free;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								version = 4;
 | 
				
			||||||
 | 
								/* fall through */
 | 
				
			||||||
		/* case N: ... */
 | 
							/* case N: ... */
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n",
 | 
							LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded from HLR DB schema version %d to %d\n",
 | 
				
			||||||
		     dbc->fname, version);
 | 
							     dbc->fname, orig_version, version);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (version != CURRENT_SCHEMA_VERSION) {
 | 
						if (version != CURRENT_SCHEMA_VERSION) {
 | 
				
			||||||
		if (version < CURRENT_SCHEMA_VERSION) {
 | 
							if (version < CURRENT_SCHEMA_VERSION) {
 | 
				
			||||||
			LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
 | 
								LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
 | 
				
			||||||
			if (!allow_upgrade) {
 | 
								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",
 | 
									     "use the --db-upgrade option to allow HLR database upgrades\n",
 | 
				
			||||||
				     CURRENT_SCHEMA_VERSION);
 | 
									     version, CURRENT_SCHEMA_VERSION);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
 | 
								LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								src/db.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								src/db.h
									
									
									
									
									
								
							@@ -3,14 +3,18 @@
 | 
				
			|||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <sqlite3.h>
 | 
					#include <sqlite3.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr;
 | 
					struct hlr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum stmt_idx {
 | 
					enum stmt_idx {
 | 
				
			||||||
	DB_STMT_SEL_BY_IMSI,
 | 
						DB_STMT_SEL_BY_IMSI,
 | 
				
			||||||
	DB_STMT_SEL_BY_MSISDN,
 | 
						DB_STMT_SEL_BY_MSISDN,
 | 
				
			||||||
	DB_STMT_SEL_BY_ID,
 | 
						DB_STMT_SEL_BY_ID,
 | 
				
			||||||
 | 
						DB_STMT_SEL_BY_IMEI,
 | 
				
			||||||
	DB_STMT_UPD_VLR_BY_ID,
 | 
						DB_STMT_UPD_VLR_BY_ID,
 | 
				
			||||||
	DB_STMT_UPD_SGSN_BY_ID,
 | 
						DB_STMT_UPD_SGSN_BY_ID,
 | 
				
			||||||
 | 
						DB_STMT_UPD_IMEI_BY_IMSI,
 | 
				
			||||||
	DB_STMT_AUC_BY_IMSI,
 | 
						DB_STMT_AUC_BY_IMSI,
 | 
				
			||||||
	DB_STMT_AUC_UPD_SQN,
 | 
						DB_STMT_AUC_UPD_SQN,
 | 
				
			||||||
	DB_STMT_UPD_PURGE_CS_BY_IMSI,
 | 
						DB_STMT_UPD_PURGE_CS_BY_IMSI,
 | 
				
			||||||
@@ -26,6 +30,10 @@ enum stmt_idx {
 | 
				
			|||||||
	DB_STMT_AUC_3G_INSERT,
 | 
						DB_STMT_AUC_3G_INSERT,
 | 
				
			||||||
	DB_STMT_AUC_3G_DELETE,
 | 
						DB_STMT_AUC_3G_DELETE,
 | 
				
			||||||
	DB_STMT_SET_LAST_LU_SEEN,
 | 
						DB_STMT_SET_LAST_LU_SEEN,
 | 
				
			||||||
 | 
						DB_STMT_EXISTS_BY_IMSI,
 | 
				
			||||||
 | 
						DB_STMT_EXISTS_BY_MSISDN,
 | 
				
			||||||
 | 
						DB_STMT_UPD_RAT_FLAG,
 | 
				
			||||||
 | 
						DB_STMT_RAT_BY_ID,
 | 
				
			||||||
	_NUM_DB_STMT
 | 
						_NUM_DB_STMT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,6 +43,11 @@ struct db_context {
 | 
				
			|||||||
	sqlite3_stmt *stmt[_NUM_DB_STMT];
 | 
						sqlite3_stmt *stmt[_NUM_DB_STMT];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Optional feature to make SQLite3 using talloc */
 | 
				
			||||||
 | 
					#ifdef SQLITE_USE_TALLOC
 | 
				
			||||||
 | 
					int db_sqlite3_use_talloc(void *ctx);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void db_remove_reset(sqlite3_stmt *stmt);
 | 
					void db_remove_reset(sqlite3_stmt *stmt);
 | 
				
			||||||
bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
 | 
					bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
 | 
				
			||||||
bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
 | 
					bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
 | 
				
			||||||
@@ -56,7 +69,7 @@ int db_update_sqn(struct db_context *dbc, int64_t id,
 | 
				
			|||||||
int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
					int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
				
			||||||
	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
 | 
						       unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
 | 
				
			||||||
	       unsigned int num_vec, const uint8_t *rand_auts,
 | 
						       unsigned int num_vec, const uint8_t *rand_auts,
 | 
				
			||||||
	       const uint8_t *auts);
 | 
						       const uint8_t *auts, bool separation_bit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
@@ -69,8 +82,9 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	int64_t		id;
 | 
						int64_t		id;
 | 
				
			||||||
	char		imsi[GSM23003_IMSI_MAX_DIGITS+1];
 | 
						char		imsi[GSM23003_IMSI_MAX_DIGITS+1];
 | 
				
			||||||
	char		msisdn[GT_MAX_DIGITS+1];
 | 
						char		msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
 | 
				
			||||||
	/* imeisv? */
 | 
						/* imeisv? */
 | 
				
			||||||
 | 
						char		imei[GSM23003_IMEI_NUM_DIGITS+1];
 | 
				
			||||||
	char		vlr_number[32];
 | 
						char		vlr_number[32];
 | 
				
			||||||
	char		sgsn_number[32];
 | 
						char		sgsn_number[32];
 | 
				
			||||||
	char		sgsn_address[GT_MAX_DIGITS+1];
 | 
						char		sgsn_address[GT_MAX_DIGITS+1];
 | 
				
			||||||
@@ -85,8 +99,14 @@ struct hlr_subscriber {
 | 
				
			|||||||
	bool		ms_purged_cs;
 | 
						bool		ms_purged_cs;
 | 
				
			||||||
	bool		ms_purged_ps;
 | 
						bool		ms_purged_ps;
 | 
				
			||||||
	time_t		last_lu_seen;
 | 
						time_t		last_lu_seen;
 | 
				
			||||||
 | 
						char		last_lu_rat[128];
 | 
				
			||||||
 | 
						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
 | 
					/* 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.
 | 
					 * used to parse the last_lu_seen column stored in the HLR database.
 | 
				
			||||||
 * See https://sqlite.org/lang_datefunc.html, function datetime(). */
 | 
					 * See https://sqlite.org/lang_datefunc.html, function datetime(). */
 | 
				
			||||||
@@ -115,13 +135,20 @@ struct sub_auth_data_str {
 | 
				
			|||||||
	} u;
 | 
						} u;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_create(struct db_context *dbc, const char *imsi);
 | 
					#define DB_SUBSCR_FLAG_NAM_CS	(1 << 1)
 | 
				
			||||||
 | 
					#define DB_SUBSCR_FLAG_NAM_PS	(1 << 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags);
 | 
				
			||||||
int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id);
 | 
					int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
 | 
					int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
 | 
				
			||||||
				    const char *msisdn);
 | 
									    const char *msisdn);
 | 
				
			||||||
int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
			       const struct sub_auth_data_str *aud);
 | 
								       const struct sub_auth_data_str *aud);
 | 
				
			||||||
 | 
					int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, const char *imei);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
 | 
				
			||||||
 | 
					int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
					int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
				
			||||||
			  struct hlr_subscriber *subscr);
 | 
								  struct hlr_subscriber *subscr);
 | 
				
			||||||
@@ -129,15 +156,21 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
 | 
				
			|||||||
			    struct hlr_subscriber *subscr);
 | 
								    struct hlr_subscriber *subscr);
 | 
				
			||||||
int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
					int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
				
			||||||
			struct hlr_subscriber *subscr);
 | 
								struct hlr_subscriber *subscr);
 | 
				
			||||||
 | 
					int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
 | 
				
			||||||
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
					int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const char *vlr_or_sgsn_number, bool is_ps);
 | 
							 const char *vlr_or_sgsn_number, bool is_ps,
 | 
				
			||||||
 | 
							 const enum osmo_rat_type rat_types[], size_t rat_types_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
					int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
				
			||||||
		    bool purge_val, bool is_ps);
 | 
							    bool purge_val, bool is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
					int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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[].
 | 
					/*! Call sqlite3_column_text() and copy result to a char[].
 | 
				
			||||||
 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
					 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
				
			||||||
 * \param[in] stmt  An sqlite3_stmt*.
 | 
					 * \param[in] stmt  An sqlite3_stmt*.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										77
									
								
								src/db_auc.c
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								src/db_auc.c
									
									
									
									
									
								
							@@ -73,6 +73,32 @@ out:
 | 
				
			|||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* hexparse a specific column of a sqlite prepared statement into dst (with length check)
 | 
				
			||||||
 | 
					 * returns 0 for success, -EIO on error */
 | 
				
			||||||
 | 
					static int hexparse_stmt(uint8_t *dst, size_t dst_len, sqlite3_stmt *stmt, int col, const char *col_name,
 | 
				
			||||||
 | 
								 const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const uint8_t *text;
 | 
				
			||||||
 | 
						size_t col_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Bytes are stored as hex strings in database, hence divide length by two */
 | 
				
			||||||
 | 
						col_len = sqlite3_column_bytes(stmt, col) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (col_len != dst_len) {
 | 
				
			||||||
 | 
							LOGAUC(imsi, LOGL_ERROR, "Error reading %s, expected length %lu but has length %lu\n", col_name,
 | 
				
			||||||
 | 
							       dst_len, col_len);
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						text = sqlite3_column_text(stmt, col);
 | 
				
			||||||
 | 
						if (!text) {
 | 
				
			||||||
 | 
							LOGAUC(imsi, LOGL_ERROR, "Error reading %s\n", col_name);
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						osmo_hexparse((void *)text, dst, dst_len);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* obtain the authentication data for a given imsi
 | 
					/* obtain the authentication data for a given imsi
 | 
				
			||||||
 * returns 0 for success, negative value on error:
 | 
					 * returns 0 for success, negative value on error:
 | 
				
			||||||
 * -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data,
 | 
					 * -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data,
 | 
				
			||||||
@@ -113,49 +139,34 @@ int db_get_auth_data(struct db_context *dbc, const char *imsi,
 | 
				
			|||||||
	/* obtain result values using sqlite3_column_*() */
 | 
						/* obtain result values using sqlite3_column_*() */
 | 
				
			||||||
	if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) {
 | 
						if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) {
 | 
				
			||||||
		/* we do have some 2G authentication data */
 | 
							/* we do have some 2G authentication data */
 | 
				
			||||||
		const uint8_t *ki;
 | 
							if (hexparse_stmt(aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki), stmt, 2, "Ki", imsi))
 | 
				
			||||||
 | 
					 | 
				
			||||||
		aud2g->algo = sqlite3_column_int(stmt, 1);
 | 
					 | 
				
			||||||
		ki = sqlite3_column_text(stmt, 2);
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
		if (sqlite3_column_bytes(stmt, 2) != sizeof(aud2g->u.gsm.ki)) {
 | 
					 | 
				
			||||||
			LOGAUC(imsi, LOGL_ERROR, "Error reading Ki: %d\n", rc);
 | 
					 | 
				
			||||||
			goto end_2g;
 | 
								goto end_2g;
 | 
				
			||||||
		}
 | 
							aud2g->algo = sqlite3_column_int(stmt, 1);
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		osmo_hexparse((void*)ki, (void*)&aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki));
 | 
					 | 
				
			||||||
		aud2g->type = OSMO_AUTH_TYPE_GSM;
 | 
							aud2g->type = OSMO_AUTH_TYPE_GSM;
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		LOGAUC(imsi, LOGL_DEBUG, "No 2G Auth Data\n");
 | 
							LOGAUC(imsi, LOGL_DEBUG, "No 2G Auth Data\n");
 | 
				
			||||||
//end_2g:
 | 
					end_2g:
 | 
				
			||||||
	if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) {
 | 
						if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) {
 | 
				
			||||||
		/* we do have some 3G authentication data */
 | 
							/* we do have some 3G authentication data */
 | 
				
			||||||
		const uint8_t *k, *op, *opc;
 | 
							if (hexparse_stmt(aud3g->u.umts.k, sizeof(aud3g->u.umts.k), stmt, 4, "K", imsi)) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
		aud3g->algo = sqlite3_column_int(stmt, 3);
 | 
					 | 
				
			||||||
		k = sqlite3_column_text(stmt, 4);
 | 
					 | 
				
			||||||
		if (!k) {
 | 
					 | 
				
			||||||
			LOGAUC(imsi, LOGL_ERROR, "Error reading K: %d\n", rc);
 | 
					 | 
				
			||||||
			ret = -EIO;
 | 
								ret = -EIO;
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		osmo_hexparse((void*)k, (void*)&aud3g->u.umts.k, sizeof(aud3g->u.umts.k));
 | 
							aud3g->algo = sqlite3_column_int(stmt, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* UMTS Subscribers can have either OP or OPC */
 | 
							/* UMTS Subscribers can have either OP or OPC */
 | 
				
			||||||
		op = sqlite3_column_text(stmt, 5);
 | 
							if (sqlite3_column_text(stmt, 5)) {
 | 
				
			||||||
		if (!op) {
 | 
								if (hexparse_stmt(aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc), stmt, 5, "OP", imsi)) {
 | 
				
			||||||
			opc = sqlite3_column_text(stmt, 6);
 | 
					 | 
				
			||||||
			if (!opc) {
 | 
					 | 
				
			||||||
				LOGAUC(imsi, LOGL_ERROR, "Error reading OPC: %d\n", rc);
 | 
					 | 
				
			||||||
				ret = -EIO;
 | 
									ret = -EIO;
 | 
				
			||||||
				goto out;
 | 
									goto out;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			osmo_hexparse((void*)opc, (void*)&aud3g->u.umts.opc,
 | 
					 | 
				
			||||||
					sizeof(aud3g->u.umts.opc));
 | 
					 | 
				
			||||||
			aud3g->u.umts.opc_is_op = 0;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			osmo_hexparse((void*)op, (void*)&aud3g->u.umts.opc,
 | 
					 | 
				
			||||||
					sizeof(aud3g->u.umts.opc));
 | 
					 | 
				
			||||||
			aud3g->u.umts.opc_is_op = 1;
 | 
								aud3g->u.umts.opc_is_op = 1;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (hexparse_stmt(aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc), stmt, 6, "OPC", imsi)) {
 | 
				
			||||||
 | 
									ret = -EIO;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								aud3g->u.umts.opc_is_op = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7);
 | 
							aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7);
 | 
				
			||||||
		aud3g->u.umts.ind_bitlen = sqlite3_column_int(stmt, 8);
 | 
							aud3g->u.umts.ind_bitlen = sqlite3_column_int(stmt, 8);
 | 
				
			||||||
@@ -178,7 +189,7 @@ out:
 | 
				
			|||||||
int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
					int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
				
			||||||
	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
 | 
						       unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
 | 
				
			||||||
	       unsigned int num_vec, const uint8_t *rand_auts,
 | 
						       unsigned int num_vec, const uint8_t *rand_auts,
 | 
				
			||||||
	       const uint8_t *auts)
 | 
						       const uint8_t *auts, bool separation_bit)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g, aud3g;
 | 
						struct osmo_sub_auth_data aud2g, aud3g;
 | 
				
			||||||
	int64_t subscr_id;
 | 
						int64_t subscr_id;
 | 
				
			||||||
@@ -198,6 +209,12 @@ int db_get_auc(struct db_context *dbc, const char *imsi,
 | 
				
			|||||||
		       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind);
 | 
							       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind);
 | 
				
			||||||
		aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
 | 
							aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						/* the first bit (bit0) cannot be used as AMF anymore, but has been
 | 
				
			||||||
 | 
						 * re-appropriated as the separation bit.  See 3GPP TS 33.102 Annex H
 | 
				
			||||||
 | 
						 * together with 3GPP TS 33.401 / 33.402 / 33.501 */
 | 
				
			||||||
 | 
						aud3g.u.umts.amf[0] = aud3g.u.umts.amf[0] & 0x7f;
 | 
				
			||||||
 | 
						if (separation_bit)
 | 
				
			||||||
 | 
							aud3g.u.umts.amf[0] |= 0x80;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec);
 | 
						LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec);
 | 
				
			||||||
	rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts);
 | 
						rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								src/db_debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/db_debug.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * libtalloc based memory allocator for SQLite3.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * (C) 2019 by Vadim Yanitskiy <axilirator@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sqlite3.h>
 | 
				
			||||||
 | 
					#include <talloc.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Dedicated talloc context for SQLite */
 | 
				
			||||||
 | 
					static void *db_sqlite_ctx = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void *tall_xMalloc(int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return talloc_size(db_sqlite_ctx, size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tall_xFree(void *ptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						talloc_free(ptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void *tall_xRealloc(void *ptr, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return talloc_realloc_fn(db_sqlite_ctx, ptr, size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tall_xSize(void *ptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return talloc_total_size(ptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DUMMY: talloc doesn't round up the allocation size */
 | 
				
			||||||
 | 
					static int tall_xRoundup(int size) { return size; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DUMMY: nothing to initialize */
 | 
				
			||||||
 | 
					static int tall_xInit(void *data) { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DUMMY: nothing to deinitialize */
 | 
				
			||||||
 | 
					static void tall_xShutdown(void *data) {  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Interface between SQLite and talloc memory allocator */
 | 
				
			||||||
 | 
					static const struct sqlite3_mem_methods tall_sqlite_if = {
 | 
				
			||||||
 | 
						/* Memory allocation function */
 | 
				
			||||||
 | 
						.xMalloc = &tall_xMalloc,
 | 
				
			||||||
 | 
						/* Free a prior allocation */
 | 
				
			||||||
 | 
						.xFree = &tall_xFree,
 | 
				
			||||||
 | 
						/* Resize an allocation */
 | 
				
			||||||
 | 
						.xRealloc = &tall_xRealloc,
 | 
				
			||||||
 | 
						/* Return the size of an allocation */
 | 
				
			||||||
 | 
						.xSize = &tall_xSize,
 | 
				
			||||||
 | 
						/* Round up request size to allocation size */
 | 
				
			||||||
 | 
						.xRoundup = &tall_xRoundup,
 | 
				
			||||||
 | 
						/* Initialize the memory allocator */
 | 
				
			||||||
 | 
						.xInit = &tall_xInit,
 | 
				
			||||||
 | 
						/* Deinitialize the memory allocator */
 | 
				
			||||||
 | 
						.xShutdown = &tall_xShutdown,
 | 
				
			||||||
 | 
						/* Argument to xInit() and xShutdown() */
 | 
				
			||||||
 | 
						.pAppData = NULL,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_sqlite3_use_talloc(void *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (db_sqlite_ctx != NULL)
 | 
				
			||||||
 | 
							return -EEXIST;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db_sqlite_ctx = talloc_named_const(ctx, 0, "SQLite3");
 | 
				
			||||||
 | 
						return sqlite3_config(SQLITE_CONFIG_MALLOC, &tall_sqlite_if);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										299
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										299
									
								
								src/db_hlr.c
									
									
									
									
									
								
							@@ -44,9 +44,10 @@
 | 
				
			|||||||
/*! Add new subscriber record to the HLR database.
 | 
					/*! Add new subscriber record to the HLR database.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] imsi  ASCII string of IMSI digits, is validated.
 | 
					 * \param[in] imsi  ASCII string of IMSI digits, is validated.
 | 
				
			||||||
 | 
					 * \param[in] flags  Bitmask of DB_SUBSCR_FLAG_*.
 | 
				
			||||||
 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
 | 
					 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int db_subscr_create(struct db_context *dbc, const char *imsi)
 | 
					int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
@@ -61,6 +62,10 @@ int db_subscr_create(struct db_context *dbc, const char *imsi)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (!db_bind_text(stmt, "$imsi", imsi))
 | 
						if (!db_bind_text(stmt, "$imsi", imsi))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						if (!db_bind_int(stmt, "$nam_cs", (flags & DB_SUBSCR_FLAG_NAM_CS) != 0))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						if (!db_bind_int(stmt, "$nam_ps", (flags & DB_SUBSCR_FLAG_NAM_PS) != 0))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* execute the statement */
 | 
						/* execute the statement */
 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
@@ -141,8 +146,8 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/*! Set a subscriber's MSISDN in the HLR database.
 | 
					/*! Set a subscriber's MSISDN in the HLR database.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] imsi  ASCII string of IMSI digits, or NULL to remove the MSISDN.
 | 
					 * \param[in] imsi  ASCII string of IMSI digits
 | 
				
			||||||
 * \param[in] msisdn  ASCII string of MSISDN digits.
 | 
					 * \param[in] msisdn  ASCII string of MSISDN digits, or NULL to remove the MSISDN.
 | 
				
			||||||
 * \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
 | 
					 * \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
 | 
				
			||||||
 *          database failure, -ENOENT if no such subscriber exists.
 | 
					 *          database failure, -ENOENT if no such subscriber exists.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -386,6 +391,53 @@ out:
 | 
				
			|||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Set a subscriber's IMEI in the HLR database.
 | 
				
			||||||
 | 
					 * \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_imei_by_imsi(struct db_context *dbc, const char* imsi, const char *imei)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc, ret = 0;
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_IMEI_BY_IMSI];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (imei && !osmo_imei_str_valid(imei, false)) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot update subscriber IMSI='%s': invalid IMEI: '%s'\n", imsi, imei);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, "$imsi", imsi))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						if (imei && !db_bind_text(stmt, "$imei", imei))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* execute the statement */
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_ERROR, "Update IMEI 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 IMEI for subscriber IMSI='%s': no such subscriber\n", imsi);
 | 
				
			||||||
 | 
							ret = -ENOENT;
 | 
				
			||||||
 | 
						} else if (rc != 1) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_ERROR, "Update IMEI for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
 | 
				
			||||||
 | 
							     imsi, rc);
 | 
				
			||||||
 | 
							ret = -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Common code for db_subscr_get_by_*() functions. */
 | 
					/* Common code for db_subscr_get_by_*() functions. */
 | 
				
			||||||
static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
 | 
					static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
 | 
				
			||||||
		  const char **err)
 | 
							  const char **err)
 | 
				
			||||||
@@ -393,7 +445,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
				
			|||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	const char *last_lu_seen_str;
 | 
						const char *last_lu_seen_str;
 | 
				
			||||||
	struct tm tm;
 | 
						struct tm tm = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* execute the statement */
 | 
						/* execute the statement */
 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
@@ -409,24 +461,25 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
				
			|||||||
	if (!subscr)
 | 
						if (!subscr)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*subscr = (struct hlr_subscriber){};
 | 
						*subscr = hlr_subscriber_empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* obtain the various columns */
 | 
						/* obtain the various columns */
 | 
				
			||||||
	subscr->id = sqlite3_column_int64(stmt, 0);
 | 
						subscr->id = sqlite3_column_int64(stmt, 0);
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr->imsi, stmt, 1);
 | 
						copy_sqlite3_text_to_buf(subscr->imsi, stmt, 1);
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr->msisdn, stmt, 2);
 | 
						copy_sqlite3_text_to_buf(subscr->msisdn, stmt, 2);
 | 
				
			||||||
 | 
						copy_sqlite3_text_to_buf(subscr->imei, stmt, 3);
 | 
				
			||||||
	/* FIXME: These should all be BLOBs as they might contain NUL */
 | 
						/* FIXME: These should all be BLOBs as they might contain NUL */
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr->vlr_number, stmt, 3);
 | 
						copy_sqlite3_text_to_buf(subscr->vlr_number, stmt, 4);
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr->sgsn_number, stmt, 4);
 | 
						copy_sqlite3_text_to_buf(subscr->sgsn_number, stmt, 5);
 | 
				
			||||||
	copy_sqlite3_text_to_buf(subscr->sgsn_address, stmt, 5);
 | 
						copy_sqlite3_text_to_buf(subscr->sgsn_address, stmt, 6);
 | 
				
			||||||
	subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
 | 
						subscr->periodic_lu_timer = sqlite3_column_int(stmt, 7);
 | 
				
			||||||
	subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
 | 
						subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 8);
 | 
				
			||||||
	subscr->nam_cs = sqlite3_column_int(stmt, 8);
 | 
						subscr->nam_cs = sqlite3_column_int(stmt, 9);
 | 
				
			||||||
	subscr->nam_ps = sqlite3_column_int(stmt, 9);
 | 
						subscr->nam_ps = sqlite3_column_int(stmt, 10);
 | 
				
			||||||
	subscr->lmsi = sqlite3_column_int(stmt, 10);
 | 
						subscr->lmsi = sqlite3_column_int(stmt, 11);
 | 
				
			||||||
	subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
 | 
						subscr->ms_purged_cs = sqlite3_column_int(stmt, 12);
 | 
				
			||||||
	subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
 | 
						subscr->ms_purged_ps = sqlite3_column_int(stmt, 13);
 | 
				
			||||||
	last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 13);
 | 
						last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 14);
 | 
				
			||||||
	if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
 | 
						if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
 | 
				
			||||||
		if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
 | 
							if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
 | 
				
			||||||
			LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
 | 
								LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
 | 
				
			||||||
@@ -440,10 +493,14 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						copy_sqlite3_text_to_buf(subscr->last_lu_rat, stmt, 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	db_remove_reset(stmt);
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret == 0)
 | 
				
			||||||
 | 
							db_subscr_get_rat_types(dbc, subscr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (ret) {
 | 
						switch (ret) {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		*err = NULL;
 | 
							*err = NULL;
 | 
				
			||||||
@@ -458,6 +515,31 @@ out:
 | 
				
			|||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Check if a subscriber exists in the HLR database.
 | 
				
			||||||
 | 
					 * \param[in, out] dbc  database context.
 | 
				
			||||||
 | 
					 * \param[in] imsi  ASCII string of IMSI digits.
 | 
				
			||||||
 | 
					 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi) {
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_IMSI];
 | 
				
			||||||
 | 
						const char *err;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, NULL, imsi))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						if (rc == SQLITE_ROW)
 | 
				
			||||||
 | 
							return 0; /* exists */
 | 
				
			||||||
 | 
						if (rc == SQLITE_DONE)
 | 
				
			||||||
 | 
							return -ENOENT; /* does not exist */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = sqlite3_errmsg(dbc->db);
 | 
				
			||||||
 | 
						LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists by IMSI='%s': %s\n", imsi, err);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Retrieve subscriber data from the HLR database.
 | 
					/*! Retrieve subscriber data from the HLR database.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] imsi  ASCII string of IMSI digits.
 | 
					 * \param[in] imsi  ASCII string of IMSI digits.
 | 
				
			||||||
@@ -482,6 +564,33 @@ int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Check if a subscriber exists in the HLR database.
 | 
				
			||||||
 | 
					 * \param[in, out] dbc  database context.
 | 
				
			||||||
 | 
					 * \param[in] msisdn  ASCII string of MSISDN digits.
 | 
				
			||||||
 | 
					 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_MSISDN];
 | 
				
			||||||
 | 
						const char *err;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, NULL, msisdn))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						if (rc == SQLITE_ROW)
 | 
				
			||||||
 | 
							return 0; /* exists */
 | 
				
			||||||
 | 
						if (rc == SQLITE_DONE)
 | 
				
			||||||
 | 
							return -ENOENT; /* does not exist */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = sqlite3_errmsg(dbc->db);
 | 
				
			||||||
 | 
						LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists "
 | 
				
			||||||
 | 
							"by MSISDN='%s': %s\n", msisdn, err);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Retrieve subscriber data from the HLR database.
 | 
					/*! Retrieve subscriber data from the HLR database.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 * \param[in] msisdn  ASCII string of MSISDN digits.
 | 
					 * \param[in] msisdn  ASCII string of MSISDN digits.
 | 
				
			||||||
@@ -530,6 +639,28 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Retrieve subscriber data from the HLR database.
 | 
				
			||||||
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
 | 
					 * \param[in] imei  ASCII string of identifier digits
 | 
				
			||||||
 | 
					 * \param[out] subscr  place retrieved data in this struct.
 | 
				
			||||||
 | 
					 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
 | 
				
			||||||
 | 
					 *          database error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMEI];
 | 
				
			||||||
 | 
						const char *err;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, NULL, imei))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_sel(dbc, stmt, subscr, &err);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
 | 
					/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
 | 
				
			||||||
 * subscriber without notifying GSUP clients.
 | 
					 * subscriber without notifying GSUP clients.
 | 
				
			||||||
 * \param[in,out] dbc  database context.
 | 
					 * \param[in,out] dbc  database context.
 | 
				
			||||||
@@ -595,11 +726,14 @@ out:
 | 
				
			|||||||
 *         -EIO on database errors.
 | 
					 *         -EIO on database errors.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const char *vlr_or_sgsn_number, bool is_ps)
 | 
							 const char *vlr_or_sgsn_number, bool is_ps,
 | 
				
			||||||
 | 
							 const enum osmo_rat_type rat_types[], size_t rat_types_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
	int rc, ret = 0;
 | 
						int rc, ret = 0;
 | 
				
			||||||
	struct timespec localtime;
 | 
						struct timespec localtime;
 | 
				
			||||||
 | 
						char rat_types_str[128] = "";
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
 | 
						stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
 | 
				
			||||||
			       : DB_STMT_UPD_VLR_BY_ID];
 | 
								       : DB_STMT_UPD_VLR_BY_ID];
 | 
				
			||||||
@@ -653,6 +787,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			|||||||
		goto out;
 | 
							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);
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR,
 | 
							LOGP(DAUC, LOGL_ERROR,
 | 
				
			||||||
@@ -782,3 +931,119 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,15 +25,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "logging.h"
 | 
					#include "logging.h"
 | 
				
			||||||
#include "gsup_server.h"
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					#include "gsup_router.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gsup_route {
 | 
					/*! Find a route for the given address.
 | 
				
			||||||
	struct llist_head list;
 | 
					 * \param[in] gs gsup server
 | 
				
			||||||
 | 
					 * \param[in] addr IPA name of the client (SGSN, MSC/VLR). Although this is passed like a blob, together with the
 | 
				
			||||||
	uint8_t *addr;
 | 
					 *                 length, it must be nul-terminated! This is for legacy reasons, see the discussion here:
 | 
				
			||||||
	struct osmo_gsup_conn *conn;
 | 
					 *                 https://gerrit.osmocom.org/#/c/osmo-hlr/+/13048/
 | 
				
			||||||
};
 | 
					 * \param[in] addrlen length of addr, *including the nul-byte* (strlen(addr) + 1).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
/* find a route for the given address */
 | 
					 | 
				
			||||||
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
					struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
				
			||||||
					const uint8_t *addr, size_t addrlen)
 | 
										const uint8_t *addr, size_t addrlen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -47,6 +47,22 @@ struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
				
			|||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Find a GSUP connection's route (to read the IPA address from the route).
 | 
				
			||||||
 | 
					 * \param[in] conn GSUP connection
 | 
				
			||||||
 | 
					 * \return GSUP route
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gsup_route *gr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(gr, &conn->server->routes, list) {
 | 
				
			||||||
 | 
							if (gr->conn == conn)
 | 
				
			||||||
 | 
								return gr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* add a new route for the given address to the given conn */
 | 
					/* add a new route for the given address to the given conn */
 | 
				
			||||||
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)
 | 
					int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -61,7 +77,7 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
 | 
				
			|||||||
	if (!gr)
 | 
						if (!gr)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGP(DMAIN, LOGL_INFO, "Adding GSUP route for %s\n", addr);
 | 
						LOGP(DMAIN, LOGL_INFO, "Adding GSUP route for %s via %s:%u\n", addr, conn->conn->addr, conn->conn->port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gr->addr = talloc_memdup(gr, addr, addrlen);
 | 
						gr->addr = talloc_memdup(gr, addr, addrlen);
 | 
				
			||||||
	gr->conn = conn;
 | 
						gr->conn = conn;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,18 @@
 | 
				
			|||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include "gsup_server.h"
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gsup_route {
 | 
				
			||||||
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t *addr;
 | 
				
			||||||
 | 
						struct osmo_gsup_conn *conn;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
					struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 | 
				
			||||||
					const uint8_t *addr, size_t addrlen);
 | 
										const uint8_t *addr, size_t addrlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* add a new route for the given address to the given conn */
 | 
					/* add a new route for the given address to the given conn */
 | 
				
			||||||
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
 | 
					int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Send a msgb to a given address using routing */
 | 
					/*! Send a msgb to a given address using routing.
 | 
				
			||||||
 | 
					 * \param[in] gs gsup server
 | 
				
			||||||
 | 
					 * \param[in] addr IPA name of the client (SGSN, MSC/VLR). Although this is passed like a blob, together with the
 | 
				
			||||||
 | 
					 *                 length, it must be nul-terminated! This is for legacy reasons, see the discussion here:
 | 
				
			||||||
 | 
					 *                 https://gerrit.osmocom.org/#/c/osmo-hlr/+/13048/
 | 
				
			||||||
 | 
					 * \param[in] addrlen length of addr, *including the nul-byte* (strlen(addr) + 1).
 | 
				
			||||||
 | 
					 * \param[in] msg message buffer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
					int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
				
			||||||
			const uint8_t *addr, size_t addrlen,
 | 
								const uint8_t *addr, size_t addrlen,
 | 
				
			||||||
			struct msgb *msg)
 | 
								struct msgb *msg)
 | 
				
			||||||
@@ -35,7 +42,7 @@ int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	conn = gsup_route_find(gs, addr, addrlen);
 | 
						conn = gsup_route_find(gs, addr, addrlen);
 | 
				
			||||||
	if (!conn) {
 | 
						if (!conn) {
 | 
				
			||||||
		DEBUGP(DLGSUP, "Cannot find route for addr %s\n", addr);
 | 
							DEBUGP(DLGSUP, "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen));
 | 
				
			||||||
		msgb_free(msg);
 | 
							msgb_free(msg);
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										317
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								src/hlr.c
									
									
									
									
									
								
							@@ -23,6 +23,7 @@
 | 
				
			|||||||
#include <getopt.h>
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/msgb.h>
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/stats.h>
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
#include <osmocom/core/application.h>
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
#include <osmocom/gsm/gsup.h>
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
@@ -32,6 +33,9 @@
 | 
				
			|||||||
#include <osmocom/vty/ports.h>
 | 
					#include <osmocom/vty/ports.h>
 | 
				
			||||||
#include <osmocom/ctrl/control_vty.h>
 | 
					#include <osmocom/ctrl/control_vty.h>
 | 
				
			||||||
#include <osmocom/gsm/apn.h>
 | 
					#include <osmocom/gsm/apn.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm48_ie.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "db.h"
 | 
					#include "db.h"
 | 
				
			||||||
#include "hlr.h"
 | 
					#include "hlr.h"
 | 
				
			||||||
@@ -45,6 +49,7 @@
 | 
				
			|||||||
#include "hlr_ussd.h"
 | 
					#include "hlr_ussd.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr *g_hlr;
 | 
					struct hlr *g_hlr;
 | 
				
			||||||
 | 
					static void *hlr_ctx = NULL;
 | 
				
			||||||
static int quit = 0;
 | 
					static int quit = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
					/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
				
			||||||
@@ -145,6 +150,78 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int generate_new_msisdn(char *msisdn, const char *imsi, unsigned int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, j, rc;
 | 
				
			||||||
 | 
						uint8_t rand_buf[GSM23003_MSISDN_MAX_DIGITS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(len <= sizeof(rand_buf));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Generate a random unique MSISDN (with retry) */
 | 
				
			||||||
 | 
						for (i = 0; i < 10; i++) {
 | 
				
			||||||
 | 
							/* Get a random number (with retry) */
 | 
				
			||||||
 | 
							for (j = 0; j < 10; j++) {
 | 
				
			||||||
 | 
								rc = osmo_get_rand_id(rand_buf, len);
 | 
				
			||||||
 | 
								if (!rc)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (rc) {
 | 
				
			||||||
 | 
								LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to generate new MSISDN, random number generator"
 | 
				
			||||||
 | 
											" failed (rc=%d)\n", imsi, rc);
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Shift 0x00 ... 0xff range to 30 ... 39 (ASCII numbers) */
 | 
				
			||||||
 | 
							for (j = 0; j < len; j++)
 | 
				
			||||||
 | 
								msisdn[j] = 48 + (rand_buf[j] % 10);
 | 
				
			||||||
 | 
							msisdn[j] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Ensure there is no subscriber with such MSISDN */
 | 
				
			||||||
 | 
							if (db_subscr_exists_by_msisdn(g_hlr->dbc, msisdn) == -ENOENT)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Failure */
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to generate a new MSISDN, consider increasing "
 | 
				
			||||||
 | 
									"the length for the automatically assigned MSISDNs "
 | 
				
			||||||
 | 
									"(see 'subscriber-create-on-demand' command)\n", imsi);
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int subscr_create_on_demand(const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char msisdn[GSM23003_MSISDN_MAX_DIGITS + 1];
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!g_hlr->subscr_create_on_demand)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						if (db_subscr_exists_by_imsi(g_hlr->dbc, imsi) == 0)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						if (rand_msisdn_len && generate_new_msisdn(msisdn, imsi, rand_msisdn_len) != 0)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_INFO, "IMSI='%s': Creating subscriber on demand\n", imsi);
 | 
				
			||||||
 | 
						rc = db_subscr_create(g_hlr->dbc, imsi, g_hlr->subscr_create_on_demand_flags);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "Failed to create subscriber on demand (rc=%d): IMSI='%s'\n", rc, imsi);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!rand_msisdn_len)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Update MSISDN of the new (just allocated) subscriber */
 | 
				
			||||||
 | 
						rc = db_subscr_update_msisdn_by_imsi(g_hlr->dbc, imsi, msisdn);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to assign MSISDN='%s' (rc=%d)\n", imsi, msisdn, rc);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_INFO, "IMSI='%s': Successfully assigned MSISDN='%s'\n", imsi, msisdn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
 * Send Auth Info handling
 | 
					 * Send Auth Info handling
 | 
				
			||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
@@ -156,16 +233,22 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct osmo_gsup_message gsup_out;
 | 
						struct osmo_gsup_message gsup_out;
 | 
				
			||||||
	struct msgb *msg_out;
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
						bool separation_bit = false;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						subscr_create_on_demand(gsup->imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* initialize return message structure */
 | 
						/* initialize return message structure */
 | 
				
			||||||
	memset(&gsup_out, 0, sizeof(gsup_out));
 | 
						memset(&gsup_out, 0, sizeof(gsup_out));
 | 
				
			||||||
	memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
 | 
						memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup->rat_types_len >= 1 && gsup->rat_types[0] == OSMO_RAT_EUTRAN_SGS)
 | 
				
			||||||
 | 
							separation_bit = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
 | 
						rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
 | 
				
			||||||
			gsup_out.auth_vectors,
 | 
								gsup_out.auth_vectors,
 | 
				
			||||||
			ARRAY_SIZE(gsup_out.auth_vectors),
 | 
								ARRAY_SIZE(gsup_out.auth_vectors),
 | 
				
			||||||
			gsup->rand, gsup->auts);
 | 
								gsup->rand, gsup->auts, separation_bit);
 | 
				
			||||||
	if (rc <= 0) {
 | 
						if (rc <= 0) {
 | 
				
			||||||
		gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
 | 
							gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
 | 
				
			||||||
		switch (rc) {
 | 
							switch (rc) {
 | 
				
			||||||
@@ -180,7 +263,7 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
			break;
 | 
								break;
 | 
				
			||||||
		case -ENOENT:
 | 
							case -ENOENT:
 | 
				
			||||||
			LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
 | 
								LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
 | 
				
			||||||
			gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
 | 
								gsup_out.cause = GMM_CAUSE_ROAMING_NOTALLOWED;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
 | 
								LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
 | 
				
			||||||
@@ -263,6 +346,9 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber *subscr;
 | 
						struct hlr_subscriber *subscr;
 | 
				
			||||||
	struct lu_operation *luop = lu_op_alloc_conn(conn);
 | 
						struct lu_operation *luop = lu_op_alloc_conn(conn);
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						bool allowed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!luop) {
 | 
						if (!luop) {
 | 
				
			||||||
		LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
 | 
							LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
@@ -288,13 +374,15 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	llist_add(&luop->list, &g_lu_ops);
 | 
						llist_add(&luop->list, &g_lu_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						subscr_create_on_demand(gsup->imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
 | 
						/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* check if subscriber is known at all */
 | 
						/* check if subscriber is known at all */
 | 
				
			||||||
	if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
 | 
						if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
 | 
				
			||||||
		/* Send Error back: Subscriber Unknown in HLR */
 | 
							/* Send Error back: Subscriber Unknown in HLR */
 | 
				
			||||||
		osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
 | 
							osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
 | 
				
			||||||
		lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
 | 
							lu_op_tx_error(luop, GMM_CAUSE_ROAMING_NOTALLOWED);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -308,6 +396,34 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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. */
 | 
				
			||||||
 | 
						allowed = false;
 | 
				
			||||||
 | 
						LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' on %s sent RAT types: %zu\n", subscr->imsi,
 | 
				
			||||||
 | 
						     gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS ? "CS" : "PS", gsup->rat_types_len);
 | 
				
			||||||
 | 
						for (i = 0; i < gsup->rat_types_len; i++) {
 | 
				
			||||||
 | 
							enum osmo_rat_type rat = gsup->rat_types[i];
 | 
				
			||||||
 | 
							if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
 | 
				
			||||||
 | 
								lu_op_tx_error(luop, GMM_CAUSE_COND_IE_ERR);
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (luop->subscr.rat_types[rat]) {
 | 
				
			||||||
 | 
								allowed = true;
 | 
				
			||||||
 | 
								LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' allowed on %s\n",
 | 
				
			||||||
 | 
								     subscr->imsi, osmo_rat_type_name(rat));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' not allowed on %s\n",
 | 
				
			||||||
 | 
								     subscr->imsi, osmo_rat_type_name(rat));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!allowed && gsup->rat_types_len > 0) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_DEBUG, "ISMI='%s' not allowed on %s%s\n",
 | 
				
			||||||
 | 
							     subscr->imsi, osmo_rat_type_name(gsup->rat_types[0]),
 | 
				
			||||||
 | 
							     gsup->rat_types_len > 1 ? " (nor on the other available RAT types)" : "");
 | 
				
			||||||
 | 
							lu_op_tx_error(luop, GMM_CAUSE_NO_SUIT_CELL_IN_LA);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
						/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
@@ -325,17 +441,17 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
	LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
 | 
						LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
 | 
				
			||||||
	     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
 | 
						     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
 | 
				
			||||||
	     osmo_quote_str((const char*)luop->peer, -1));
 | 
						     osmo_quote_str((const char*)luop->peer, -1));
 | 
				
			||||||
	if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))
 | 
						if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps,
 | 
				
			||||||
 | 
								 gsup->rat_types, gsup->rat_types_len))
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
 | 
							LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
 | 
				
			||||||
		     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
 | 
							     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{
 | 
						/* TODO: Subscriber allowed to roam in PLMN? */
 | 
				
			||||||
		/* TODO: Subscriber allowed to roam in PLMN? */
 | 
						/* TODO: Update RoutingInfo */
 | 
				
			||||||
		/* TODO: Update RoutingInfo */
 | 
						/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
				
			||||||
		/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
						/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
				
			||||||
		/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
						lu_op_tx_insert_subscr_data(luop);
 | 
				
			||||||
		lu_op_tx_insert_subscr_data(luop);
 | 
					
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -379,16 +495,10 @@ static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
 | 
					static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
 | 
				
			||||||
				enum osmo_gsup_message_type type_in, uint8_t err_cause)
 | 
									enum osmo_gsup_message_type type_in, uint8_t err_cause)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int type_err = osmo_gsup_get_err_msg_type(type_in);
 | 
						int type_err = OSMO_GSUP_TO_MSGT_ERROR(type_in);
 | 
				
			||||||
	struct osmo_gsup_message gsup_reply = {0};
 | 
						struct osmo_gsup_message gsup_reply = {0};
 | 
				
			||||||
	struct msgb *msg_out;
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (type_err < 0) {
 | 
					 | 
				
			||||||
		LOGP(DMAIN, LOGL_ERROR, "unable to determine error response for %s\n",
 | 
					 | 
				
			||||||
			osmo_gsup_message_type_name(type_in));
 | 
					 | 
				
			||||||
		return type_err;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi);
 | 
						OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi);
 | 
				
			||||||
	gsup_reply.message_type = type_err;
 | 
						gsup_reply.message_type = type_err;
 | 
				
			||||||
	gsup_reply.cause = err_cause;
 | 
						gsup_reply.cause = err_cause;
 | 
				
			||||||
@@ -399,21 +509,166 @@ static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
 | 
				
			|||||||
	return osmo_gsup_conn_send(conn, msg_out);
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup_reply = {0};
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
						char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Require IMEI */
 | 
				
			||||||
 | 
						if (!gsup->imei_enc) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: missing IMEI\n", gsup->imsi);
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Decode IMEI (fails if IMEI is too long) */
 | 
				
			||||||
 | 
						rc = gsm48_decode_bcd_number2(imei, sizeof(imei), gsup->imei_enc, gsup->imei_enc_len, 0);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: failed to decode IMEI (rc: %i)\n", gsup->imsi, rc);
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check if IMEI is too short */
 | 
				
			||||||
 | 
						if (strlen(imei) != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "%s: wrong encoded IMEI length (IMEI: '%s', %lu, %i)\n", gsup->imsi, imei,
 | 
				
			||||||
 | 
							     strlen(imei), GSM23003_IMEI_NUM_DIGITS_NO_CHK);
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						subscr_create_on_demand(gsup->imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Save in DB if desired */
 | 
				
			||||||
 | 
						if (g_hlr->store_imei) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);
 | 
				
			||||||
 | 
							if (db_subscr_update_imei_by_imsi(g_hlr->dbc, gsup->imsi, imei) < 0) {
 | 
				
			||||||
 | 
								gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* Check if subscriber exists and print IMEI */
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_INFO, "IMSI='%s': has IMEI = %s (consider setting 'store-imei')\n", gsup->imsi, imei);
 | 
				
			||||||
 | 
							struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
							if (db_subscr_get_by_imsi(g_hlr->dbc, gsup->imsi, &subscr) < 0) {
 | 
				
			||||||
 | 
								gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Accept all IMEIs */
 | 
				
			||||||
 | 
						gsup_reply.imei_result = OSMO_GSUP_IMEI_RESULT_ACK;
 | 
				
			||||||
 | 
						gsup_reply.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT;
 | 
				
			||||||
 | 
						msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP Check_IMEI response");
 | 
				
			||||||
 | 
						memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
 | 
				
			||||||
 | 
						osmo_gsup_encode(msg_out, &gsup_reply);
 | 
				
			||||||
 | 
						return osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char namebuf[255];
 | 
				
			||||||
 | 
					#define LOGP_GSUP_FWD(gsup, level, fmt, args ...) \
 | 
				
			||||||
 | 
						LOGP(DMAIN, level, "Forward %s (class=%s, IMSI=%s, %s->%s): " fmt, \
 | 
				
			||||||
 | 
						     osmo_gsup_message_type_name(gsup->message_type), \
 | 
				
			||||||
 | 
						     osmo_gsup_message_class_name(gsup->message_class), \
 | 
				
			||||||
 | 
						     gsup->imsi, \
 | 
				
			||||||
 | 
						     osmo_quote_str((const char *)gsup->source_name, gsup->source_name_len), \
 | 
				
			||||||
 | 
						     osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)gsup->destination_name, gsup->destination_name_len), \
 | 
				
			||||||
 | 
						     ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int read_cb_forward(struct osmo_gsup_conn *conn, struct msgb *msg, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = -EINVAL;
 | 
				
			||||||
 | 
						struct osmo_gsup_message *gsup_err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* FIXME: it would be better if the msgb never were deallocated immediately by osmo_gsup_addr_send(), which a
 | 
				
			||||||
 | 
						 * select-loop volatile talloc context could facilitate. Then we would still be able to access gsup-> members
 | 
				
			||||||
 | 
						 * (pointing into the msgb) even after sending failed, and we wouldn't need to copy this data before sending: */
 | 
				
			||||||
 | 
						/* Prepare error message (before IEs get deallocated) */
 | 
				
			||||||
 | 
						gsup_err = talloc_zero(hlr_ctx, struct osmo_gsup_message);
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(gsup_err->imsi, gsup->imsi);
 | 
				
			||||||
 | 
						gsup_err->message_class = gsup->message_class;
 | 
				
			||||||
 | 
						gsup_err->destination_name = talloc_memdup(gsup_err, gsup->destination_name, gsup->destination_name_len);
 | 
				
			||||||
 | 
						gsup_err->destination_name_len = gsup->destination_name_len;
 | 
				
			||||||
 | 
						gsup_err->message_type = gsup->message_type;
 | 
				
			||||||
 | 
						gsup_err->session_state = gsup->session_state;
 | 
				
			||||||
 | 
						gsup_err->session_id = gsup->session_id;
 | 
				
			||||||
 | 
						gsup_err->source_name = talloc_memdup(gsup_err, gsup->source_name, gsup->source_name_len);
 | 
				
			||||||
 | 
						gsup_err->source_name_len = gsup->source_name_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check for routing IEs */
 | 
				
			||||||
 | 
						if (!gsup->source_name || !gsup->source_name_len || !gsup->destination_name || !gsup->destination_name_len) {
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup, LOGL_ERROR, "missing routing IEs\n");
 | 
				
			||||||
 | 
							goto end;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Verify source name (e.g. "MSC-00-00-00-00-00-00") */
 | 
				
			||||||
 | 
						if (gsup_route_find(conn->server, gsup->source_name, gsup->source_name_len) != conn) {
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup, LOGL_ERROR, "mismatching source name\n");
 | 
				
			||||||
 | 
							goto end;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Forward message without re-encoding (so we don't remove unknown IEs) */
 | 
				
			||||||
 | 
						LOGP_GSUP_FWD(gsup, LOGL_INFO, "checks passed, forwarding\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remove incoming IPA header to be able to prepend an outgoing IPA header */
 | 
				
			||||||
 | 
						msgb_pull_to_l2(msg);
 | 
				
			||||||
 | 
						ret = osmo_gsup_addr_send(g_hlr->gs, gsup->destination_name, gsup->destination_name_len, msg);
 | 
				
			||||||
 | 
						/* AT THIS POINT, THE msg MAY BE DEALLOCATED and the data like gsup->imsi, gsup->source_name etc may all be
 | 
				
			||||||
 | 
						 * invalid and cause segfaults. */
 | 
				
			||||||
 | 
						msg = NULL;
 | 
				
			||||||
 | 
						gsup = NULL;
 | 
				
			||||||
 | 
						if (ret == -ENODEV)
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "destination not connected\n");
 | 
				
			||||||
 | 
						else if (ret)
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "unknown error %i\n", ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end:
 | 
				
			||||||
 | 
						/* Send error back to source */
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							struct msgb *msg_err = msgb_alloc_headroom(1024+16, 16, "GSUP forward ERR response");
 | 
				
			||||||
 | 
							OSMO_ASSERT(msg_err);
 | 
				
			||||||
 | 
							gsup_err->message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR;
 | 
				
			||||||
 | 
							osmo_gsup_encode(msg_err, gsup_err);
 | 
				
			||||||
 | 
							LOGP_GSUP_FWD(gsup_err, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(gsup_err->message_type));
 | 
				
			||||||
 | 
							osmo_gsup_conn_send(conn, msg_err);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						talloc_free(gsup_err);
 | 
				
			||||||
 | 
						if (msg)
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
					static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static struct osmo_gsup_message gsup;
 | 
						static struct osmo_gsup_message gsup;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!msgb_l2(msg) || !msgb_l2len(msg)) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "missing or empty L2 data\n");
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
 | 
						rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
 | 
							LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
		return rc;
 | 
							return rc;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5
 | 
						/* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5
 | 
				
			||||||
	 * digits is impossible.  Even 5 digits is a highly theoretical case */
 | 
						 * digits is impossible.  Even 5 digits is a highly theoretical case */
 | 
				
			||||||
	if (strlen(gsup.imsi) < 5)
 | 
						if (strlen(gsup.imsi) < 5) { /* TODO: move this check to libosmogsm/gsup.c? */
 | 
				
			||||||
		return gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
							LOGP(DMAIN, LOGL_ERROR, "IMSI too short: %s\n", osmo_quote_str(gsup.imsi, -1));
 | 
				
			||||||
 | 
							gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup.destination_name_len)
 | 
				
			||||||
 | 
							return read_cb_forward(conn, msg, &gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (gsup.message_type) {
 | 
						switch (gsup.message_type) {
 | 
				
			||||||
	/* requests sent to us */
 | 
						/* requests sent to us */
 | 
				
			||||||
@@ -459,6 +714,9 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
				
			|||||||
			lu_op_rx_gsup(luop, &gsup);
 | 
								lu_op_rx_gsup(luop, &gsup);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
 | 
				
			||||||
 | 
							rx_check_imei_req(conn, &gsup);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
 | 
							LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
 | 
				
			||||||
		     osmo_gsup_message_type_name(gsup.message_type));
 | 
							     osmo_gsup_message_type_name(gsup.message_type));
 | 
				
			||||||
@@ -494,7 +752,7 @@ static struct {
 | 
				
			|||||||
	bool db_upgrade;
 | 
						bool db_upgrade;
 | 
				
			||||||
} cmdline_opts = {
 | 
					} cmdline_opts = {
 | 
				
			||||||
	.config_file = "osmo-hlr.cfg",
 | 
						.config_file = "osmo-hlr.cfg",
 | 
				
			||||||
	.db_file = "hlr.db",
 | 
						.db_file = NULL,
 | 
				
			||||||
	.daemonize = false,
 | 
						.daemonize = false,
 | 
				
			||||||
	.db_upgrade = false,
 | 
						.db_upgrade = false,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -564,13 +822,12 @@ static void handle_options(int argc, char **argv)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void *hlr_ctx = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void signal_hdlr(int signal)
 | 
					static void signal_hdlr(int signal)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (signal) {
 | 
						switch (signal) {
 | 
				
			||||||
 | 
						case SIGTERM:
 | 
				
			||||||
	case SIGINT:
 | 
						case SIGINT:
 | 
				
			||||||
		LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
 | 
							LOGP(DMAIN, LOGL_NOTICE, "Terminating due to signal=%d\n", signal);
 | 
				
			||||||
		quit++;
 | 
							quit++;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case SIGUSR1:
 | 
						case SIGUSR1:
 | 
				
			||||||
@@ -610,6 +867,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	INIT_LLIST_HEAD(&g_hlr->iuse_list);
 | 
						INIT_LLIST_HEAD(&g_hlr->iuse_list);
 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
						INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
				
			||||||
	INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
						INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
				
			||||||
 | 
						g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Init default (call independent) SS session guard timeout value */
 | 
						/* Init default (call independent) SS session guard timeout value */
 | 
				
			||||||
	g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
 | 
						g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
 | 
				
			||||||
@@ -620,10 +878,11 @@ int main(int argc, char **argv)
 | 
				
			|||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_stats_init(hlr_ctx);
 | 
				
			||||||
	vty_init(&vty_info);
 | 
						vty_init(&vty_info);
 | 
				
			||||||
	ctrl_vty_init(hlr_ctx);
 | 
						ctrl_vty_init(hlr_ctx);
 | 
				
			||||||
	handle_options(argc, argv);
 | 
						handle_options(argc, argv);
 | 
				
			||||||
	hlr_vty_init(&hlr_log_info);
 | 
						hlr_vty_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = vty_read_config_file(cmdline_opts.config_file, NULL);
 | 
						rc = vty_read_config_file(cmdline_opts.config_file, NULL);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
@@ -647,9 +906,12 @@ int main(int argc, char **argv)
 | 
				
			|||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
 | 
						if (cmdline_opts.db_file)
 | 
				
			||||||
 | 
							osmo_talloc_replace_string(g_hlr, &g_hlr->db_file_path, cmdline_opts.db_file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_hlr->dbc = db_open(hlr_ctx, g_hlr->db_file_path, true, cmdline_opts.db_upgrade);
 | 
				
			||||||
	if (!g_hlr->dbc) {
 | 
						if (!g_hlr->dbc) {
 | 
				
			||||||
		LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
 | 
							LOGP(DMAIN, LOGL_FATAL, "Error opening database %s\n", osmo_quote_str(g_hlr->db_file_path, -1));
 | 
				
			||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -665,6 +927,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	osmo_init_ignore_signals();
 | 
						osmo_init_ignore_signals();
 | 
				
			||||||
	signal(SIGINT, &signal_hdlr);
 | 
						signal(SIGINT, &signal_hdlr);
 | 
				
			||||||
 | 
						signal(SIGTERM, &signal_hdlr);
 | 
				
			||||||
	signal(SIGUSR1, &signal_hdlr);
 | 
						signal(SIGUSR1, &signal_hdlr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cmdline_opts.daemonize) {
 | 
						if (cmdline_opts.daemonize) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/hlr.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/hlr.h
									
									
									
									
									
								
							@@ -25,6 +25,8 @@
 | 
				
			|||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr_euse;
 | 
					struct hlr_euse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr {
 | 
					struct hlr {
 | 
				
			||||||
@@ -32,6 +34,7 @@ struct hlr {
 | 
				
			|||||||
	struct osmo_gsup_server *gs;
 | 
						struct osmo_gsup_server *gs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* DB context */
 | 
						/* DB context */
 | 
				
			||||||
 | 
						char *db_file_path;
 | 
				
			||||||
	struct db_context *dbc;
 | 
						struct db_context *dbc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Control Interface */
 | 
						/* Control Interface */
 | 
				
			||||||
@@ -51,6 +54,13 @@ struct hlr {
 | 
				
			|||||||
	struct llist_head ussd_routes;
 | 
						struct llist_head ussd_routes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct llist_head ss_sessions;
 | 
						struct llist_head ss_sessions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool store_imei;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool subscr_create_on_demand;
 | 
				
			||||||
 | 
						/* Bitmask of DB_SUBSCR_FLAG_* */
 | 
				
			||||||
 | 
						uint8_t subscr_create_on_demand_flags;
 | 
				
			||||||
 | 
						unsigned int subscr_create_on_demand_rand_msisdn_len;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct hlr *g_hlr;
 | 
					extern struct hlr *g_hlr;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -302,7 +302,7 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
 | 
						snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_subscr_create(dbc, imsi_str);
 | 
						rc = db_subscr_create(dbc, imsi_str, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
 | 
							LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
 | 
				
			||||||
		     dbc->fname,
 | 
							     dbc->fname,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										251
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							@@ -34,6 +34,7 @@
 | 
				
			|||||||
#include "gsup_server.h"
 | 
					#include "gsup_server.h"
 | 
				
			||||||
#include "gsup_router.h"
 | 
					#include "gsup_router.h"
 | 
				
			||||||
#include "logging.h"
 | 
					#include "logging.h"
 | 
				
			||||||
 | 
					#include "db.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
 * core data structures expressing config from VTY
 | 
					 * core data structures expressing config from VTY
 | 
				
			||||||
@@ -149,7 +150,7 @@ struct ss_session {
 | 
				
			|||||||
	/* link us to hlr->ss_sessions */
 | 
						/* link us to hlr->ss_sessions */
 | 
				
			||||||
	struct llist_head list;
 | 
						struct llist_head list;
 | 
				
			||||||
	/* imsi of this session */
 | 
						/* imsi of this session */
 | 
				
			||||||
	char imsi[GSM23003_IMSI_MAX_DIGITS+2];
 | 
						char imsi[OSMO_IMSI_BUF_SIZE];
 | 
				
			||||||
	/* ID of this session (unique per IMSI) */
 | 
						/* ID of this session (unique per IMSI) */
 | 
				
			||||||
	uint32_t session_id;
 | 
						uint32_t session_id;
 | 
				
			||||||
	/* state of the session */
 | 
						/* state of the session */
 | 
				
			||||||
@@ -166,6 +167,12 @@ struct ss_session {
 | 
				
			|||||||
		const struct hlr_iuse *iuse;
 | 
							const struct hlr_iuse *iuse;
 | 
				
			||||||
	} u;
 | 
						} u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* subscriber's vlr_number
 | 
				
			||||||
 | 
						 * MO USSD: originating MSC's vlr_number
 | 
				
			||||||
 | 
						 * MT USSD: looked up once per session and cached here */
 | 
				
			||||||
 | 
						uint8_t *vlr_number;
 | 
				
			||||||
 | 
						size_t vlr_number_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
 | 
						/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
 | 
				
			||||||
	 * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
 | 
						 * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
 | 
				
			||||||
	 * every time we receive an USSD component from the EUSE */
 | 
						 * every time we receive an USSD component from the EUSE */
 | 
				
			||||||
@@ -222,6 +229,35 @@ struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t
 | 
				
			|||||||
 * handling functions for encoding SS messages + wrapping them in GSUP
 | 
					 * handling functions for encoding SS messages + wrapping them in GSUP
 | 
				
			||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Resolve the target MSC by ss->imsi and send GSUP message. */
 | 
				
			||||||
 | 
					static int ss_gsup_send(struct ss_session *ss, struct osmo_gsup_server *gs, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr = {};
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Use vlr_number as looked up by the caller, or look up now. */
 | 
				
			||||||
 | 
						if (!ss->vlr_number) {
 | 
				
			||||||
 | 
							rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
							if (rc < 0) {
 | 
				
			||||||
 | 
								LOGPSS(ss, LOGL_ERROR, "Cannot find subscriber, cannot route GSUP message\n");
 | 
				
			||||||
 | 
								msgb_free(msg);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ss->vlr_number = (uint8_t *)talloc_strdup(ss, subscr.vlr_number);
 | 
				
			||||||
 | 
							ss->vlr_number_len = strlen(subscr.vlr_number) + 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */
 | 
				
			||||||
 | 
						if (ss->vlr_number_len == 1) {
 | 
				
			||||||
 | 
							LOGPSS(ss, LOGL_ERROR, "Cannot send GSUP message, no VLR number stored for subscriber\n");
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_quote_str((char *)ss->vlr_number, ss->vlr_number_len));
 | 
				
			||||||
 | 
						return osmo_gsup_addr_send(gs, ss->vlr_number, ss->vlr_number_len, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
 | 
					static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
 | 
				
			||||||
			bool final, struct msgb *ss_msg)
 | 
								bool final, struct msgb *ss_msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -241,13 +277,12 @@ static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_m
 | 
				
			|||||||
		resp.ss_info_len = msgb_length(ss_msg);
 | 
							resp.ss_info_len = msgb_length(ss_msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp_msg = gsm0480_msgb_alloc_name(__func__);
 | 
						resp_msg = msgb_alloc_headroom(4000, 64, __func__);
 | 
				
			||||||
	OSMO_ASSERT(resp_msg);
 | 
						OSMO_ASSERT(resp_msg);
 | 
				
			||||||
	osmo_gsup_encode(resp_msg, &resp);
 | 
						osmo_gsup_encode(resp_msg, &resp);
 | 
				
			||||||
	msgb_free(ss_msg);
 | 
						msgb_free(ss_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: resolve this based on the database vlr_addr */
 | 
						return ss_gsup_send(ss, g_hlr->gs, resp_msg);
 | 
				
			||||||
	return osmo_gsup_addr_send(g_hlr->gs, (uint8_t *)"MSC-00-00-00-00-00-00", 22, resp_msg);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
@@ -301,11 +336,11 @@ static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session
 | 
				
			|||||||
		ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
							ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case -ENOENT:
 | 
						case -ENOENT:
 | 
				
			||||||
		ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case -EIO:
 | 
						case -EIO:
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@@ -320,6 +355,150 @@ static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_get_ran(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								       const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								       const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
								rat = "nothing, you don't exist";
 | 
				
			||||||
 | 
							else if (!strcmp(subscr.last_lu_rat, "GERAN-A"))
 | 
				
			||||||
 | 
								rat = "2G";
 | 
				
			||||||
 | 
							else if (!strcmp(subscr.last_lu_rat, "UTRAN-Iu"))
 | 
				
			||||||
 | 
								rat = "3G";
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								rat = subscr.last_lu_rat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							snprintf(response, sizeof(response),
 | 
				
			||||||
 | 
								 "Now on %s. Available:%s%s.",
 | 
				
			||||||
 | 
								 rat,
 | 
				
			||||||
 | 
								 subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "",
 | 
				
			||||||
 | 
								 subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, response);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_gsm_on(struct osmo_gsup_conn *conn, 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_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Enabled GERAN-A (2G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_gsm_off(struct osmo_gsup_conn *conn, 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_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Disabled GERAN-A (2G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_umts_on(struct osmo_gsup_conn *conn, 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_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Enabled UTRAN-Iu (3G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_umts_off(struct osmo_gsup_conn *conn, 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_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Disabled UTRAN-Iu (3G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct hlr_iuse hlr_iuses[] = {
 | 
					static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -330,6 +509,26 @@ static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			|||||||
		.name = "own-imsi",
 | 
							.name = "own-imsi",
 | 
				
			||||||
		.handle_ussd = handle_ussd_own_imsi,
 | 
							.handle_ussd = handle_ussd_own_imsi,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "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,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct hlr_iuse *iuse_find(const char *name)
 | 
					const struct hlr_iuse *iuse_find(const char *name)
 | 
				
			||||||
@@ -433,8 +632,7 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			|||||||
		OSMO_ASSERT(msg_out);
 | 
							OSMO_ASSERT(msg_out);
 | 
				
			||||||
		/* Received from EUSE, Forward to VLR */
 | 
							/* Received from EUSE, Forward to VLR */
 | 
				
			||||||
		osmo_gsup_encode(msg_out, gsup);
 | 
							osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
		/* FIXME: resolve this based on the database vlr_addr */
 | 
							ss_gsup_send(ss, conn->server, msg_out);
 | 
				
			||||||
		osmo_gsup_addr_send(conn->server, (uint8_t *)"MSC-00-00-00-00-00-00", 22, msg_out);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* Received from VLR (MS) */
 | 
							/* Received from VLR (MS) */
 | 
				
			||||||
		if (ss->is_external) {
 | 
							if (ss->is_external) {
 | 
				
			||||||
@@ -471,6 +669,7 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
 | 
				
			|||||||
	struct hlr *hlr = conn->server->priv;
 | 
						struct hlr *hlr = conn->server->priv;
 | 
				
			||||||
	struct ss_session *ss;
 | 
						struct ss_session *ss;
 | 
				
			||||||
	struct ss_request req = {0};
 | 
						struct ss_request req = {0};
 | 
				
			||||||
 | 
						struct gsup_route *gsup_rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
						LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
				
			||||||
		osmo_gsup_session_state_name(gsup->session_state));
 | 
							osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
@@ -484,6 +683,11 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
 | 
				
			|||||||
			/* FIXME: Send a Reject component? */
 | 
								/* FIXME: Send a Reject component? */
 | 
				
			||||||
			goto out_err;
 | 
								goto out_err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if (gsup->session_state != OSMO_GSUP_SESSION_STATE_END) {
 | 
				
			||||||
 | 
							LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Missing SS payload for '%s'\n",
 | 
				
			||||||
 | 
							     gsup->imsi, gsup->session_id,
 | 
				
			||||||
 | 
							     osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
 | 
							goto out_err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (gsup->session_state) {
 | 
						switch (gsup->session_state) {
 | 
				
			||||||
@@ -500,6 +704,20 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
 | 
				
			|||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			goto out_err;
 | 
								goto out_err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							/* Get IPA name from VLR conn and save as ss->vlr_number */
 | 
				
			||||||
 | 
							if (!conn_is_euse(conn)) {
 | 
				
			||||||
 | 
								gsup_rt = gsup_route_find_by_conn(conn);
 | 
				
			||||||
 | 
								if (gsup_rt) {
 | 
				
			||||||
 | 
									ss->vlr_number = (uint8_t *)talloc_strdup(ss, (const char *)gsup_rt->addr);
 | 
				
			||||||
 | 
									ss->vlr_number_len = strlen((const char *)gsup_rt->addr) + 1;
 | 
				
			||||||
 | 
									LOGPSS(ss, LOGL_DEBUG, "Destination IPA name retrieved from GSUP route: %s\n",
 | 
				
			||||||
 | 
									       osmo_quote_str((const char *)ss->vlr_number, ss->vlr_number_len));
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									LOGPSS(ss, LOGL_NOTICE, "Could not find GSUP route, therefore can't set the destination"
 | 
				
			||||||
 | 
												" IPA name. We'll try to look it up later, but this should not"
 | 
				
			||||||
 | 
												" have happened.\n");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (ss_op_is_ussd(req.opcode)) {
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
			if (conn_is_euse(conn)) {
 | 
								if (conn_is_euse(conn)) {
 | 
				
			||||||
				/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
 | 
									/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
 | 
				
			||||||
@@ -557,13 +775,18 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
 | 
				
			|||||||
				gsup->imsi, gsup->session_id);
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
			goto out_err;
 | 
								goto out_err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (ss_op_is_ussd(req.opcode)) {
 | 
					
 | 
				
			||||||
			/* dispatch unstructured SS to routing */
 | 
							/* SS payload is optional for END */
 | 
				
			||||||
			handle_ussd(conn, ss, gsup, &req);
 | 
							if (gsup->ss_info && gsup->ss_info_len) {
 | 
				
			||||||
		} else {
 | 
								if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
			/* dispatch non-call SS to internal code */
 | 
									/* dispatch unstructured SS to routing */
 | 
				
			||||||
			handle_ss(ss, gsup, &req);
 | 
									handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									/* dispatch non-call SS to internal code */
 | 
				
			||||||
 | 
									handle_ss(ss, gsup, &req);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ss_session_free(ss);
 | 
							ss_session_free(ss);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										103
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							@@ -27,11 +27,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/talloc.h>
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
#include <osmocom/vty/vty.h>
 | 
					#include <osmocom/vty/vty.h>
 | 
				
			||||||
 | 
					#include <osmocom/vty/stats.h>
 | 
				
			||||||
#include <osmocom/vty/command.h>
 | 
					#include <osmocom/vty/command.h>
 | 
				
			||||||
#include <osmocom/vty/logging.h>
 | 
					#include <osmocom/vty/logging.h>
 | 
				
			||||||
#include <osmocom/vty/misc.h>
 | 
					#include <osmocom/vty/misc.h>
 | 
				
			||||||
#include <osmocom/abis/ipa.h>
 | 
					#include <osmocom/abis/ipa.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "db.h"
 | 
				
			||||||
#include "hlr.h"
 | 
					#include "hlr.h"
 | 
				
			||||||
#include "hlr_vty.h"
 | 
					#include "hlr_vty.h"
 | 
				
			||||||
#include "hlr_vty_subscr.h"
 | 
					#include "hlr_vty_subscr.h"
 | 
				
			||||||
@@ -71,6 +73,27 @@ DEFUN(cfg_gsup,
 | 
				
			|||||||
static int config_write_hlr(struct vty *vty)
 | 
					static int config_write_hlr(struct vty *vty)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	vty_out(vty, "hlr%s", VTY_NEWLINE);
 | 
						vty_out(vty, "hlr%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (g_hlr->store_imei)
 | 
				
			||||||
 | 
							vty_out(vty, " store-imei%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH))
 | 
				
			||||||
 | 
							vty_out(vty, " database %s%s", g_hlr->db_file_path, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (g_hlr->subscr_create_on_demand) {
 | 
				
			||||||
 | 
							const char *flags_str = "none";
 | 
				
			||||||
 | 
							uint8_t flags = g_hlr->subscr_create_on_demand_flags;
 | 
				
			||||||
 | 
							unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((flags & DB_SUBSCR_FLAG_NAM_CS) && (flags & DB_SUBSCR_FLAG_NAM_PS))
 | 
				
			||||||
 | 
								flags_str = "cs+ps";
 | 
				
			||||||
 | 
							else if (flags & DB_SUBSCR_FLAG_NAM_CS)
 | 
				
			||||||
 | 
								flags_str = "cs";
 | 
				
			||||||
 | 
							else if (flags & DB_SUBSCR_FLAG_NAM_PS)
 | 
				
			||||||
 | 
								flags_str = "ps";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (rand_msisdn_len)
 | 
				
			||||||
 | 
								vty_out(vty, " subscriber-create-on-demand %i %s%s", rand_msisdn_len, flags_str, VTY_NEWLINE);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								vty_out(vty, " subscriber-create-on-demand no-msisdn %s%s", flags_str, VTY_NEWLINE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,10 +156,13 @@ DEFUN(cfg_hlr_gsup_bind_ip,
 | 
				
			|||||||
#define UROUTE_STR "Routing Configuration\n"
 | 
					#define UROUTE_STR "Routing Configuration\n"
 | 
				
			||||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
					#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define INT_CHOICE "(own-msisdn|own-imsi)"
 | 
					#define INT_CHOICE "(own-msisdn|own-imsi|get-ran|gsm-on|gsm-off|umts-on|umts-off)"
 | 
				
			||||||
#define INT_STR "Internal USSD Handler\n" \
 | 
					#define INT_STR "Internal USSD Handler\n" \
 | 
				
			||||||
		"Respond with subscribers' own MSISDN\n" \
 | 
							"Respond with subscribers' own MSISDN\n" \
 | 
				
			||||||
		"Respond with subscribers' own IMSI\n"
 | 
							"Respond with subscribers' own IMSI\n" \
 | 
				
			||||||
 | 
							"Respond with available RAN types\n" \
 | 
				
			||||||
 | 
							"Enable UMTS service\n" \
 | 
				
			||||||
 | 
							"Disable UMTS service\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define EXT_STR "External USSD Handler\n" \
 | 
					#define EXT_STR "External USSD Handler\n" \
 | 
				
			||||||
		"Name of External USSD Handler (IPA CCM ID)\n"
 | 
							"Name of External USSD Handler (IPA CCM ID)\n"
 | 
				
			||||||
@@ -221,6 +247,15 @@ DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_database, cfg_database_cmd,
 | 
				
			||||||
 | 
						"database PATH",
 | 
				
			||||||
 | 
						"Set the path to the HLR database file\n"
 | 
				
			||||||
 | 
						"Relative or absolute file system path to the database file (default is '" HLR_DEFAULT_DB_FILE_PATH "')\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						osmo_talloc_replace_string(g_hlr, &g_hlr->db_file_path, argv[0]);
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cmd_node euse_node = {
 | 
					struct cmd_node euse_node = {
 | 
				
			||||||
	EUSE_NODE,
 | 
						EUSE_NODE,
 | 
				
			||||||
	"%s(config-hlr-euse)# ",
 | 
						"%s(config-hlr-euse)# ",
 | 
				
			||||||
@@ -305,6 +340,59 @@ DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_store_imei, cfg_store_imei_cmd,
 | 
				
			||||||
 | 
						"store-imei",
 | 
				
			||||||
 | 
						"Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
 | 
				
			||||||
 | 
						" Check IMEI requests (for OsmoMSC, you may want to set 'check-imei-rqd 1').")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						g_hlr->store_imei = true;
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_no_store_imei, cfg_no_store_imei_cmd,
 | 
				
			||||||
 | 
						"no store-imei",
 | 
				
			||||||
 | 
						"Do not save the IMEI in the database, when receiving Check IMEI requests.")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						g_hlr->store_imei = false;
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_subscr_create_on_demand, cfg_subscr_create_on_demand_cmd,
 | 
				
			||||||
 | 
						"subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)",
 | 
				
			||||||
 | 
						"Make a new record when a subscriber is first seen.\n"
 | 
				
			||||||
 | 
						"Do not automatically assign MSISDN.\n"
 | 
				
			||||||
 | 
						"Length of an automatically assigned MSISDN.\n"
 | 
				
			||||||
 | 
						"Do not allow any NAM (Network Access Mode) by default.\n"
 | 
				
			||||||
 | 
						"Allow access to circuit switched NAM by default.\n"
 | 
				
			||||||
 | 
						"Allow access to packet switched NAM by default.\n"
 | 
				
			||||||
 | 
						"Allow access to circuit and packet switched NAM by default.\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int rand_msisdn_len = 0;
 | 
				
			||||||
 | 
						uint8_t flags = 0x00;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(argv[0], "no-msisdn") != 0)
 | 
				
			||||||
 | 
							rand_msisdn_len = atoi(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strstr(argv[1], "cs"))
 | 
				
			||||||
 | 
							flags |= DB_SUBSCR_FLAG_NAM_CS;
 | 
				
			||||||
 | 
						if (strstr(argv[1], "ps"))
 | 
				
			||||||
 | 
							flags |= DB_SUBSCR_FLAG_NAM_PS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_hlr->subscr_create_on_demand = true;
 | 
				
			||||||
 | 
						g_hlr->subscr_create_on_demand_rand_msisdn_len = rand_msisdn_len;
 | 
				
			||||||
 | 
						g_hlr->subscr_create_on_demand_flags = flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_no_subscr_create_on_demand, cfg_no_subscr_create_on_demand_cmd,
 | 
				
			||||||
 | 
						"no subscriber-create-on-demand",
 | 
				
			||||||
 | 
						"Do not make a new record when a subscriber is first seen.\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						g_hlr->subscr_create_on_demand = false;
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************
 | 
					/***********************************************************************
 | 
				
			||||||
 * Common Code
 | 
					 * Common Code
 | 
				
			||||||
 ***********************************************************************/
 | 
					 ***********************************************************************/
 | 
				
			||||||
@@ -344,10 +432,11 @@ int hlr_vty_is_config_node(struct vty *vty, int node)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void hlr_vty_init(const struct log_info *cat)
 | 
					void hlr_vty_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	logging_vty_add_cmds(cat);
 | 
						logging_vty_add_cmds();
 | 
				
			||||||
	osmo_talloc_vty_add_cmds();
 | 
						osmo_talloc_vty_add_cmds();
 | 
				
			||||||
 | 
						osmo_stats_vty_add_cmds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	install_element_ve(&show_gsup_conn_cmd);
 | 
						install_element_ve(&show_gsup_conn_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -359,6 +448,8 @@ void hlr_vty_init(const struct log_info *cat)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
						install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_database_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_euse_cmd);
 | 
						install_element(HLR_NODE, &cfg_euse_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_no_euse_cmd);
 | 
						install_element(HLR_NODE, &cfg_no_euse_cmd);
 | 
				
			||||||
	install_node(&euse_node, config_write_euse);
 | 
						install_node(&euse_node, config_write_euse);
 | 
				
			||||||
@@ -368,6 +459,10 @@ void hlr_vty_init(const struct log_info *cat)
 | 
				
			|||||||
	install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
 | 
						install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
 | 
						install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
 | 
				
			||||||
	install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
 | 
						install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_store_imei_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_no_store_imei_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_no_subscr_create_on_demand_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hlr_vty_subscriber_init();
 | 
						hlr_vty_subscriber_init();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,4 +35,4 @@ enum hlr_vty_node {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
					int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
				
			||||||
int hlr_vty_go_parent(struct vty *vty);
 | 
					int hlr_vty_go_parent(struct vty *vty);
 | 
				
			||||||
void hlr_vty_init(const struct log_info *cat);
 | 
					void hlr_vty_init(void);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@
 | 
				
			|||||||
#include <osmocom/vty/vty.h>
 | 
					#include <osmocom/vty/vty.h>
 | 
				
			||||||
#include <osmocom/vty/command.h>
 | 
					#include <osmocom/vty/command.h>
 | 
				
			||||||
#include <osmocom/core/utils.h>
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "hlr.h"
 | 
					#include "hlr.h"
 | 
				
			||||||
#include "db.h"
 | 
					#include "db.h"
 | 
				
			||||||
@@ -35,29 +36,37 @@ struct vty;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
					#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char *
 | 
					static char *get_datestr(const time_t *t)
 | 
				
			||||||
get_datestr(const time_t *t, char *datebuf)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *p, *s = ctime_r(t, datebuf);
 | 
						static char buf[32];
 | 
				
			||||||
 | 
						struct tm tm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Strip trailing newline. */
 | 
						tm = *gmtime(t);
 | 
				
			||||||
	p = strchr(s, '\n');
 | 
					
 | 
				
			||||||
	if (p)
 | 
						strftime(buf, sizeof(buf), "%FT%T+00:00", &tm);
 | 
				
			||||||
		*p = '\0';
 | 
						return buf;
 | 
				
			||||||
	return s;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
					static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
						struct osmo_sub_auth_data aud2g;
 | 
				
			||||||
	struct osmo_sub_auth_data aud3g;
 | 
						struct osmo_sub_auth_data aud3g;
 | 
				
			||||||
	char datebuf[26]; /* for ctime_r(3) */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
 | 
						vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vty_out(vty, "    IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
 | 
						vty_out(vty, "    IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
 | 
				
			||||||
	vty_out(vty, "    MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
 | 
						vty_out(vty, "    MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (*subscr->imei) {
 | 
				
			||||||
 | 
							char checksum = osmo_luhn(subscr->imei, 14);
 | 
				
			||||||
 | 
							if (checksum == -EINVAL)
 | 
				
			||||||
 | 
								vty_out(vty, "    IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								vty_out(vty, "    IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (*subscr->vlr_number)
 | 
						if (*subscr->vlr_number)
 | 
				
			||||||
		vty_out(vty, "    VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
 | 
							vty_out(vty, "    VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
 | 
				
			||||||
	if (*subscr->sgsn_number)
 | 
						if (*subscr->sgsn_number)
 | 
				
			||||||
@@ -79,7 +88,15 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			|||||||
	if (subscr->ms_purged_ps)
 | 
						if (subscr->ms_purged_ps)
 | 
				
			||||||
		vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
							vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
				
			||||||
	if (subscr->last_lu_seen)
 | 
						if (subscr->last_lu_seen)
 | 
				
			||||||
		vty_out(vty, "    last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);
 | 
							vty_out(vty, "    last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (subscr->last_lu_rat[0])
 | 
				
			||||||
 | 
							vty_out(vty, "    last LU RAT: %s%s", subscr->last_lu_rat, VTY_NEWLINE);
 | 
				
			||||||
 | 
						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->ms_purged_cs)
 | 
				
			||||||
 | 
							vty_out(vty, "    CS purged%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*subscr->imsi)
 | 
						if (!*subscr->imsi)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
@@ -131,6 +148,7 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
 | 
					static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
 | 
				
			||||||
	int rc = -1;
 | 
						int rc = -1;
 | 
				
			||||||
	if (strcmp(type, "imsi") == 0)
 | 
						if (strcmp(type, "imsi") == 0)
 | 
				
			||||||
		rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
 | 
							rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
 | 
				
			||||||
@@ -138,6 +156,17 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
 | 
				
			|||||||
		rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
 | 
							rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
 | 
				
			||||||
	else if (strcmp(type, "id") == 0)
 | 
						else if (strcmp(type, "id") == 0)
 | 
				
			||||||
		rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
 | 
							rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
 | 
				
			||||||
 | 
						else if (strcmp(type, "imei") == 0) {
 | 
				
			||||||
 | 
							/* Verify IMEI with checksum digit */
 | 
				
			||||||
 | 
							if (osmo_imei_str_valid(id, true)) {
 | 
				
			||||||
 | 
								/* Cut the checksum off */
 | 
				
			||||||
 | 
								osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
 | 
				
			||||||
 | 
								id = imei_buf;
 | 
				
			||||||
 | 
								vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
 | 
				
			||||||
 | 
									VTY_NEWLINE);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (rc)
 | 
						if (rc)
 | 
				
			||||||
		vty_out(vty, "%% No subscriber for %s = '%s'%s",
 | 
							vty_out(vty, "%% No subscriber for %s = '%s'%s",
 | 
				
			||||||
			type, id, VTY_NEWLINE);
 | 
								type, id, VTY_NEWLINE);
 | 
				
			||||||
@@ -147,12 +176,13 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
 | 
				
			|||||||
#define SUBSCR_CMD "subscriber "
 | 
					#define SUBSCR_CMD "subscriber "
 | 
				
			||||||
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
 | 
					#define SUBSCR_CMD_HELP "Subscriber management commands\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SUBSCR_ID "(imsi|msisdn|id) IDENT"
 | 
					#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
 | 
				
			||||||
#define SUBSCR_ID_HELP \
 | 
					#define SUBSCR_ID_HELP \
 | 
				
			||||||
	"Identify subscriber by IMSI\n" \
 | 
						"Identify subscriber by IMSI\n" \
 | 
				
			||||||
	"Identify subscriber by MSISDN (phone number)\n" \
 | 
						"Identify subscriber by MSISDN (phone number)\n" \
 | 
				
			||||||
	"Identify subscriber by database ID\n" \
 | 
						"Identify subscriber by database ID\n" \
 | 
				
			||||||
	"IMSI/MSISDN/ID of the subscriber\n"
 | 
						"Identify subscriber by IMEI\n" \
 | 
				
			||||||
 | 
						"IMSI/MSISDN/ID/IMEI of the subscriber\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SUBSCR 		SUBSCR_CMD SUBSCR_ID " "
 | 
					#define SUBSCR 		SUBSCR_CMD SUBSCR_ID " "
 | 
				
			||||||
#define SUBSCR_HELP	SUBSCR_CMD_HELP SUBSCR_ID_HELP
 | 
					#define SUBSCR_HELP	SUBSCR_CMD_HELP SUBSCR_ID_HELP
 | 
				
			||||||
@@ -198,7 +228,7 @@ DEFUN(subscriber_create,
 | 
				
			|||||||
		return CMD_WARNING;
 | 
							return CMD_WARNING;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = db_subscr_create(g_hlr->dbc, imsi);
 | 
						rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rc) {
 | 
						if (rc) {
 | 
				
			||||||
		if (rc == -EEXIST)
 | 
							if (rc == -EEXIST)
 | 
				
			||||||
@@ -508,6 +538,122 @@ DEFUN(subscriber_aud3g,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(subscriber_imei,
 | 
				
			||||||
 | 
					      subscriber_imei_cmd,
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE "imei (none|IMEI)",
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE_HELP
 | 
				
			||||||
 | 
					      "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
 | 
				
			||||||
 | 
					      "Forget IMEI\n"
 | 
				
			||||||
 | 
					      "Set IMEI (use for debug only!)\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						const char *id_type = argv[0];
 | 
				
			||||||
 | 
						const char *id = argv[1];
 | 
				
			||||||
 | 
						const char *imei = argv[2];
 | 
				
			||||||
 | 
						char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(imei, "none") == 0)
 | 
				
			||||||
 | 
							imei = NULL;
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							/* Verify IMEI with checksum digit */
 | 
				
			||||||
 | 
							if (osmo_imei_str_valid(imei, true)) {
 | 
				
			||||||
 | 
								/* Cut the checksum off */
 | 
				
			||||||
 | 
								osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
 | 
				
			||||||
 | 
								imei = imei_buf;
 | 
				
			||||||
 | 
							} else if (!osmo_imei_str_valid(imei, false)) {
 | 
				
			||||||
 | 
								vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
 | 
				
			||||||
 | 
								return CMD_WARNING;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (get_subscr_by_argv(vty, id_type, id, &subscr))
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
 | 
				
			||||||
 | 
								subscr.imsi, VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (imei)
 | 
				
			||||||
 | 
							vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
 | 
				
			||||||
 | 
								subscr.imsi, imei, VTY_NEWLINE);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
 | 
				
			||||||
 | 
								subscr.imsi, VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(subscriber_nam,
 | 
				
			||||||
 | 
					      subscriber_nam_cmd,
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE_HELP
 | 
				
			||||||
 | 
					      "Set Network Access Mode (NAM) of the subscriber\n"
 | 
				
			||||||
 | 
					      "Do not allow access to circuit switched or packet switched services\n"
 | 
				
			||||||
 | 
					      "Allow access to circuit switched services only\n"
 | 
				
			||||||
 | 
					      "Allow access to packet switched services only\n"
 | 
				
			||||||
 | 
					      "Allow access to both circuit and packet switched services\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						const char *id_type = argv[0];
 | 
				
			||||||
 | 
						const char *id = argv[1];
 | 
				
			||||||
 | 
						bool nam_cs = strstr(argv[2], "cs");
 | 
				
			||||||
 | 
						bool nam_ps = strstr(argv[2], "ps");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (get_subscr_by_argv(vty, id_type, id, &subscr))
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nam_cs != subscr.nam_cs)
 | 
				
			||||||
 | 
							hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
 | 
				
			||||||
 | 
						if (nam_ps != subscr.nam_ps)
 | 
				
			||||||
 | 
							hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(subscriber_rat,
 | 
				
			||||||
 | 
					      subscriber_rat_cmd,
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE "rat (geran-a|utran-iu) (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"
 | 
				
			||||||
 | 
					      "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") == 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)
 | 
					void hlr_vty_subscriber_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	install_element_ve(&subscriber_show_cmd);
 | 
						install_element_ve(&subscriber_show_cmd);
 | 
				
			||||||
@@ -519,4 +665,7 @@ void hlr_vty_subscriber_init(void)
 | 
				
			|||||||
	install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
				
			||||||
 | 
						install_element(ENABLE_NODE, &subscriber_imei_cmd);
 | 
				
			||||||
 | 
						install_element(ENABLE_NODE, &subscriber_nam_cmd);
 | 
				
			||||||
 | 
						install_element(ENABLE_NODE, &subscriber_rat_cmd);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,9 @@ struct lu_operation {
 | 
				
			|||||||
	enum lu_state state;
 | 
						enum lu_state state;
 | 
				
			||||||
	/*! CS (false) or PS (true) Location Update? */
 | 
						/*! CS (false) or PS (true) Location Update? */
 | 
				
			||||||
	bool is_ps;
 | 
						bool is_ps;
 | 
				
			||||||
 | 
						/*! RAT type indicator: coming in on GERAN-A? UTRAN-Iu? */
 | 
				
			||||||
 | 
						enum osmo_rat_type via_rat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*! currently running timer */
 | 
						/*! currently running timer */
 | 
				
			||||||
	struct osmo_timer_list timer;
 | 
						struct osmo_timer_list timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,6 +56,8 @@ VTY_TEST_DB = hlr_vty_test.db
 | 
				
			|||||||
#   make vty-test U=-u
 | 
					#   make vty-test U=-u
 | 
				
			||||||
vty-test:
 | 
					vty-test:
 | 
				
			||||||
	-rm -f $(VTY_TEST_DB)
 | 
						-rm -f $(VTY_TEST_DB)
 | 
				
			||||||
 | 
						sqlite3 $(VTY_TEST_DB) < $(top_srcdir)/sql/hlr.sql
 | 
				
			||||||
 | 
						sqlite3 $(VTY_TEST_DB) < $(srcdir)/test_subscriber.vty.sql
 | 
				
			||||||
	osmo_verify_transcript_vty.py -v \
 | 
						osmo_verify_transcript_vty.py -v \
 | 
				
			||||||
		-n OsmoHLR -p 4258 \
 | 
							-n OsmoHLR -p 4258 \
 | 
				
			||||||
		-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(VTY_TEST_DB)" \
 | 
							-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(VTY_TEST_DB)" \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ AM_CFLAGS = \
 | 
				
			|||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_LDFLAGS = \
 | 
					AM_LDFLAGS = \
 | 
				
			||||||
 | 
						-no-install \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,10 @@ AM_CFLAGS = \
 | 
				
			|||||||
	$(SQLITE3_CFLAGS) \
 | 
						$(SQLITE3_CFLAGS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_LDFLAGS = \
 | 
				
			||||||
 | 
						-no-install \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
	db_test.ok \
 | 
						db_test.ok \
 | 
				
			||||||
	db_test.err \
 | 
						db_test.err \
 | 
				
			||||||
@@ -22,16 +26,20 @@ db_test_SOURCES = \
 | 
				
			|||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_test_LDADD = \
 | 
					db_test_LDADD = \
 | 
				
			||||||
	$(top_srcdir)/src/db.c \
 | 
						$(top_builddir)/src/logging.o \
 | 
				
			||||||
	$(top_srcdir)/src/db_hlr.c \
 | 
						$(top_builddir)/src/db_auc.o \
 | 
				
			||||||
	$(top_srcdir)/src/db_auc.c \
 | 
						$(top_builddir)/src/db_hlr.o \
 | 
				
			||||||
	$(top_srcdir)/src/logging.c \
 | 
						$(top_builddir)/src/db.o \
 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
	$(LIBOSMOGSM_LIBS) \
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
	$(LIBOSMOABIS_LIBS) \
 | 
						$(LIBOSMOABIS_LIBS) \
 | 
				
			||||||
	$(SQLITE3_LIBS) \
 | 
						$(SQLITE3_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if DB_SQLITE_DEBUG
 | 
				
			||||||
 | 
					db_test_LDADD += $(top_builddir)/src/db_debug.o
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: db_test.db update_exp manual manual-nonverbose manual-gdb
 | 
					.PHONY: db_test.db update_exp manual manual-nonverbose manual-gdb
 | 
				
			||||||
db_test.db:
 | 
					db_test.db:
 | 
				
			||||||
	rm -f db_test.db
 | 
						rm -f db_test.db
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,12 @@ static void _fill_invalid(void *dest, size_t size)
 | 
				
			|||||||
 * The return code is then available in g_rc. */
 | 
					 * The return code is then available in g_rc. */
 | 
				
			||||||
#define ASSERT_RC(call, expect_rc) \
 | 
					#define ASSERT_RC(call, expect_rc) \
 | 
				
			||||||
	do { \
 | 
						do { \
 | 
				
			||||||
		fprintf(stderr, #call " --> " #expect_rc "\n"); \
 | 
					    if ((expect_rc) == -ENOKEY) \
 | 
				
			||||||
 | 
							  fprintf(stderr, #call " --> -ENOKEY\n"); \
 | 
				
			||||||
 | 
					    else if ((expect_rc) == -ENOTSUP) \
 | 
				
			||||||
 | 
							  fprintf(stderr, #call " --> -ENOTSUP\n"); \
 | 
				
			||||||
 | 
					    else \
 | 
				
			||||||
 | 
							  fprintf(stderr, #call " --> " #expect_rc "\n"); \
 | 
				
			||||||
		g_rc = call; \
 | 
							g_rc = call; \
 | 
				
			||||||
		if (g_rc != (expect_rc)) \
 | 
							if (g_rc != (expect_rc)) \
 | 
				
			||||||
			fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
 | 
								fprintf(stderr, " MISMATCH: got rc = %d, expected: " \
 | 
				
			||||||
@@ -67,7 +72,12 @@ static void _fill_invalid(void *dest, size_t size)
 | 
				
			|||||||
	do { \
 | 
						do { \
 | 
				
			||||||
		int rc; \
 | 
							int rc; \
 | 
				
			||||||
		fill_invalid(g_subscr); \
 | 
							fill_invalid(g_subscr); \
 | 
				
			||||||
		fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> " \
 | 
					    if ((expect_rc) == -ENOKEY) \
 | 
				
			||||||
 | 
							  fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> -ENOKEY \n"); \
 | 
				
			||||||
 | 
					    else if ((expect_rc) == -ENOTSUP) \
 | 
				
			||||||
 | 
							  fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> -ENOTSUP \n"); \
 | 
				
			||||||
 | 
					    else \
 | 
				
			||||||
 | 
							  fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> " \
 | 
				
			||||||
                                #expect_rc "\n"); \
 | 
					                                #expect_rc "\n"); \
 | 
				
			||||||
		rc = db_subscr_get_by_##by(dbc, val, &g_subscr); \
 | 
							rc = db_subscr_get_by_##by(dbc, val, &g_subscr); \
 | 
				
			||||||
		if (rc != (expect_rc)) \
 | 
							if (rc != (expect_rc)) \
 | 
				
			||||||
@@ -148,6 +158,7 @@ void dump_subscr(struct hlr_subscriber *subscr)
 | 
				
			|||||||
	Pd(id);
 | 
						Pd(id);
 | 
				
			||||||
	Ps(imsi);
 | 
						Ps(imsi);
 | 
				
			||||||
	Ps(msisdn);
 | 
						Ps(msisdn);
 | 
				
			||||||
 | 
						Ps(imei);
 | 
				
			||||||
	Ps(vlr_number);
 | 
						Ps(vlr_number);
 | 
				
			||||||
	Ps(sgsn_number);
 | 
						Ps(sgsn_number);
 | 
				
			||||||
	Ps(sgsn_address);
 | 
						Ps(sgsn_address);
 | 
				
			||||||
@@ -159,6 +170,7 @@ void dump_subscr(struct hlr_subscriber *subscr)
 | 
				
			|||||||
		Pfo(lmsi, "0x%x", subscr);
 | 
							Pfo(lmsi, "0x%x", subscr);
 | 
				
			||||||
	Pb(true, ms_purged_cs);
 | 
						Pb(true, ms_purged_cs);
 | 
				
			||||||
	Pb(true, ms_purged_ps);
 | 
						Pb(true, ms_purged_ps);
 | 
				
			||||||
 | 
						Ps(last_lu_rat);
 | 
				
			||||||
	fprintf(stderr, "}\n");
 | 
						fprintf(stderr, "}\n");
 | 
				
			||||||
#undef Ps
 | 
					#undef Ps
 | 
				
			||||||
#undef Pd
 | 
					#undef Pd
 | 
				
			||||||
@@ -207,11 +219,23 @@ void dump_aud(const char *label, struct osmo_sub_auth_data *aud)
 | 
				
			|||||||
#undef Phex
 | 
					#undef Phex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void db_raw_sql(struct db_context *dbc, const char *sql)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fprintf(stderr, "raw SQL: %s\n", sql);
 | 
				
			||||||
 | 
						ASSERT_RC(sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL), SQLITE_OK);
 | 
				
			||||||
 | 
						ASSERT_RC(sqlite3_step(stmt), SQLITE_DONE);
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *imsi0 = "123456789000000";
 | 
					static const char *imsi0 = "123456789000000";
 | 
				
			||||||
static const char *imsi1 = "123456789000001";
 | 
					static const char *imsi1 = "123456789000001";
 | 
				
			||||||
static const char *imsi2 = "123456789000002";
 | 
					static const char *imsi2 = "123456789000002";
 | 
				
			||||||
static const char *short_imsi = "123456";
 | 
					static const char *short_imsi = "123456";
 | 
				
			||||||
static const char *unknown_imsi = "999999999";
 | 
					static const char *unknown_imsi = "999999999";
 | 
				
			||||||
 | 
					static const enum osmo_rat_type rat_types[2] = { OSMO_RAT_GERAN_A, OSMO_RAT_UTRAN_IU, };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_subscr_create_update_sel_delete()
 | 
					static void test_subscr_create_update_sel_delete()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -220,40 +244,45 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Create with valid / invalid IMSI");
 | 
						comment("Create with valid / invalid IMSI");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
	id0 = g_subscr.id;
 | 
						id0 = g_subscr.id;
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi1), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi1, 0);
 | 
						ASSERT_SEL(imsi, imsi1, 0);
 | 
				
			||||||
	id1 = g_subscr.id;
 | 
						id1 = g_subscr.id;
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi2), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi2, 0);
 | 
						ASSERT_SEL(imsi, imsi2, 0);
 | 
				
			||||||
	id2 = g_subscr.id;
 | 
						id2 = g_subscr.id;
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0), -EIO);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi1), -EIO);
 | 
						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), -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_SEL(imsi, imsi1, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi2), -EIO);
 | 
						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), -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_SEL(imsi, imsi2, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, "123456789 000003"), -EINVAL);
 | 
						ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
				
			||||||
	ASSERT_SEL(imsi, "123456789000003", -ENOENT);
 | 
						ASSERT_SEL(imsi, "123456789000003", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, "123456789000002123456"), -EINVAL);
 | 
						ASSERT_RC(db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS),
 | 
				
			||||||
 | 
							  -EINVAL);
 | 
				
			||||||
	ASSERT_SEL(imsi, "123456789000002123456", -ENOENT);
 | 
						ASSERT_SEL(imsi, "123456789000002123456", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, "foobar123"), -EINVAL);
 | 
						ASSERT_RC(db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
				
			||||||
	ASSERT_SEL(imsi, "foobar123", -ENOENT);
 | 
						ASSERT_SEL(imsi, "foobar123", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, "123"), -EINVAL);
 | 
						ASSERT_RC(db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
 | 
				
			||||||
	ASSERT_SEL(imsi, "123", -ENOENT);
 | 
						ASSERT_SEL(imsi, "123", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, short_imsi), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, short_imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, short_imsi, 0);
 | 
						ASSERT_SEL(imsi, short_imsi, 0);
 | 
				
			||||||
	id_short = g_subscr.id;
 | 
						id_short = g_subscr.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Check if subscriber exists (by IMSI)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_exists_by_imsi(dbc, imsi0), 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_exists_by_imsi(dbc, unknown_imsi), -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Set valid / invalid MSISDN");
 | 
						comment("Set valid / invalid MSISDN");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,6 +317,11 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
	ASSERT_SEL(msisdn, "5432101234567891", -ENOENT);
 | 
						ASSERT_SEL(msisdn, "5432101234567891", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Check if subscriber exists (by MSISDN)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_exists_by_msisdn(dbc, "543210123456789"), 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_exists_by_msisdn(dbc, "5432101234567891"), -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Set MSISDN on non-existent / invalid IMSI");
 | 
						comment("Set MSISDN on non-existent / invalid IMSI");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99"), -ENOENT);
 | 
						ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99"), -ENOENT);
 | 
				
			||||||
@@ -296,6 +330,23 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99"), -ENOENT);
 | 
						ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, "foobar", "99"), -ENOENT);
 | 
				
			||||||
	ASSERT_SEL(msisdn, "99", -ENOENT);
 | 
						ASSERT_SEL(msisdn, "99", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Set valid / invalid IMEI");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234"), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imei, "12345678901234", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "123456789012345"), -EINVAL); /* too long */
 | 
				
			||||||
 | 
						ASSERT_SEL(imei, "12345678901234", 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imei, "123456789012345", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Set the same IMEI again");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234"), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imei, "12345678901234", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Remove IMEI");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_imei_by_imsi(dbc, imsi0, NULL), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imei, "12345678901234", -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Set / unset nam_cs and nam_ps");
 | 
						comment("Set / unset nam_cs and nam_ps");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*                                nam_val, is_ps */
 | 
						/*                                nam_val, is_ps */
 | 
				
			||||||
@@ -337,39 +388,44 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "712", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
						comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for non-existent ID");
 | 
						comment("Record LU for non-existent ID");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true, NULL, 0), -ENOENT);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false, NULL, 0), -ENOENT);
 | 
				
			||||||
	ASSERT_SEL(id, 99999, -ENOENT);
 | 
						ASSERT_SEL(id, 99999, -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Purge and un-purge PS and CS");
 | 
						comment("Purge and un-purge PS and CS");
 | 
				
			||||||
@@ -434,6 +490,22 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
	ASSERT_RC(db_subscr_delete_by_id(dbc, id_short), 0);
 | 
						ASSERT_RC(db_subscr_delete_by_id(dbc, id_short), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, short_imsi, -ENOENT);
 | 
						ASSERT_SEL(imsi, short_imsi, -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Create and delete subscribers with non-default nam_cs and nam_ps");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, 0x00), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
 | 
						id0 = g_subscr.id;
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imsi, imsi1, 0);
 | 
				
			||||||
 | 
						id1 = g_subscr.id;
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imsi, imsi2, 0);
 | 
				
			||||||
 | 
						id2 = g_subscr.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_delete_by_id(dbc, id0), 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_delete_by_id(dbc, id1), 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_delete_by_id(dbc, id2), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -477,7 +549,7 @@ static void test_subscr_aud()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Create subscriber");
 | 
						comment("Create subscriber");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id = g_subscr.id;
 | 
						id = g_subscr.id;
 | 
				
			||||||
@@ -689,7 +761,7 @@ static void test_subscr_aud()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Re-add subscriber and verify auth data didn't come back");
 | 
						comment("Re-add subscriber and verify auth data didn't come back");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* For this test to work, we want to get the same subscriber ID back,
 | 
						/* For this test to work, we want to get the same subscriber ID back,
 | 
				
			||||||
@@ -705,6 +777,70 @@ static void test_subscr_aud()
 | 
				
			|||||||
	comment_end();
 | 
						comment_end();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the
 | 
				
			||||||
 | 
					 * table structure. */
 | 
				
			||||||
 | 
					static void test_subscr_aud_invalid_len()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int64_t id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment_start();
 | 
				
			||||||
 | 
						comment("Create subscriber");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
 | 
						id = g_subscr.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Invalid Ki length */
 | 
				
			||||||
 | 
						comment("Set auth data, 2G only, with invalid Ki length");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
 | 
				
			||||||
 | 
							  mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")),
 | 
				
			||||||
 | 
							  0);
 | 
				
			||||||
 | 
						/* Use raw SQL to avoid length check in db_subscr_update_aud_by_id(). This changes all rows in the table, which
 | 
				
			||||||
 | 
					         * is fine for this test (implicit WHERE 1). */
 | 
				
			||||||
 | 
						db_raw_sql(dbc, "UPDATE auc_2g SET ki = '0123456789abcdef0123456789abcde'");
 | 
				
			||||||
 | 
						ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Remove 2G auth data");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
 | 
				
			||||||
 | 
							  mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)),
 | 
				
			||||||
 | 
							  0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Invalid K length */
 | 
				
			||||||
 | 
						comment("Set auth data, 3G only, with invalid K length");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
 | 
				
			||||||
 | 
							mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
 | 
				
			||||||
 | 
								  "BeefedCafeFaceAcedAddedDecadeFee", true,
 | 
				
			||||||
 | 
								  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
 | 
				
			||||||
 | 
							0);
 | 
				
			||||||
 | 
						db_raw_sql(dbc, "UPDATE auc_3g SET k = 'C01ffedC1cadaeAc1d1f1edAcac1aB0'");
 | 
				
			||||||
 | 
						ASSERT_SEL_AUD(imsi0, -EIO, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Invalid OP length */
 | 
				
			||||||
 | 
						comment("Set auth data, 3G only, with invalid OP length");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
 | 
				
			||||||
 | 
							mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
 | 
				
			||||||
 | 
								  "BeefedCafeFaceAcedAddedDecadeFee", true,
 | 
				
			||||||
 | 
								  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
 | 
				
			||||||
 | 
							0);
 | 
				
			||||||
 | 
						db_raw_sql(dbc, "UPDATE auc_3g SET op = 'BeefedCafeFaceAcedAddedDecadeFe'");
 | 
				
			||||||
 | 
						ASSERT_SEL_AUD(imsi0, -EIO, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Invalid OPC length */
 | 
				
			||||||
 | 
						comment("Set auth data, 3G only, with invalid OPC length");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
 | 
				
			||||||
 | 
							mk_aud_3g(OSMO_AUTH_ALG_MILENAGE,
 | 
				
			||||||
 | 
								  "BeefedCafeFaceAcedAddedDecadeFee", false,
 | 
				
			||||||
 | 
								  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
 | 
				
			||||||
 | 
							0);
 | 
				
			||||||
 | 
						db_raw_sql(dbc, "UPDATE auc_3g SET opc = 'BeefedCafeFaceAcedAddedDecadeFe'");
 | 
				
			||||||
 | 
						ASSERT_SEL_AUD(imsi0, -EIO, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment("Delete subscriber");
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
 | 
				
			||||||
 | 
						comment_end();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_subscr_sqn()
 | 
					static void test_subscr_sqn()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int64_t id;
 | 
						int64_t id;
 | 
				
			||||||
@@ -721,7 +857,7 @@ static void test_subscr_sqn()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Create subscriber");
 | 
						comment("Create subscriber");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_create(dbc, imsi0), 0);
 | 
						ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
 | 
				
			||||||
	ASSERT_SEL(imsi, imsi0, 0);
 | 
						ASSERT_SEL(imsi, imsi0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id = g_subscr.id;
 | 
						id = g_subscr.id;
 | 
				
			||||||
@@ -856,9 +992,11 @@ int main(int argc, char **argv)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	test_subscr_create_update_sel_delete();
 | 
						test_subscr_create_update_sel_delete();
 | 
				
			||||||
	test_subscr_aud();
 | 
						test_subscr_aud();
 | 
				
			||||||
 | 
						test_subscr_aud_invalid_len();
 | 
				
			||||||
	test_subscr_sqn();
 | 
						test_subscr_sqn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("Done\n");
 | 
						printf("Done\n");
 | 
				
			||||||
 | 
						db_close(dbc);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Create with valid / invalid IMSI
 | 
					--- Create with valid / invalid IMSI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0) --> 0
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -11,7 +11,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi1) --> 0
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -19,7 +19,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000001',
 | 
					  .imsi = '123456789000001',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi2) --> 0
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -27,7 +27,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000002',
 | 
					  .imsi = '123456789000002',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0) --> -EIO
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
@@ -36,10 +36,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi1) --> -EIO
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi1) --> -EIO
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
				
			||||||
@@ -48,10 +48,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000001',
 | 
					  .imsi = '123456789000001',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi2) --> -EIO
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi2) --> -EIO
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
 | 
				
			||||||
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
					DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
				
			||||||
@@ -60,31 +60,31 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000002',
 | 
					  .imsi = '123456789000002',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "123456789 000003") --> -EINVAL
 | 
					db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: '123456789 000003'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123456789 000003'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123456789000003", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123456789000003", &g_subscr) --> -ENOENT
 | 
				
			||||||
DAUC Cannot read subscriber from db: IMSI='123456789000003': No such subscriber
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000003': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "123456789000002123456") --> -EINVAL
 | 
					db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: '123456789000002123456'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123456789000002123456'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123456789000002123456", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123456789000002123456", &g_subscr) --> -ENOENT
 | 
				
			||||||
DAUC Cannot read subscriber from db: IMSI='123456789000002123456': No such subscriber
 | 
					DAUC Cannot read subscriber from db: IMSI='123456789000002123456': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "foobar123") --> -EINVAL
 | 
					db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: 'foobar123'
 | 
					DAUC Cannot create subscriber: invalid IMSI: 'foobar123'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "foobar123", &g_subscr) --> -ENOENT
 | 
				
			||||||
DAUC Cannot read subscriber from db: IMSI='foobar123': No such subscriber
 | 
					DAUC Cannot read subscriber from db: IMSI='foobar123': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, "123") --> -EINVAL
 | 
					db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EINVAL
 | 
				
			||||||
DAUC Cannot create subscriber: invalid IMSI: '123'
 | 
					DAUC Cannot create subscriber: invalid IMSI: '123'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, "123", &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, "123", &g_subscr) --> -ENOENT
 | 
				
			||||||
DAUC Cannot read subscriber from db: IMSI='123': No such subscriber
 | 
					DAUC Cannot read subscriber from db: IMSI='123': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, short_imsi) --> 0
 | 
					db_subscr_create(dbc, short_imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -93,6 +93,13 @@ struct hlr_subscriber {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Check if subscriber exists (by IMSI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_exists_by_imsi(dbc, imsi0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_exists_by_imsi(dbc, unknown_imsi) --> -ENOENT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set valid / invalid MSISDN
 | 
					--- Set valid / invalid MSISDN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
@@ -212,6 +219,13 @@ db_subscr_get_by_msisdn(dbc, "5432101234567891", &g_subscr) --> -ENOENT
 | 
				
			|||||||
DAUC Cannot read subscriber from db: MSISDN='5432101234567891': No such subscriber
 | 
					DAUC Cannot read subscriber from db: MSISDN='5432101234567891': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Check if subscriber exists (by MSISDN)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_exists_by_msisdn(dbc, "543210123456789") --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_exists_by_msisdn(dbc, "5432101234567891") --> -ENOENT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set MSISDN on non-existent / invalid IMSI
 | 
					--- Set MSISDN on non-existent / invalid IMSI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99") --> -ENOENT
 | 
					db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99") --> -ENOENT
 | 
				
			||||||
@@ -227,6 +241,54 @@ db_subscr_get_by_msisdn(dbc, "99", &g_subscr) --> -ENOENT
 | 
				
			|||||||
DAUC Cannot read subscriber from db: MSISDN='99': No such subscriber
 | 
					DAUC Cannot read subscriber from db: MSISDN='99': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set valid / invalid IMEI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234") --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .imei = '12345678901234',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_imei_by_imsi(dbc, imsi0, "123456789012345") --> -EINVAL
 | 
				
			||||||
 | 
					DAUC Cannot update subscriber IMSI='123456789000000': invalid IMEI: '123456789012345'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .imei = '12345678901234',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imei(dbc, "123456789012345", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMEI=123456789012345: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set the same IMEI again
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_imei_by_imsi(dbc, imsi0, "12345678901234") --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .imei = '12345678901234',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Remove IMEI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_imei_by_imsi(dbc, imsi0, NULL) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imei(dbc, "12345678901234", &g_subscr) --> -ENOENT
 | 
				
			||||||
 | 
					DAUC Cannot read subscriber from db: IMEI=12345678901234: No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set / unset nam_cs and nam_ps
 | 
					--- Set / unset nam_cs and nam_ps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_nam(dbc, imsi0, false, true) --> 0
 | 
					db_subscr_nam(dbc, imsi0, false, true) --> 0
 | 
				
			||||||
@@ -373,7 +435,7 @@ DAUC Cannot disable CS: no such subscriber: IMSI='foobar'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names)
 | 
					--- Record LU for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "5952", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "5952", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -383,7 +445,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '5952',
 | 
					  .sgsn_number = '5952',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "712", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "712", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -397,7 +459,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
					--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -408,7 +470,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -419,7 +481,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -430,7 +492,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -441,20 +503,44 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .vlr_number = '333',
 | 
				
			||||||
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
 | 
					  .last_lu_rat = 'GERAN-A',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .vlr_number = '333',
 | 
				
			||||||
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
 | 
					  .last_lu_rat = 'GERAN-A',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
					--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
  .id = 1,
 | 
					  .id = 1,
 | 
				
			||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
  .msisdn = '543210123456789',
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '333',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -463,9 +549,9 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .msisdn = '543210123456789',
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -476,7 +562,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, NULL, true) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -486,7 +572,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '222',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, NULL, false) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -498,10 +584,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for non-existent ID
 | 
					--- Record LU for non-existent ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, 99999, "5952", true) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "5952", true, NULL, 0) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, 99999, "712", false) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "712", false, NULL, 0) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
				
			||||||
@@ -704,6 +790,43 @@ db_subscr_delete_by_id(dbc, id_short) --> 0
 | 
				
			|||||||
db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_imsi(dbc, short_imsi, &g_subscr) --> -ENOENT
 | 
				
			||||||
DAUC Cannot read subscriber from db: IMSI='123456': No such subscriber
 | 
					DAUC Cannot read subscriber from db: IMSI='123456': No such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Create and delete subscribers with non-default nam_cs and nam_ps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_create(dbc, imsi0, 0x00) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .nam_cs = false,
 | 
				
			||||||
 | 
					  .nam_ps = false,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 2,
 | 
				
			||||||
 | 
					  .imsi = '123456789000001',
 | 
				
			||||||
 | 
					  .nam_ps = false,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 3,
 | 
				
			||||||
 | 
					  .imsi = '123456789000002',
 | 
				
			||||||
 | 
					  .nam_cs = false,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_delete_by_id(dbc, id0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_delete_by_id(dbc, id1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_delete_by_id(dbc, id2) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
===== test_subscr_create_update_sel_delete: SUCCESS
 | 
					===== test_subscr_create_update_sel_delete: SUCCESS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -721,7 +844,7 @@ DAUC IMSI='123456789000000': No such subscriber
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Create subscriber
 | 
					--- Create subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0) --> 0
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -729,12 +852,12 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -811,12 +934,12 @@ DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> 0
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -836,12 +959,12 @@ DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")) --> 0
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -938,12 +1061,12 @@ DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> 0
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -973,12 +1096,12 @@ DAUC IMSI='123456789000000': Updating SQN=0 in DB
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, "asdfasdfasd", false, "asdfasdfasdf", 99999)) --> 0
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, "asdfasdfasd", false, "asdfasdfasdf", 99999)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1211,7 +1334,7 @@ DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Re-add subscriber and verify auth data didn't come back
 | 
					--- Re-add subscriber and verify auth data didn't come back
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0) --> 0
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -1219,12 +1342,12 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
 | 
					db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1239,6 +1362,91 @@ DAUC IMSI='123456789000000': No such subscriber
 | 
				
			|||||||
===== test_subscr_aud: SUCCESS
 | 
					===== test_subscr_aud: SUCCESS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					===== test_subscr_aud_invalid_len
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Create subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set auth data, 2G only, with invalid Ki length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					raw SQL: UPDATE auc_2g SET ki = '0123456789abcdef0123456789abcde'
 | 
				
			||||||
 | 
					sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL) --> SQLITE_OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3_step(stmt) --> SQLITE_DONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': Error reading Ki, expected length 16 but has length 15
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Remove 2G auth data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set auth data, 3G only, with invalid K length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", true, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					raw SQL: UPDATE auc_3g SET k = 'C01ffedC1cadaeAc1d1f1edAcac1aB0'
 | 
				
			||||||
 | 
					sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL) --> SQLITE_OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3_step(stmt) --> SQLITE_DONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': Error reading K, expected length 16 but has length 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set auth data, 3G only, with invalid OP length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", true, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					raw SQL: UPDATE auc_3g SET op = 'BeefedCafeFaceAcedAddedDecadeFe'
 | 
				
			||||||
 | 
					sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL) --> SQLITE_OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3_step(stmt) --> SQLITE_DONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': Error reading OP, expected length 16 but has length 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Set auth data, 3G only, with invalid OPC length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", false, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					raw SQL: UPDATE auc_3g SET opc = 'BeefedCafeFaceAcedAddedDecadeFe'
 | 
				
			||||||
 | 
					sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL) --> SQLITE_OK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3_step(stmt) --> SQLITE_DONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
 | 
					DAUC IMSI='123456789000000': Error reading OPC, expected length 16 but has length 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Delete subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_delete_by_id(dbc, id) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					===== test_subscr_aud_invalid_len: SUCCESS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
===== test_subscr_sqn
 | 
					===== test_subscr_sqn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Set SQN for unknown subscriber
 | 
					--- Set SQN for unknown subscriber
 | 
				
			||||||
@@ -1258,7 +1466,7 @@ DAUC Cannot read subscriber from db: ID=9999: No such subscriber
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Create subscriber
 | 
					--- Create subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_create(dbc, imsi0) --> 0
 | 
					db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
					db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -1266,7 +1474,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1277,7 +1485,7 @@ DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			|||||||
db_update_sqn(dbc, id, 123) --> -ENOENT
 | 
					db_update_sqn(dbc, id, 123) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
 | 
					DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1285,7 +1493,7 @@ DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			|||||||
db_update_sqn(dbc, id, 543) --> -ENOENT
 | 
					db_update_sqn(dbc, id, 543) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
 | 
					DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
 | 
					db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
 | 
				
			||||||
DAUC IMSI='123456789000000': No 2G Auth Data
 | 
					DAUC IMSI='123456789000000': No 2G Auth Data
 | 
				
			||||||
DAUC IMSI='123456789000000': No 3G Auth Data
 | 
					DAUC IMSI='123456789000000': No 3G Auth Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ AM_CFLAGS = \
 | 
				
			|||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_LDFLAGS = \
 | 
					AM_LDFLAGS = \
 | 
				
			||||||
 | 
						-no-install \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ AM_CFLAGS = \
 | 
				
			|||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_LDFLAGS = \
 | 
					AM_LDFLAGS = \
 | 
				
			||||||
 | 
						-no-install \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = \
 | 
					EXTRA_DIST = \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,13 @@ OsmoHLR> list
 | 
				
			|||||||
  show talloc-context (application|all) (full|brief|DEPTH)
 | 
					  show talloc-context (application|all) (full|brief|DEPTH)
 | 
				
			||||||
  show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS
 | 
					  show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS
 | 
				
			||||||
  show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
 | 
					  show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
 | 
				
			||||||
 | 
					  show stats
 | 
				
			||||||
 | 
					  show stats level (global|peer|subscriber)
 | 
				
			||||||
 | 
					  show asciidoc counters
 | 
				
			||||||
 | 
					  show rate-counters
 | 
				
			||||||
  show gsup-connections
 | 
					  show gsup-connections
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT show
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT show
 | 
				
			||||||
  show subscriber (imsi|msisdn|id) IDENT
 | 
					  show subscriber (imsi|msisdn|id|imei) IDENT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR> enable
 | 
					OsmoHLR> enable
 | 
				
			||||||
OsmoHLR# list
 | 
					OsmoHLR# list
 | 
				
			||||||
@@ -71,6 +75,7 @@ OsmoHLR(config-hlr)# list
 | 
				
			|||||||
  exit
 | 
					  exit
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  gsup
 | 
					  gsup
 | 
				
			||||||
 | 
					  database PATH
 | 
				
			||||||
  euse NAME
 | 
					  euse NAME
 | 
				
			||||||
  no euse NAME
 | 
					  no euse NAME
 | 
				
			||||||
  ussd route prefix PREFIX internal (own-msisdn|own-imsi)
 | 
					  ussd route prefix PREFIX internal (own-msisdn|own-imsi)
 | 
				
			||||||
@@ -79,6 +84,10 @@ OsmoHLR(config-hlr)# list
 | 
				
			|||||||
  ussd default-route external EUSE
 | 
					  ussd default-route external EUSE
 | 
				
			||||||
  no ussd default-route
 | 
					  no ussd default-route
 | 
				
			||||||
  ncss-guard-timeout <0-255>
 | 
					  ncss-guard-timeout <0-255>
 | 
				
			||||||
 | 
					  store-imei
 | 
				
			||||||
 | 
					  no store-imei
 | 
				
			||||||
 | 
					  subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)
 | 
				
			||||||
 | 
					  no subscriber-create-on-demand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR(config-hlr)# gsup
 | 
					OsmoHLR(config-hlr)# gsup
 | 
				
			||||||
OsmoHLR(config-hlr-gsup)# list
 | 
					OsmoHLR(config-hlr-gsup)# list
 | 
				
			||||||
@@ -98,6 +107,7 @@ OsmoHLR(config-hlr)# exit
 | 
				
			|||||||
OsmoHLR(config)# exit
 | 
					OsmoHLR(config)# exit
 | 
				
			||||||
OsmoHLR# configure terminal
 | 
					OsmoHLR# configure terminal
 | 
				
			||||||
OsmoHLR(config)# hlr
 | 
					OsmoHLR(config)# hlr
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# store-imei
 | 
				
			||||||
OsmoHLR(config-hlr)# gsup
 | 
					OsmoHLR(config-hlr)# gsup
 | 
				
			||||||
OsmoHLR(config-hlr-gsup)# end
 | 
					OsmoHLR(config-hlr-gsup)# end
 | 
				
			||||||
OsmoHLR# disable
 | 
					OsmoHLR# disable
 | 
				
			||||||
@@ -116,6 +126,8 @@ log stderr
 | 
				
			|||||||
 logging level ss info
 | 
					 logging level ss info
 | 
				
			||||||
...
 | 
					...
 | 
				
			||||||
hlr
 | 
					hlr
 | 
				
			||||||
 | 
					 store-imei
 | 
				
			||||||
 | 
					 database hlr_vty_test.db
 | 
				
			||||||
 gsup
 | 
					 gsup
 | 
				
			||||||
  bind ip 127.0.0.1
 | 
					  bind ip 127.0.0.1
 | 
				
			||||||
 ussd route prefix *#100# internal own-msisdn
 | 
					 ussd route prefix *#100# internal own-msisdn
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										43
									
								
								tests/test_subscr_create_on_demand.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/test_subscr_create_on_demand.vty
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					OsmoHLR> enable
 | 
				
			||||||
 | 
					OsmoHLR# configure terminal
 | 
				
			||||||
 | 
					OsmoHLR(config)# hlr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# subscriber-create-on-demand no-msisdn none
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# show running-config
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					 subscriber-create-on-demand no-msisdn none
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# subscriber-create-on-demand 3 none
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# show running-config
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 3 none
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# subscriber-create-on-demand 4 cs
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# show running-config
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 4 cs
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# subscriber-create-on-demand 5 ps
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# show running-config
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 5 ps
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# subscriber-create-on-demand 6 cs+ps
 | 
				
			||||||
 | 
					OsmoHLR(config-hlr)# show running-config
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					hlr
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					 subscriber-create-on-demand 6 cs+ps
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
@@ -2,15 +2,17 @@ OsmoHLR> enable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# list
 | 
					OsmoHLR# list
 | 
				
			||||||
...
 | 
					...
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT show
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT show
 | 
				
			||||||
  show subscriber (imsi|msisdn|id) IDENT
 | 
					  show subscriber (imsi|msisdn|id|imei) IDENT
 | 
				
			||||||
  subscriber imsi IDENT create
 | 
					  subscriber imsi IDENT create
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT delete
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT delete
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT update msisdn (none|MSISDN)
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update msisdn (none|MSISDN)
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT update aud2g none
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update aud2g none
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT update aud3g none
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update aud3g none
 | 
				
			||||||
  subscriber (imsi|msisdn|id) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]
 | 
				
			||||||
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update imei (none|IMEI)
 | 
				
			||||||
 | 
					  subscriber (imsi|msisdn|id|imei) IDENT update network-access-mode (none|cs|ps|cs+ps)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber?
 | 
					OsmoHLR# subscriber?
 | 
				
			||||||
  subscriber  Subscriber management commands
 | 
					  subscriber  Subscriber management commands
 | 
				
			||||||
@@ -19,27 +21,36 @@ OsmoHLR# subscriber ?
 | 
				
			|||||||
  imsi    Identify subscriber by IMSI
 | 
					  imsi    Identify subscriber by IMSI
 | 
				
			||||||
  msisdn  Identify subscriber by MSISDN (phone number)
 | 
					  msisdn  Identify subscriber by MSISDN (phone number)
 | 
				
			||||||
  id      Identify subscriber by database ID
 | 
					  id      Identify subscriber by database ID
 | 
				
			||||||
 | 
					  imei    Identify subscriber by IMEI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi ?
 | 
					OsmoHLR# subscriber imsi ?
 | 
				
			||||||
  IDENT  IMSI/MSISDN/ID of the subscriber
 | 
					  IDENT  IMSI/MSISDN/ID/IMEI of the subscriber
 | 
				
			||||||
OsmoHLR# subscriber msisdn ?
 | 
					OsmoHLR# subscriber msisdn ?
 | 
				
			||||||
  IDENT  IMSI/MSISDN/ID of the subscriber
 | 
					  IDENT  IMSI/MSISDN/ID/IMEI of the subscriber
 | 
				
			||||||
OsmoHLR# subscriber id ?
 | 
					OsmoHLR# subscriber id ?
 | 
				
			||||||
  IDENT  IMSI/MSISDN/ID of the subscriber
 | 
					  IDENT  IMSI/MSISDN/ID/IMEI of the subscriber
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imei ?
 | 
				
			||||||
 | 
					  IDENT  IMSI/MSISDN/ID/IMEI of the subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
% No subscriber for imsi = '123456789023000'
 | 
					% No subscriber for imsi = '123456789023000'
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
% No subscriber for id = '1'
 | 
					% No subscriber for id = '101'
 | 
				
			||||||
OsmoHLR# subscriber msisdn 12345 show
 | 
					OsmoHLR# subscriber msisdn 12345 show
 | 
				
			||||||
% No subscriber for msisdn = '12345'
 | 
					% No subscriber for msisdn = '12345'
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imei 357613004448485 show
 | 
				
			||||||
 | 
					% Checksum validated and stripped for search: imei = '35761300444848'
 | 
				
			||||||
 | 
					% No subscriber for imei = '35761300444848'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# show subscriber imsi 123456789023000
 | 
					OsmoHLR# show subscriber imsi 123456789023000
 | 
				
			||||||
% No subscriber for imsi = '123456789023000'
 | 
					% No subscriber for imsi = '123456789023000'
 | 
				
			||||||
OsmoHLR# show subscriber id 1
 | 
					OsmoHLR# show subscriber id 101
 | 
				
			||||||
% No subscriber for id = '1'
 | 
					% No subscriber for id = '101'
 | 
				
			||||||
OsmoHLR# show subscriber msisdn 12345
 | 
					OsmoHLR# show subscriber msisdn 12345
 | 
				
			||||||
% No subscriber for msisdn = '12345'
 | 
					% No subscriber for msisdn = '12345'
 | 
				
			||||||
 | 
					OsmoHLR# show subscriber imei 357613004448485
 | 
				
			||||||
 | 
					% Checksum validated and stripped for search: imei = '35761300444848'
 | 
				
			||||||
 | 
					% No subscriber for imei = '35761300444848'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 1234567890230001 create
 | 
					OsmoHLR# subscriber imsi 1234567890230001 create
 | 
				
			||||||
% Not a valid IMSI: 1234567890230001
 | 
					% Not a valid IMSI: 1234567890230001
 | 
				
			||||||
@@ -50,16 +61,16 @@ OsmoHLR# subscriber imsi 12345 create
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 create
 | 
					OsmoHLR# subscriber imsi 123456789023000 create
 | 
				
			||||||
% Created subscriber 123456789023000
 | 
					% Created subscriber 123456789023000
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: none
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: none
 | 
					    MSISDN: none
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: none
 | 
					    MSISDN: none
 | 
				
			||||||
OsmoHLR# subscriber msisdn 12345 show
 | 
					OsmoHLR# subscriber msisdn 12345 show
 | 
				
			||||||
@@ -69,15 +80,15 @@ OsmoHLR# subscriber imsi 123456789023000 update msisdn 12345
 | 
				
			|||||||
% Updated subscriber IMSI='123456789023000' to MSISDN='12345'
 | 
					% Updated subscriber IMSI='123456789023000' to MSISDN='12345'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 12345
 | 
					    MSISDN: 12345
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 12345
 | 
					    MSISDN: 12345
 | 
				
			||||||
OsmoHLR# subscriber msisdn 12345 show
 | 
					OsmoHLR# subscriber msisdn 12345 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 12345
 | 
					    MSISDN: 12345
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -91,33 +102,35 @@ OsmoHLR# subscriber msisdn 423 update msisdn none
 | 
				
			|||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
% No subscriber for msisdn = '423'
 | 
					% No subscriber for msisdn = '423'
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: none
 | 
					    MSISDN: none
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
 | 
					OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
 | 
				
			||||||
% Updated subscriber IMSI='123456789023000' to MSISDN='423'
 | 
					% Updated subscriber IMSI='123456789023000' to MSISDN='423'
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update ?
 | 
					OsmoHLR# subscriber imsi 123456789023000 update ?
 | 
				
			||||||
  msisdn  Set MSISDN (phone number) of the subscriber
 | 
					  msisdn               Set MSISDN (phone number) of the subscriber
 | 
				
			||||||
  aud2g   Set 2G authentication data
 | 
					  aud2g                Set 2G authentication data
 | 
				
			||||||
  aud3g   Set UMTS authentication data (3G, and 2G with UMTS AKA)
 | 
					  aud3g                Set UMTS authentication data (3G, and 2G with UMTS AKA)
 | 
				
			||||||
 | 
					  imei                 Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)
 | 
				
			||||||
 | 
					  network-access-mode  Set Network Access Mode (NAM) of the subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update msisdn ?
 | 
					OsmoHLR# subscriber imsi 123456789023000 update msisdn ?
 | 
				
			||||||
  none    Remove MSISDN (phone number)
 | 
					  none    Remove MSISDN (phone number)
 | 
				
			||||||
@@ -141,7 +154,7 @@ OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki val ?
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud2g xor ki Deaf0ff1ceD0d0DabbedD1ced1ceF00d
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud2g xor ki Deaf0ff1ceD0d0DabbedD1ced1ceF00d
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: XOR
 | 
					    2G auth: XOR
 | 
				
			||||||
@@ -149,39 +162,39 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v1
 | 
					    2G auth: COMP128v1
 | 
				
			||||||
             KI=beefedcafefaceacedaddeddecadefee
 | 
					             KI=beefedcafefaceacedaddeddecadefee
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v1
 | 
					    2G auth: COMP128v1
 | 
				
			||||||
             KI=beefedcafefaceacedaddeddecadefee
 | 
					             KI=beefedcafefaceacedaddeddecadefee
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v1
 | 
					    2G auth: COMP128v1
 | 
				
			||||||
             KI=beefedcafefaceacedaddeddecadefee
 | 
					             KI=beefedcafefaceacedaddeddecadefee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
 | 
					OsmoHLR# subscriber id 101 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v2
 | 
					    2G auth: COMP128v2
 | 
				
			||||||
             KI=cededeffacedacefacedbadfadedbeef
 | 
					             KI=cededeffacedacefacedbadfadedbeef
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v2
 | 
					    2G auth: COMP128v2
 | 
				
			||||||
             KI=cededeffacedacefacedbadfadedbeef
 | 
					             KI=cededeffacedacefacedbadfadedbeef
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v2
 | 
					    2G auth: COMP128v2
 | 
				
			||||||
@@ -189,63 +202,63 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki C01ffedC1cadaeAc1d1f1edAcac1aB0a
 | 
					OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki C01ffedC1cadaeAc1d1f1edAcac1aB0a
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g nonsense ki BeefedCafeFaceAcedAddedDecadeFee
 | 
					OsmoHLR# subscriber id 101 update aud2g nonsense ki BeefedCafeFaceAcedAddedDecadeFee
 | 
				
			||||||
% Unknown command.
 | 
					% Unknown command.
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g milenage ki BeefedCafeFaceAcedAddedDecadeFee
 | 
					OsmoHLR# subscriber id 101 update aud2g milenage ki BeefedCafeFaceAcedAddedDecadeFee
 | 
				
			||||||
% Unknown command.
 | 
					% Unknown command.
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g xor ki CoiffedCicadaeAcidifiedAcaciaBoa
 | 
					OsmoHLR# subscriber id 101 update aud2g xor ki CoiffedCicadaeAcidifiedAcaciaBoa
 | 
				
			||||||
% Invalid value for KI: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
					% Invalid value for KI: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g xor ki C01ffedC1cadaeAc1d1f1edAcac1aB0aX
 | 
					OsmoHLR# subscriber id 101 update aud2g xor ki C01ffedC1cadaeAc1d1f1edAcac1aB0aX
 | 
				
			||||||
% Invalid value for KI: 'C01ffedC1cadaeAc1d1f1edAcac1aB0aX'
 | 
					% Invalid value for KI: 'C01ffedC1cadaeAc1d1f1edAcac1aB0aX'
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v3
 | 
					    2G auth: COMP128v3
 | 
				
			||||||
             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             KI=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g none
 | 
					OsmoHLR# subscriber id 101 update aud2g none
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -275,7 +288,7 @@ OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0D
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -286,7 +299,7 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op DeafBeddedBabeAcceededFadedDecaf
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op DeafBeddedBabeAcceededFadedDecaf
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -296,13 +309,13 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g none
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g none
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ind-bitlen 23
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ind-bitlen 23
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -313,7 +326,7 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k CoiffedCicadaeAcidifiedAcaciaBoa opc CededEffacedAceFacedBadFadedBeef
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k CoiffedCicadaeAcidifiedAcaciaBoa opc CededEffacedAceFacedBadFadedBeef
 | 
				
			||||||
% Invalid value for K: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
					% Invalid value for K: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -324,7 +337,7 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CoiffedCicadaeAcidifiedAcaciaBoa
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CoiffedCicadaeAcidifiedAcaciaBoa
 | 
				
			||||||
% Invalid value for OPC: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
					% Invalid value for OPC: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -336,7 +349,7 @@ OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0D
 | 
				
			|||||||
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op CoiffedCicadaeAcidifiedAcaciaBoa
 | 
					OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op CoiffedCicadaeAcidifiedAcaciaBoa
 | 
				
			||||||
% Invalid value for OP: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
					% Invalid value for OP: 'CoiffedCicadaeAcidifiedAcaciaBoa'
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    3G auth: MILENAGE
 | 
					    3G auth: MILENAGE
 | 
				
			||||||
@@ -344,9 +357,9 @@ OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			|||||||
             OP=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
					             OP=c01ffedc1cadaeac1d1f1edacac1ab0a
 | 
				
			||||||
             IND-bitlen=5
 | 
					             IND-bitlen=5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber id 1 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
 | 
					OsmoHLR# subscriber id 101 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: 423
 | 
					    MSISDN: 423
 | 
				
			||||||
    2G auth: COMP128v2
 | 
					    2G auth: COMP128v2
 | 
				
			||||||
@@ -361,16 +374,100 @@ OsmoHLR# subscriber imsi 123456789023000 delete
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 show
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
% No subscriber for imsi = '123456789023000'
 | 
					% No subscriber for imsi = '123456789023000'
 | 
				
			||||||
OsmoHLR# subscriber id 1 show
 | 
					OsmoHLR# subscriber id 101 show
 | 
				
			||||||
% No subscriber for id = '1'
 | 
					% No subscriber for id = '101'
 | 
				
			||||||
OsmoHLR# subscriber msisdn 423 show
 | 
					OsmoHLR# subscriber msisdn 423 show
 | 
				
			||||||
% No subscriber for msisdn = '423'
 | 
					% No subscriber for msisdn = '423'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 create
 | 
					OsmoHLR# subscriber imsi 123456789023000 create
 | 
				
			||||||
% Created subscriber 123456789023000
 | 
					% Created subscriber 123456789023000
 | 
				
			||||||
    ID: 1
 | 
					    ID: 101
 | 
				
			||||||
    IMSI: 123456789023000
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
    MSISDN: none
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OsmoHLR# subscriber imsi 123456789023000 delete
 | 
					OsmoHLR# subscriber imsi 123456789023000 delete
 | 
				
			||||||
% Deleted subscriber for IMSI '123456789023000'
 | 
					% Deleted subscriber for IMSI '123456789023000'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 create
 | 
				
			||||||
 | 
					% Created subscriber 123456789023000
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update imei ?
 | 
				
			||||||
 | 
					  none  Forget IMEI
 | 
				
			||||||
 | 
					  IMEI  Set IMEI (use for debug only!)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update imei 35761300444848
 | 
				
			||||||
 | 
					% Updated subscriber IMSI='123456789023000' to IMEI='35761300444848'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update imei 357613004448484
 | 
				
			||||||
 | 
					% IMEI invalid: '357613004448484'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update imei 357613004448485
 | 
				
			||||||
 | 
					% Updated subscriber IMSI='123456789023000' to IMEI='35761300444848'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# show subscriber imei 35761300444848
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    IMEI: 357613004448485
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# show subscriber imei 357613004448485
 | 
				
			||||||
 | 
					% Checksum validated and stripped for search: imei = '35761300444848'
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    IMEI: 357613004448485
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# show subscriber imei 357613004448484
 | 
				
			||||||
 | 
					% No subscriber for imei = '357613004448484'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update imei none
 | 
				
			||||||
 | 
					% Updated subscriber IMSI='123456789023000': removed IMEI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 delete
 | 
				
			||||||
 | 
					% Deleted subscriber for IMSI '123456789023000'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# show subscriber id 99
 | 
				
			||||||
 | 
					    ID: 99
 | 
				
			||||||
 | 
					    IMSI: 000000000000099
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    IMEI: 12345 (INVALID LENGTH!)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 create
 | 
				
			||||||
 | 
					% Created subscriber 123456789023000
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update network-access-mode none
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    CS disabled
 | 
				
			||||||
 | 
					    PS disabled
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update network-access-mode cs
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    PS disabled
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update network-access-mode ps
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					    CS disabled
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 update network-access-mode cs+ps
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 show
 | 
				
			||||||
 | 
					    ID: 101
 | 
				
			||||||
 | 
					    IMSI: 123456789023000
 | 
				
			||||||
 | 
					    MSISDN: none
 | 
				
			||||||
 | 
					OsmoHLR# subscriber imsi 123456789023000 delete
 | 
				
			||||||
 | 
					% Deleted subscriber for IMSI '123456789023000'
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								tests/test_subscriber.vty.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/test_subscriber.vty.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					-- Subscriber with invalid IMEI length
 | 
				
			||||||
 | 
					INSERT INTO subscriber (id, imsi, imei) VALUES(99, '000000000000099', '12345');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Dummy entry with ID=100 gives all subscribers created in the VTY test an
 | 
				
			||||||
 | 
					-- ID > 100, so we can pre-fill the database with IDs < 100.
 | 
				
			||||||
 | 
					INSERT INTO subscriber (id, imsi) VALUES(100, '000000000000100');
 | 
				
			||||||
		Reference in New Issue
	
	Block a user