mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-10-31 04:03:53 +00:00 
			
		
		
		
	Compare commits
	
		
			316 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | ||
|  | df8d454919 | ||
|  | 9ea9bbbc7f | ||
|  | 5c14c9ccca | ||
|  | 705b61bcb7 | ||
|  | 638ba8cc04 | ||
|  | 55f5efa568 | ||
|  | e6ce52bbde | ||
|  | d157a56361 | ||
|  | 9c8806acf5 | ||
|  | 4b8be4d12d | ||
|  | bc9bead62a | ||
|  | 4655e6f1fe | ||
|  | 8f3a7cce80 | ||
|  | a820ea1f67 | ||
|  | 8aa780bf80 | ||
|  | f08da2459b | ||
|  | 62ce834fbf | ||
|  | bf6b4eb0b9 | ||
|  | 79efdf3474 | ||
|  | b41394a700 | ||
|  | 0c331abdbc | ||
|  | 25e716c849 | ||
|  | 92e49ef363 | ||
|  | 95380ab037 | ||
|  | 849bfd0bef | ||
|  | 7e2d3c7f4c | ||
|  | 8f725ae655 | ||
|  | 1ed4bb4ff1 | ||
|  | 25dd785157 | ||
|  | 4f5f6f83f3 | ||
|  | e66e525e09 | ||
|  | 4a4bdcdf97 | ||
|  | cb364bb429 | ||
|  | d646207553 | ||
|  | 6cee799d5e | ||
|  | c88bdab96d | ||
|  | 607ce5ca93 | ||
|  | ccdb970c57 | ||
|  | a5b36a0904 | ||
|  | 13000d8d14 | ||
|  | 7ebfd065da | ||
|  | 966fcb2ca8 | ||
|  | 6fe1c2220a | ||
|  | 0da9f2f19c | ||
|  | 1eb9869d81 | ||
|  | 3adb33de93 | ||
|  | 791ea72ee4 | ||
|  | 9f7b69a618 | ||
|  | e6c839ed2d | ||
|  | b93c44f32e | ||
|  | a05efe8803 | ||
|  | 764514198b | ||
|  | 5198609a5e | ||
|  | 7c2f430fc5 | ||
|  | 633fddebcd | ||
|  | 7c5e930aa8 | ||
|  | 83df349045 | ||
|  | 05fe0233d2 | ||
|  | 2781bb767e | ||
|  | f473c7b23c | ||
|  | dab544e14b | ||
|  | 7d29d59292 | ||
|  | 55d32a1e3c | ||
|  | 95b96d4245 | ||
|  | 9b6bc9e479 | ||
|  | 7f32f5f3e6 | ||
|  | 7266731eca | ||
|  | 97bfb65eeb | ||
|  | bb77939a86 | ||
|  | 4956ae1f70 | ||
|  | d5807b8c87 | ||
|  | 21c14fc7f4 | ||
|  | 050eb1d803 | ||
|  | dc17e05e28 | ||
|  | 953d27ce8f | ||
|  | ec6915a771 | ||
|  | 9fdb854174 | ||
|  | 4793a7efc3 | ||
|  | 527d934807 | ||
|  | 6b274b95fc | ||
|  | edca4f88a6 | ||
|  | b9c1028cb0 | ||
|  | 1442e3a3d3 | ||
|  | 0b8f054b5f | ||
|  | fa7ee333f7 | ||
|  | 8fbf82b83f | ||
|  | bd72f1331d | ||
|  | 32acace879 | ||
|  | b85f60477f | ||
|  | a1d3b048fb | ||
|  | f83432c25c | ||
|  | 78f4301025 | ||
|  | 1b8a1dc00a | ||
|  | 9d307ec7ae | ||
|  | 5aeb438194 | ||
|  | cb360f06c8 | ||
|  | 1cb489231a | ||
|  | 93c5b10310 | ||
|  | ec9440f1bc | ||
|  | 51530311a8 | ||
|  | f162252a08 | ||
|  | 8f8401453c | ||
|  | 70e7f21cb1 | ||
|  | 2e78858756 | ||
|  | 3f2a9a2ab1 | ||
|  | 880a34d2ef | ||
|  | 7ee6e554af | ||
|  | 9214c6c7ef | ||
|  | 79fc6984ac | ||
|  | ba1605afdc | ||
|  | b6265d1a55 | ||
|  | 6c84da5942 | ||
|  | 84c2f43d00 | ||
|  | 63f68ccc4c | ||
|  | b5d77012d1 | ||
|  | fc96f688d4 | ||
|  | 43bd6069e8 | ||
|  | 2dee60ef44 | ||
|  | ab4d509a83 | ||
|  | bd1dca0859 | ||
|  | 33eeeef9dc | ||
|  | 33cbde9ced | ||
|  | 671db90ac3 | ||
|  | d3814b936b | ||
|  | 6f3e8d6297 | ||
|  | db5dae6c99 | ||
|  | 38f08770a3 | ||
|  | aee7be901b | ||
|  | 8db490695d | ||
|  | c82e6ad190 | ||
|  | 0959e8b354 | ||
|  | 4f3841c153 | ||
|  | bd0d5bf5d8 | ||
|  | 87a04b6b95 | ||
|  | 85e8a64bb4 | ||
|  | 0dcbd47a1e | ||
|  | 71b5f5b923 | ||
|  | 73d14af278 | ||
|  | 6eb231eccc | ||
|  | dbced93b5f | ||
|  | 88c91f6fb2 | ||
|  | 7750d2cedc | ||
|  | cd7fa4502c | ||
|  | 99a14c8ca1 | ||
|  | c6a6d26f50 | ||
|  | 3f697cdc71 | ||
|  | 446eb0f1bc | ||
|  | 234f9cb701 | ||
|  | 16140f70c5 | ||
|  | 36bec87104 | ||
|  | 00b1d43435 | ||
|  | 7ae8d878cf | ||
|  | 68f87915e4 | ||
|  | e86437cae4 | ||
|  | 200f56e995 | ||
|  | 50e4de7e49 | ||
|  | 86d09ec266 | ||
|  | 183e7009af | ||
|  | b6837e36a3 | ||
|  | 2e86ab3a87 | ||
|  | c5122f2829 | ||
|  | 1cbdb70b27 | ||
|  | 76328e57d1 | ||
|  | 57a8792f23 | ||
|  | 1332a17a3d | ||
|  | e50121ec96 | ||
|  | dd783056f7 | ||
|  | e8ccd5013a | ||
|  | 9c2bbc840f | ||
|  | 32633e2b89 | ||
|  | d7d9697d85 | ||
|  | 9850946013 | ||
|  | f7c3e6e3a2 | ||
|  | 28da26ec19 | ||
|  | cd83b8a44c | ||
|  | d3cd102505 | ||
|  | d4bb51ba1f | ||
|  | 1e31d18822 | ||
|  | e9c0c5b272 | ||
|  | 3522819d8b | ||
|  | 40aa61ccf0 | ||
|  | 0cac0a067e | ||
|  | f31445915e | ||
|  | 518335e688 | ||
|  | 4bde949b34 | ||
|  | 32c38f09e5 | ||
|  | f88c914efd | ||
|  | f95ce04cbd | ||
|  | 05c8b465ab | ||
|  | 43bf7bc5c5 | ||
|  | 0b1b6b1f1e | ||
|  | 84201d3a4b | ||
|  | 62491379f1 | ||
|  | 02098d9d22 | ||
|  | 886ecef1c0 | ||
|  | 1f3a1ce1a3 | ||
|  | 69f3860d28 | ||
|  | ce9bc40846 | ||
|  | 1790c8246a | ||
|  | 63b7e86dcf | ||
|  | 0ad929b418 | ||
|  | 6fb234c251 | ||
|  | 743cf42ac5 | ||
|  | 9d27398e5b | ||
|  | edebc22989 | ||
|  | cab2fcd5b5 | ||
|  | ee392bb3b1 | ||
|  | ea1052d300 | ||
|  | d846ae8978 | ||
|  | 7f39468c75 | ||
|  | e9d37db7f2 | ||
|  | 5ecdc56ad4 | ||
|  | 9cacb6f74b | ||
|  | 372868baa3 | ||
|  | adc6648841 | ||
|  | d4bebbd855 | ||
|  | 4436dececd | ||
|  | 21380ae55e | ||
|  | b5b11e31fb | ||
|  | 8d97d34f58 | ||
|  | 428c9478cd | ||
|  | 3aa3c103c2 | ||
|  | 569d322597 | ||
|  | ec9036bdd2 | ||
|  | 3ce3686768 | ||
|  | 58d4a84a31 | ||
|  | 00b3715723 | ||
|  | ea8b0d46eb | ||
|  | aa0fefd5d6 | ||
|  | 27c6b9016a | ||
|  | f8c7b6f3c9 | ||
|  | 912a303fec | ||
|  | cc785f0c43 | ||
|  | 6b883f7848 | ||
|  | 8cde66242a | ||
|  | 00c069726e | ||
|  | 8089d51f74 | ||
|  | d71dbec7b6 | ||
|  | 862f1dc4fd | ||
|  | 24537b95bd | ||
|  | 7685a78757 | ||
|  | 7f9491fe5f | ||
|  | ca43e30be3 | ||
|  | 5b581ac6eb | ||
|  | 2116ce2471 | ||
|  | 0acd31e9a6 | ||
|  | 4307ad94b6 | ||
|  | 627de84abe | ||
|  | 2fc63a6e84 | ||
|  | 21229a06be | ||
|  | 54db5e712d | ||
|  | c317cc20c8 | ||
|  | cb2a63406e | ||
|  | 3e6a69d2ab | ||
|  | ec1b959496 | ||
|  | 6eed322063 | ||
|  | 40d8b01dea | 
							
								
								
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,63 @@ | ||||
| *.o | ||||
| *.lo | ||||
| *.la | ||||
| *.db | ||||
| src/hlr | ||||
| *.db-shm | ||||
| *.db-wal | ||||
| *.pyc | ||||
| .*.sw? | ||||
| .version | ||||
| .tarball-version | ||||
| Makefile | ||||
| Makefile.in | ||||
| aclocal.m4 | ||||
| autom4te.cache | ||||
| compile | ||||
| config.guess | ||||
| config.log | ||||
| config.status | ||||
| config.sub | ||||
| configure | ||||
| depcomp | ||||
| install-sh | ||||
| libtool | ||||
| ltmain.sh | ||||
| m4 | ||||
| *.m4 | ||||
| missing | ||||
| .deps | ||||
|  | ||||
| *.pc | ||||
| .libs | ||||
|  | ||||
| src/db_test | ||||
| src/db_bootstrap.h | ||||
| src/osmo-hlr | ||||
| src/osmo-hlr-db-tool | ||||
| src/osmo-euse-demo | ||||
| src/gsupclient/gsup-test-client | ||||
|  | ||||
| tests/atconfig | ||||
| tests/testsuite | ||||
| tests/testsuite.log | ||||
| tests/testsuite.dir | ||||
|  | ||||
| tests/auc/auc_3g_test | ||||
| tests/auc/auc_ts_55_205_test_sets.c | ||||
| tests/auc/auc_ts_55_205_test_sets | ||||
| tests/auc/auc_test | ||||
| tests/gsup_server/gsup_server_test | ||||
| tests/gsup/gsup_test | ||||
| tests/db/db_test | ||||
| tests/hlr_vty_test.db* | ||||
|  | ||||
| # manuals | ||||
| doc/manuals/*.html | ||||
| doc/manuals/*.svg | ||||
| doc/manuals/*.pdf | ||||
| doc/manuals/*__*.png | ||||
| doc/manuals/*.check | ||||
| doc/manuals/generated/ | ||||
| doc/manuals/osmomsc-usermanual.xml | ||||
| doc/manuals/common | ||||
| doc/manuals/build | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| [gerrit] | ||||
| host=gerrit.osmocom.org | ||||
| project=osmo-hlr | ||||
							
								
								
									
										28
									
								
								Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| AUTOMAKE_OPTIONS = foreign dist-bzip2 | ||||
|  | ||||
| SUBDIRS = \ | ||||
| 	doc \ | ||||
| 	src \ | ||||
| 	include \ | ||||
| 	sql \ | ||||
| 	contrib \ | ||||
| 	tests \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	.version \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_DISTCHECK_CONFIGURE_FLAGS = \ | ||||
| 	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) | ||||
|  | ||||
| pkgconfigdir = $(libdir)/pkgconfig | ||||
| pkgconfig_DATA = libosmo-gsup-client.pc | ||||
|  | ||||
| @RELMAKE@ | ||||
|  | ||||
| BUILT_SOURCES = $(top_srcdir)/.version | ||||
| $(top_srcdir)/.version: | ||||
| 	echo $(VERSION) > $@-t && mv $@-t $@ | ||||
| dist-hook: | ||||
| 	echo $(VERSION) > $(distdir)/.tarball-version | ||||
							
								
								
									
										189
									
								
								configure.ac
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								configure.ac
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| AC_INIT([osmo-hlr], | ||||
| 	m4_esyscmd([./git-version-gen .tarball-version]), | ||||
| 	[openbsc@lists.osmocom.org]) | ||||
|  | ||||
| dnl *This* is the root dir, even if an install-sh exists in ../ or ../../ | ||||
| AC_CONFIG_AUX_DIR([.]) | ||||
|  | ||||
| dnl libtool init | ||||
| LT_INIT | ||||
|  | ||||
| AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9]) | ||||
|  | ||||
| AC_CONFIG_TESTDIR(tests) | ||||
|  | ||||
| dnl kernel style compile messages | ||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||||
|  | ||||
| dnl include release helper | ||||
| RELMAKE='-include osmo-release.mk' | ||||
| AC_SUBST([RELMAKE]) | ||||
|  | ||||
| dnl checks for programs | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_MKDIR_P | ||||
| AC_PROG_CC | ||||
| AC_PROG_INSTALL | ||||
|  | ||||
| dnl check for pkg-config (explained in detail in libosmocore/configure.ac) | ||||
| AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) | ||||
| if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | ||||
|         AC_MSG_WARN([You need to install pkg-config]) | ||||
| fi | ||||
| PKG_PROG_PKG_CONFIG([0.20]) | ||||
|  | ||||
| PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1]) | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0) | ||||
|  | ||||
| PKG_CHECK_MODULES(SQLITE3, sqlite3) | ||||
|  | ||||
| AC_CONFIG_MACRO_DIR([m4]) | ||||
|  | ||||
| dnl checks for header files | ||||
| AC_HEADER_STDC | ||||
|  | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-sanitize], | ||||
| 		[Compile with address sanitizer enabled], | ||||
| 	)], | ||||
| 	[sanitize=$enableval], [sanitize="no"]) | ||||
| if test x"$sanitize" = x"yes" | ||||
| then | ||||
| 	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| 	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| 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, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-werror], | ||||
| 		[Turn all compiler warnings into errors, with exceptions: | ||||
| 		 a) deprecation (allow upstream to mark deprecation without breaking builds); | ||||
| 		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) | ||||
| 		] | ||||
| 	)], | ||||
| 	[werror=$enableval], [werror="no"]) | ||||
| if test x"$werror" = x"yes" | ||||
| then | ||||
| 	WERROR_FLAGS="-Werror" | ||||
| 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||
| 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||
| 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||
| 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE([external_tests], | ||||
| 		AC_HELP_STRING([--enable-external-tests], | ||||
| 				[Include the VTY/CTRL tests in make check [default=no]]), | ||||
| 		[enable_ext_tests="$enableval"],[enable_ext_tests="no"]) | ||||
| if test "x$enable_ext_tests" = "xyes" ; then | ||||
| 	AM_PATH_PYTHON | ||||
| 	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes) | ||||
| 	 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then | ||||
| 		AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.]) | ||||
| 	fi | ||||
| fi | ||||
| AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) | ||||
| AC_MSG_RESULT([$enable_ext_tests]) | ||||
| AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") | ||||
|  | ||||
| # Generate manuals | ||||
| AC_ARG_ENABLE(manuals, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-manuals], | ||||
| 		[Generate manual PDFs [default=no]], | ||||
| 	)], | ||||
| 	[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"]) | ||||
| AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"]) | ||||
| AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals" | ||||
| 	fallback]) | ||||
| if test x"$osmo_ac_build_manuals" = x"yes" | ||||
| then | ||||
| 	# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback) | ||||
| 	if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)" | ||||
| 	else | ||||
| 		OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)" | ||||
| 		if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)" | ||||
| 		else | ||||
| 			OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals" | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)" | ||||
| 		fi | ||||
| 	fi | ||||
| 	if ! test -d "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.") | ||||
| 	fi | ||||
|  | ||||
| 	# Find and run check-depends | ||||
| 	CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh" | ||||
| 	if ! test -x "$CHECK_DEPENDS"; then | ||||
| 		CHECK_DEPENDS="osmo-gsm-manuals-check-depends" | ||||
| 	fi | ||||
| 	if ! $CHECK_DEPENDS; then | ||||
| 		AC_MSG_ERROR("missing dependencies for --enable-manuals") | ||||
| 	fi | ||||
|  | ||||
| 	# Put in Makefile with absolute path | ||||
| 	OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")" | ||||
| 	AC_SUBST([OSMO_GSM_MANUALS_DIR]) | ||||
| fi | ||||
|  | ||||
| # https://www.freedesktop.org/software/systemd/man/daemon.html | ||||
| AC_ARG_WITH([systemdsystemunitdir], | ||||
|      [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, | ||||
|      [with_systemdsystemunitdir=auto]) | ||||
| AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ | ||||
|      def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) | ||||
|  | ||||
|      AS_IF([test "x$def_systemdsystemunitdir" = "x"], | ||||
|    [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], | ||||
|     [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) | ||||
|     with_systemdsystemunitdir=no], | ||||
|    [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) | ||||
| AS_IF([test "x$with_systemdsystemunitdir" != "xno"], | ||||
|       [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) | ||||
| AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) | ||||
|  | ||||
| AC_MSG_RESULT([CFLAGS="$CFLAGS"]) | ||||
| AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) | ||||
|  | ||||
| AC_OUTPUT( | ||||
| 	Makefile | ||||
| 	doc/Makefile | ||||
| 	doc/examples/Makefile | ||||
| 	src/Makefile | ||||
| 	src/gsupclient/Makefile | ||||
| 	include/Makefile | ||||
| 	libosmo-gsup-client.pc | ||||
| 	sql/Makefile | ||||
| 	doc/manuals/Makefile | ||||
| 	contrib/Makefile | ||||
| 	contrib/systemd/Makefile | ||||
| 	tests/Makefile | ||||
| 	tests/auc/Makefile | ||||
| 	tests/auc/gen_ts_55_205_test_sets/Makefile | ||||
| 	tests/gsup_server/Makefile | ||||
| 	tests/gsup/Makefile | ||||
| 	tests/db/Makefile | ||||
| 	) | ||||
							
								
								
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| SUBDIRS = systemd | ||||
							
								
								
									
										62
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| #!/bin/sh | ||||
| # jenkins build helper script for osmo-hlr.  This is how we build on jenkins.osmocom.org | ||||
| # | ||||
| # environment variables: | ||||
| # * WITH_MANUALS: build manual PDFs if set to "1" | ||||
| # * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1") | ||||
| # | ||||
|  | ||||
| if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then | ||||
| 	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" | ||||
| 	exit 2 | ||||
| fi | ||||
|  | ||||
|  | ||||
| set -ex | ||||
|  | ||||
| base="$PWD" | ||||
| deps="$base/deps" | ||||
| inst="$deps/install" | ||||
| export deps inst | ||||
|  | ||||
| osmo-clean-workspace.sh | ||||
|  | ||||
| mkdir "$deps" || true | ||||
|  | ||||
| verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") | ||||
|  | ||||
| export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" | ||||
| export LD_LIBRARY_PATH="$inst/lib" | ||||
| export PATH="$inst/bin:$PATH" | ||||
|  | ||||
| osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false | ||||
| osmo-build-dep.sh libosmo-abis | ||||
|  | ||||
| # Additional configure options and depends | ||||
| CONFIG="" | ||||
| if [ "$WITH_MANUALS" = "1" ]; then | ||||
| 	osmo-build-dep.sh osmo-gsm-manuals | ||||
| 	CONFIG="--enable-manuals" | ||||
| fi | ||||
|  | ||||
| set +x | ||||
| echo | ||||
| echo | ||||
| echo | ||||
| echo " =============================== osmo-hlr ===============================" | ||||
| echo | ||||
| set -x | ||||
|  | ||||
| cd "$base" | ||||
| autoreconf --install --force | ||||
| ./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG | ||||
| $MAKE $PARALLEL_MAKE | ||||
| $MAKE check || cat-testlogs.sh | ||||
| DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh | ||||
|  | ||||
| if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then | ||||
| 	make -C "$base/doc/manuals" publish | ||||
| fi | ||||
|  | ||||
| $MAKE maintainer-clean | ||||
| osmo-clean-workspace.sh | ||||
							
								
								
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| EXTRA_DIST = osmo-hlr.service | ||||
|  | ||||
| if HAVE_SYSTEMD | ||||
| systemdsystemunit_DATA = \ | ||||
|   osmo-hlr.service | ||||
| endif | ||||
							
								
								
									
										12
									
								
								contrib/systemd/osmo-hlr.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contrib/systemd/osmo-hlr.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| [Unit] | ||||
| Description=Osmocom Home Location Register (OsmoHLR) | ||||
| Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										345
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,345 @@ | ||||
| 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 | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * fix luop crash: use buffer for APN that remains valid | ||||
|   * add gsup_test to catch OS#3231 | ||||
|   * add error handling to osmo_gsup_configure_wildcard_apn() | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 04 May 2018 18:41:35 +0200 | ||||
|  | ||||
| osmo-hlr (0.2.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * vty: skip installing cmds now always installed by default | ||||
|   * hlr_db_tool: fix error log strerror invocation | ||||
|   * cosmetic: add comment on ignored return value | ||||
|   * db-tool: add command 'create' | ||||
|   * db-tool: cosmetic: tweak printf output | ||||
|   * db-tool: error-exit on too many arguments | ||||
|   * add --enable-sanitize config option | ||||
|   * db_test: don't verify SQLite issued error messages, they might change | ||||
|   * cosmetic: rx_send_auth_info(): decide error cause with switch() | ||||
|   * return GMM_CAUSE_IMSI_UNKNOWN if there is no auth data | ||||
|   * db_get_auth_data / db_get_auc: clarify return values | ||||
|   * osmo-hlr: log details for unknown IMSI / no auth data / db error | ||||
|   * db_test: also test db_get_auc() return values | ||||
|   * fix test_subscriber_errors.ctrl after libosmocore change | ||||
|   * fix debug log: put 'deriving 2G from 3G' in proper place | ||||
|   * ctrl test: fix: adjust expectations after stricter ctrl parsing | ||||
|   * fix build: db_test: missing LIBOSMOABIS_CFLAGS and _LIBS | ||||
|   * configure: add --enable-werror | ||||
|   * jenkins.sh: use --enable-werror configure flag, not CFLAGS | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * hlr.c: Avoid overflow of lu_operation.subscr.imsi | ||||
|   * Fix expected test output after new 'logging print file 1' vty command | ||||
|   * osmo-hlr: Add talloc context introspection via VTY | ||||
|   * vty: Don't print error if removing auth data while none present | ||||
|   * Fix responses to PURGE MS | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * debian: include systemd service osmo-hlr.service | ||||
|   * doc: install example .cfg files to $(docdir)/examples/ | ||||
|   * debian: install osmo-hlr.cfg to /etc/osmocom | ||||
|  | ||||
|   [ Max ] | ||||
|   * Remove unused check | ||||
|   * Remove unused ipa.py | ||||
|   * Enable sanitize for CI tests | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * luop.c: Transform FIXME from warning to pragma message | ||||
|   * contrib:jenkins.sh: Enable Werror | ||||
|   * use osmo_init_logging2 | ||||
|   * Remove unused src/db_test.c | ||||
|  | ||||
|   [ Alexander Huemer ] | ||||
|   * Add missing build products in .gitignore | ||||
|  | ||||
|   [ Stefan Sperling ] | ||||
|   * more robust usage of osmo_timer API for osmo-hlr luop timer | ||||
|   * notify GSUP clients when HLR subscriber information changes | ||||
|   * rewrite subscriber_update_notify() without calls into luop | ||||
|   * don't forget to mark luop as a packet switched connection | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 16:27:13 +0200 | ||||
|  | ||||
| osmo-hlr (0.1.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * build with autoconf/automake, add jenkins.sh script | ||||
|   * fix build on FreeBSD: eliminate implicitly declared functions | ||||
|   * fix various compiler warnings | ||||
|   * fix DLGSUP logging cat after change in libosmocore | ||||
|   * build: recoin db_test as non-installable program | ||||
|   * build: actually make sqlite mandatory | ||||
|   * bump required libosmocore version to 0.9.5 | ||||
|   * gsup: send subscriber MSISDN | ||||
|   * debug log: log computed vector kinds | ||||
|   * log: move a log from info to debug level | ||||
|   * hlr.sql: typo in comment | ||||
|   * auc.c: typo in comment | ||||
|   * main: add and use root talloc ctx | ||||
|   * main: add option parsing with db file and default options | ||||
|   * main: add VTY and '-c config-file' option | ||||
|   * sql: fix 3g_auc's column K data type | ||||
|   * cosmetic: sql: indicate VARCHAR size of key columns as 32 | ||||
|   * sql: auc_3g: set sqn NOT NULL DEFAULT 0 | ||||
|   * comment: sql: describe auc_2g and auc_3g columns | ||||
|   * Add test suite skeleton with empty test (auc_3g_test) | ||||
|   * tests: auc_3g_test: implement vector generation test | ||||
|   * auth: verify test sets from 3GPP TS 55.205 | ||||
|   * sql: add unique constraints to IMSI and MSISDN | ||||
|   * UMTS AKA resync: fix argument ordering | ||||
|   * auc_3g_test: add AUTS resync test | ||||
|   * auc_gen_vectors(): ensure sane arguments, test | ||||
|   * auc_3g_test: allow to inc fake rand bytes upon rand request | ||||
|   * auc_3g_test: add AUTS test with N vectors, to show bug | ||||
|   * cosmetic: refactor auc_compute_vectors(), add debug log | ||||
|   * auc_compute_vectors(): fix AUTS resync for multiple vectors | ||||
|   * cosmetic: auc_3g_test: improve test debugging tools | ||||
|   * cosmetic: rename auc_3g_test.c to auc_test.c | ||||
|   * fix: properly handle error rc by osmo_gsup_conn_ccm_get() | ||||
|   * auc tests: adjust cosmetically to prepare for SQN changes | ||||
|   * auc tests: fix after SQN scheme changes from libosmocore | ||||
|   * fix debug log: adjust to new SQN increment scheme | ||||
|   * UMTS AKA: implement SQN increment according to SEQ and IND | ||||
|   * debug log: output ind slot, previous sqn, and sqn db update | ||||
|   * jenkins: add value_string termination check | ||||
|   * fix db_subscr_ps error handling | ||||
|   * add config example (mostly empty) | ||||
|   * install hlr.sql in prefix/doc/osmo-hlr/ | ||||
|   * use OSMO_GSUP_PORT == 4222 instead of hardcoded 2222 | ||||
|   * add basic CTRL interface tests | ||||
|   * add CTRL tests for enable-/disable-/status-ps | ||||
|   * cosmetic: prepend DB_STMT_ to enum stmt_idx entries | ||||
|   * cosmetic: rename db_subscr_get() to db_subscr_get_by_imsi() | ||||
|   * cosmetic: refactor db_bind_imsi() as db_bind_text() | ||||
|   * cosmetic: multi-line DB_STMT_AUC_BY_IMSI | ||||
|   * cosmetic: log IMSI='<imsi>', log "no such subscriber" | ||||
|   * cosmetic: log: "SQLite" with capital L | ||||
|   * cosmetic: db_hlr: SL3_TXT: clarify indenting | ||||
|   * ctrl_test_runner.py: use proper constant as test db path | ||||
|   * gitignore: tests/package.m4 | ||||
|   * cosmetic: don't log about missing SQLite log cb | ||||
|   * add db_bind_int() and db_bind_int64() | ||||
|   * add db_subscr_create(), db_subscr_delete(), db_subscr_update_msisdn_by_imsi() | ||||
|   * add initial db_test: creating and deleting subscribers | ||||
|   * less noise: simplify db_remove_reset() | ||||
|   * db: use int64_t as subscriber id | ||||
|   * add db_subscr_get_by_msisdn() and db_subscr_get_by_id() | ||||
|   * refactor db_subscr_ps() to db_subscr_nam() | ||||
|   * refactor db_subscr_lu() | ||||
|   * refactor db_subscr_purge | ||||
|   * add db_subscr_update_aud_by_id(), complete db_subscr_delete_by_id() | ||||
|   * refactor db_get_auth_data return val | ||||
|   * code undup: use db_remove_reset() in db_auc.c | ||||
|   * fix db_update_sqn(): reset stmt in all error cases | ||||
|   * code undup: use db_bind_text() in db_get_auth_data() | ||||
|   * debian: 'make check' needs sqlite3, add to Build-Depends | ||||
|   * fix db_subscr_get_by_*(): clear output data; test in db_test.c | ||||
|   * implement subscriber vty interface, tests | ||||
|   * add test_nodes.vty | ||||
|   * replace ctrl_test_runner.py with transcript test_subscriber.ctrl | ||||
|   * add lu_op_free(), use in luop.c | ||||
|   * luop: fix mem leak upon error in lu_op_alloc_conn() | ||||
|   * fix mem leak in handle_cmd_ps(): free luop | ||||
|   * api doc: say that lu_op_tx_del_subscr_data() doesn't free | ||||
|   * add hlr_subsrc_nam to put GSUP client notification in proper API | ||||
|   * vty: fix output of empty IMSI | ||||
|   * db api: fix/add API docs | ||||
|   * cosmetic: tweak params of hlr_controlif_setup() | ||||
|   * ctrl: completely replace all CTRL commands | ||||
|   * test_subscriber.ctrl: test against octal/hex interpretation of id | ||||
|   * jenkins: use osmo-clean-workspace.sh before and after build | ||||
|   * tests/Makefile: use test db var instead of repeating the path | ||||
|   * db_test: fix *FLAGS | ||||
|   * automatically create db tables on osmo-hlr invocation | ||||
|   * cosmetic: sql/hlr.sql: move comments | ||||
|   * cosmetic: rename SL3_TXT macro, use osmo_strlcpy() | ||||
|   * fix default logging levels to NOTICE, not DEBUG | ||||
|   * add osmo-hlr-db-tool, program to migrate from osmo-nitb db | ||||
|  | ||||
|   [ Max ] | ||||
|   * Add gerrit settings | ||||
|   * Add hardcoded APN | ||||
|   * Log error cause as a string | ||||
|   * Move GSUP msg init into separate function | ||||
|   * Use strings for GSUP message type | ||||
|   * Move lu_operation into separate file | ||||
|   * db: move duplicated code into helper functions | ||||
|   * Fix compiler's warning about printf security | ||||
|   * Add routines to update nam_ps | ||||
|   * Add global HLR struct | ||||
|   * Make subscr parameter to db_subscr_get() optional | ||||
|   * Add CTRL interface | ||||
|   * CTRL: add enable/disable packet service cmds | ||||
|   * Add .deb packaging | ||||
|   * deb: fix OBS build | ||||
|   * debian: remove obsolete dependency | ||||
|   * Attempt to fix .deb package | ||||
|   * deb: use python in shebang | ||||
|   * Another attempt at fixing .deb | ||||
|   * Use release helper from libosmocore | ||||
|   * Use value string check from osmo-ci | ||||
|  | ||||
|   [ Daniel Willmann ] | ||||
|   * Add systemd service file | ||||
|   * hlr_data.sql: Insert ki and opc instead of op to example data | ||||
|   * tests/auc: Don't require python3 | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * VTY: Add hlr node and bind ip field | ||||
|   * debian: remove unneeded dependency libdbd-sqlite3 | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * jenkins.sh: Proper error message if local environment isn't set up | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * debian/rules: show testsuite.log when tests are failing | ||||
|  | ||||
|  -- Harald Welte <laforge@gnumonks.org>  Sat, 28 Oct 2017 20:37:33 +0200 | ||||
|  | ||||
| osmo-hlr (0.0.1) UNRELEASED; urgency=low | ||||
|  | ||||
|      * Initial release (Closes: OS#1948) | ||||
|  | ||||
|  -- Max Suraev <msuraev@sysmocom.de>  Mon, 13 Mar 2017 16:26:41 +0200 | ||||
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 9 | ||||
							
								
								
									
										69
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| Source: osmo-hlr | ||||
| Section: net | ||||
| Priority: optional | ||||
| Maintainer: Max Suraev <msuraev@sysmocom.de> | ||||
| Build-Depends: debhelper (>= 9), | ||||
|                pkg-config, | ||||
|                dh-autoreconf, | ||||
|                dh-systemd (>= 1.5), | ||||
|                autotools-dev, | ||||
|                python-minimal, | ||||
|                libosmocore-dev, | ||||
|                libosmo-abis-dev, | ||||
|                libosmo-netif-dev, | ||||
|                libsqlite3-dev, | ||||
|                sqlite3, | ||||
|                osmo-gsm-manuals-dev | ||||
| Standards-Version: 3.9.6 | ||||
| Vcs-Browser: http://cgit.osmocom.org/osmo-hlr | ||||
| Vcs-Git: git://git.osmocom.org/osmo-hlr | ||||
| Homepage: https://projects.osmocom.org/projects/osmo-hlr | ||||
|  | ||||
| Package: osmo-hlr | ||||
| Architecture: any | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3 | ||||
| Description: Osmocom Home Location Register | ||||
|  OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which works over GSUP | ||||
|  protocol. The subscribers are store in sqlite DB. It supports both 2G and 3G authentication. | ||||
|  | ||||
| Package: osmo-hlr-dbg | ||||
| Architecture: any | ||||
| Section: debug | ||||
| Priority: extra | ||||
| Depends: osmo-hlr (= ${binary:Version}), ${misc:Depends} | ||||
| Description: Debug symbols for the osmo-hlr | ||||
|  Make debugging possible | ||||
|  | ||||
| Package: libosmo-gsup-client0 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: ${shlibs:Depends}, | ||||
| 	 ${misc:Depends} | ||||
| Pre-Depends: ${misc:Pre-Depends} | ||||
| Description: Osmocom GSUP (General Subscriber Update Protocol) client library | ||||
|   This is a shared library that can be used to implement client programs for | ||||
|   the GSUP protocol.  The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN | ||||
|   and External USSD Entities (EUSEs) using this library to implement clients. | ||||
|  | ||||
| Package: libosmo-gsup-client-dev | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: ${misc:Depends}, | ||||
| 	 libosmo-gsup-client0 (= ${binary:Version}), | ||||
| 	 libosmocore-dev | ||||
| Description: Development headers of Osmocom GSUP client library | ||||
|   This is a shared library that can be used to implement client programs for | ||||
|   the GSUP protocol.  The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN | ||||
|   and External USSD Entities (EUSEs) using this library to implement clients. | ||||
|   . | ||||
|   This 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. | ||||
							
								
								
									
										21
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | ||||
| Upstream-Name: OsmoHLR | ||||
| Source: http://cgit.osmocom.org/osmo-hlr/ | ||||
|  | ||||
| Files: * | ||||
| Copyright: 2016-2017 Sysmocom s. f. m. c. GmbH <info@sysmocom.de> | ||||
| License: AGPL-3+ | ||||
|  | ||||
| License: AGPL-3+ | ||||
|  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/>. | ||||
							
								
								
									
										5
									
								
								debian/libosmo-gsup-client-dev.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								debian/libosmo-gsup-client-dev.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| usr/include/osmocom/gsupclient | ||||
| usr/lib/*/libosmo-gsup-client*.a | ||||
| usr/lib/*/libosmo-gsup-client*.so | ||||
| usr/lib/*/libosmo-gsup-client*.la | ||||
| usr/lib/*/pkgconfig/libosmo-gsup-client.pc | ||||
							
								
								
									
										1
									
								
								debian/libosmo-gsup-client0.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-gsup-client0.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| usr/lib/*/libosmo-gsup-client*.so.* | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										8
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| /etc/osmocom/osmo-hlr.cfg | ||||
| /lib/systemd/system/osmo-hlr.service | ||||
| /usr/bin/osmo-hlr | ||||
| /usr/bin/osmo-hlr-db-tool | ||||
| /usr/share/doc/osmo-hlr/sql/hlr.sql | ||||
| /usr/share/doc/osmo-hlr/sql/hlr_data.sql | ||||
| /usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg | ||||
| /var/lib/osmocom | ||||
							
								
								
									
										24
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										24
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #!/usr/bin/make -f | ||||
|  | ||||
| #export DH_VERBOSE=1 | ||||
| export DEB_BUILD_MAINT_OPTIONS = hardening=+all | ||||
|  | ||||
| %: | ||||
| 	dh $@ --with autoreconf | ||||
|  | ||||
| override_dh_shlibdeps: | ||||
| 	dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info | ||||
|  | ||||
| override_dh_strip: | ||||
| 	dh_strip --dbg-package=osmo-hlr-dbg | ||||
|  | ||||
| # Print test results in case of a failure | ||||
| override_dh_auto_test: | ||||
| 	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) | ||||
|  | ||||
| override_dh_auto_configure: | ||||
| 	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 | ||||
							
								
								
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 3.0 (native) | ||||
							
								
								
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| SUBDIRS = \ | ||||
| 	examples \ | ||||
| 	manuals \ | ||||
| 	$(NULL) | ||||
							
								
								
									
										27
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| osmoconfdir = $(sysconfdir)/osmocom | ||||
| osmoconf_DATA = osmo-hlr.cfg | ||||
|  | ||||
| EXTRA_DIST = osmo-hlr.cfg | ||||
|  | ||||
| CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' | ||||
|  | ||||
| dist-hook: | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(distdir)/$$f" && \ | ||||
| 		mkdir -p "$$(dirname $$j)" && \ | ||||
| 		$(INSTALL_DATA) $(srcdir)/$$f $$j; \ | ||||
| 	done | ||||
|  | ||||
| install-data-hook: | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(DESTDIR)$(docdir)/examples/$$f" && \ | ||||
| 		mkdir -p "$$(dirname $$j)" && \ | ||||
| 		$(INSTALL_DATA) $(srcdir)/$$f $$j; \ | ||||
| 	done | ||||
|  | ||||
| uninstall-hook: | ||||
| 	@$(PRE_UNINSTALL) | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(DESTDIR)$(docdir)/examples/$$f" && \ | ||||
| 		$(RM) $$j; \ | ||||
| 	done | ||||
							
								
								
									
										26
									
								
								doc/examples/osmo-hlr.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								doc/examples/osmo-hlr.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| ! | ||||
| ! OsmoHLR example configuration | ||||
| ! | ||||
| log stderr | ||||
|  logging filter all 1 | ||||
|  logging color 1 | ||||
|  logging print category 1 | ||||
|  logging print category-hex 0 | ||||
|  logging print level 1 | ||||
|  logging print file basename last | ||||
|  logging print extended-timestamp 1 | ||||
|  logging level main notice | ||||
|  logging level db notice | ||||
|  logging level auc notice | ||||
|  logging level ss info | ||||
|  logging level linp error | ||||
| ! | ||||
| line vty | ||||
|  bind 127.0.0.1 | ||||
| ctrl | ||||
|  bind 127.0.0.1 | ||||
| hlr | ||||
|  gsup | ||||
|   bind ip 127.0.0.1 | ||||
|  ussd route prefix *#100# internal own-msisdn | ||||
|  ussd route prefix *#101# internal own-imsi | ||||
							
								
								
									
										61
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| EXTRA_DIST = example_subscriber_add_update_delete.vty \ | ||||
|     example_subscriber_cs_ps_enabled.ctrl \ | ||||
|     example_subscriber_info.ctrl \ | ||||
|     osmohlr-usermanual.adoc \ | ||||
|     osmohlr-usermanual-docinfo.xml \ | ||||
|     osmohlr-vty-reference.xml \ | ||||
|     regen_doc.sh \ | ||||
|     chapters \ | ||||
|     vty | ||||
|  | ||||
| if BUILD_MANUALS | ||||
|   ASCIIDOC = osmohlr-usermanual.adoc | ||||
|   ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc | ||||
|  | ||||
|   VTY_REFERENCE = osmohlr-vty-reference.xml | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc | ||||
|  | ||||
|   OSMO_REPOSITORY = osmo-hlr | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc | ||||
| endif | ||||
|  | ||||
| TMP_DB = generated/hlr.db | ||||
|  | ||||
| update-examples: update-examples-ctrl update-examples-vty | ||||
|  | ||||
| .PHONY: found-update-deps | ||||
| found-update-deps: | ||||
| 	@if [ ! -f "$(top_srcdir)/sql/hlr.sql" ]; then \ | ||||
| 		echo "You need to define OSMO_HLR_PATH to point at an osmo-hlr.git"; \ | ||||
| 		exit 1; \ | ||||
| 	fi | ||||
| 	@if [ -z "$(shell which osmo-hlr)" ]; then \ | ||||
| 		echo "osmo-hlr needs to be installed / available in the PATH"; \ | ||||
| 		exit 1; \ | ||||
| 	fi | ||||
| 	@if [ -z "$(shell which osmo_verify_transcript_ctrl.py)" ]; then \ | ||||
| 		echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \ | ||||
| 		exit 1; \ | ||||
| 	fi | ||||
| 	@if [ -z "$(shell which osmo_verify_transcript_vty.py)" ]; then \ | ||||
| 		echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \ | ||||
| 		exit 1; \ | ||||
| 	fi | ||||
|  | ||||
| update-examples-ctrl: found-update-deps | ||||
| 	mkdir -p generated | ||||
| 	rm -f "$(TMP_DB)" | ||||
| 	sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql" | ||||
| 	sqlite3 "$(TMP_DB)" < "$(top_srcdir)/tests/test_subscriber.sql" | ||||
| 	osmo_verify_transcript_ctrl.py \ | ||||
| 		-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \ | ||||
| 		-p 4259 --update *.ctrl | ||||
|  | ||||
| update-examples-vty: found-update-deps | ||||
| 	mkdir -p generated | ||||
| 	rm -f "$(TMP_DB)" | ||||
| 	sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql" | ||||
| 	osmo_verify_transcript_vty.py \ | ||||
| 		-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \ | ||||
| 		-p 4258 --update *.vty | ||||
							
								
								
									
										106
									
								
								doc/manuals/chapters/control.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								doc/manuals/chapters/control.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| [[hlr-ctrl]] | ||||
| == Control interface | ||||
|  | ||||
| The actual protocol is described in <<common-control-if>>, the variables common | ||||
| to all programs using it are described in <<ctrl_common_vars>>. This section | ||||
| describes the CTRL interface variables specific to OsmoHLR. | ||||
|  | ||||
| All subscriber variables are available by different selectors, which are freely | ||||
| interchangeable: | ||||
|  | ||||
| .Subscriber selectors available on OsmoHLR's Control interface | ||||
| [options="header",width="100%",cols="35%,65%"] | ||||
| |=== | ||||
| |Selector|Comment | ||||
| |subscriber.*by-imsi-*'123456'.*|Subscriber selector by IMSI, replace "123456" with the actual IMSI | ||||
| |subscriber.*by-msisdn-*'123456'.*|Subscriber selector by MSISDN | ||||
| |subscriber.*by-id-*'123456'.*|Subscriber selector by database ID | ||||
| |=== | ||||
|  | ||||
| Each of the above selectors feature all of these control variables: | ||||
|  | ||||
| .Subscriber variables available on OsmoHLR's Control interface | ||||
| [options="header",width="100%",cols="35%,8%,8%,8%,41%"] | ||||
| |=== | ||||
| |Name|Access|Trap|Value|Comment | ||||
| |subscriber.by-\*.*info*|R|No||List (short) subscriber information | ||||
| |subscriber.by-\*.*info-aud*|R|No||List subscriber authentication tokens | ||||
| |subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one | ||||
| |subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access | ||||
| |subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access | ||||
| |=== | ||||
|  | ||||
| === subscriber.by-*.info, info-aud, info-all | ||||
|  | ||||
| Query the HLR database and return current subscriber record, in multiple lines | ||||
| of the format | ||||
|  | ||||
| ---- | ||||
| name<tab>value | ||||
| ---- | ||||
|  | ||||
| To keep the reply as short as possible, some values are omitted if they are | ||||
| empty. These are the returned values and their presence | ||||
| modalities; for their meaning, see <<subscriber-params>>: | ||||
|  | ||||
| .Returned values by OsmoHLR's 'info', 'info-all' and 'info-aud' commands | ||||
| [options="header",width="100%",cols="15%,15%,30%,40%"] | ||||
| |=== | ||||
| |Returned by 'info-all' and|Name|Format|Presence | ||||
| |'info'|id|-9223372036854775808 .. 9223372036854775807 (usually not negative)|always | ||||
| |'info'|imsi|6 to 15 decimal digits|always | ||||
| |'info'|msisdn|1 to 15 decimal digits|when non-empty | ||||
| |'info'|nam_cs|'1' if CS is enabled, or '0'|always | ||||
| |'info'|nam_ps|'1' if PS is enabled, or '0'|always | ||||
| |'info'|vlr_number|up to 15 decimal digits|when non-empty | ||||
| |'info'|sgsn_number|up to 15 decimal digits|when non-empty | ||||
| |'info'|sgsn_address||when non-empty | ||||
| |'info'|ms_purged_cs|'1' if CS is purged, or '0'|always | ||||
| |'info'|ms_purged_ps|'1' if PS is purged, or '0'|always | ||||
| |'info'|periodic_lu_timer|0..4294967295|always | ||||
| |'info'|periodic_rau_tau_timer|0..4294967295|always | ||||
| |'info'|lmsi|8 hex digits|always | ||||
| |'info-aud'|aud2g.algo|one of 'comp128v1', 'comp128v2', 'comp128v3' or 'xor'|when valid 2G auth data is set | ||||
| |'info-aud'|aud2g.ki|32 hexadecimal digits|when valid 2G auth data is set | ||||
| |'info-aud'|aud3g.algo|so far always 'milenage'|when valid 3G auth data is set | ||||
| |'info-aud'|aud3g.k|32 hexadecimal digits|when valid 3G auth data is set | ||||
| |'info-aud'|aud3g.op|32 hexadecimal digits|when valid 3G auth data is set, *not* when OPC is set | ||||
| |'info-aud'|aud3g.opc|32 hexadecimal digits|when valid 3G auth data is set, *not* when OP is set | ||||
| |'info-aud'|aud3g.ind_bitlen|0..28|when valid 3G auth data is set | ||||
| |'info-aud'|aud3g.sqn|0 .. 18446744073709551615|when valid 3G auth data is set | ||||
| |=== | ||||
|  | ||||
| This is an example Control Interface transcript that illustrates the various | ||||
| 'info' commands: | ||||
|  | ||||
| ---- | ||||
| include::../example_subscriber_info.ctrl[] | ||||
| ---- | ||||
|  | ||||
| === subscriber.by-*.ps-enabled, cs-enabled | ||||
|  | ||||
| Disable or enable packet-/circuit-switched access for the given IMSI; | ||||
|  | ||||
| * 'ps-enabled' switches access to GPRS or UMTS data services, | ||||
| * 'cs-enabled' switches access to voice services. | ||||
|  | ||||
| When disabled, the next time this subscriber attempts to do a Location Updating | ||||
| GSUP operation for the given domain (i.e. from the SGSN for 'ps-enabled', from | ||||
| the MSC/VLR for 'cs-enabled'), it will be rejected by OsmoHLR. Currently | ||||
| connected GSUP clients will be notified via GSUP when a subscriber is being | ||||
| disabled, so that the subscriber can be dropped in case it is currently | ||||
| attached. | ||||
|  | ||||
| The current 'ps-enabled'/'cs-enabled' status can be queried by 'GET' commands, | ||||
| and also by looking at 'nam_ps' and 'nam_cs' in a 'subscriber.by-*.info' | ||||
| response. | ||||
|  | ||||
| A value of "1" indicates that the given domain is enabled, which is the | ||||
| default; a value of "0" disables access. | ||||
|  | ||||
| This is an example transcript that illustrates 'ps-enabled' and 'cs-enabled' | ||||
| commands: | ||||
|  | ||||
| ---- | ||||
| include::../example_subscriber_cs_ps_enabled.ctrl[] | ||||
| ---- | ||||
							
								
								
									
										69
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| [[overview]] | ||||
| == Overview | ||||
|  | ||||
| This manual should help you getting started with OsmoHLR. It will cover | ||||
| aspects of configuring and running the OsmoHLR. | ||||
|  | ||||
| [[intro_overview]] | ||||
| === About OsmoHLR | ||||
|  | ||||
| OsmoHLR is Osmocom's minimal implementation of a Home Location Register (HLR) | ||||
| for 2G and 3G GSM and UMTS mobile core networks. Its interfaces are: | ||||
|  | ||||
| - GSUP, serving towards OsmoMSC and OsmoSGSN; | ||||
| - A local SQLite database; | ||||
| - The Osmocom typical telnet VTY and CTRL interfaces. | ||||
|  | ||||
| Originally, the OpenBSC project's OsmoNITB all-in-one implementation had an | ||||
| integrated HLR, managing subscribers and SMS in the same local database. Along | ||||
| with the separate OsmoMSC and its new VLR component, OsmoHLR was implemented | ||||
| from scratch to alleviate various shortcomings of the internal HLR: | ||||
|  | ||||
| - The separate HLR allows using centralized subscriber management for both | ||||
|   circuit-switched and packet-switched domains (i.e. one OsmoHLR for both | ||||
|   OsmoMSC and OsmoSGSN). | ||||
|  | ||||
| - VLR and HLR brought full UMTS AKA (Authentication and Key Agreement) support, | ||||
|   i.e. Milenage authentication in both the full 3G variant as well as the | ||||
|   backwards compatible 2G variant. | ||||
|  | ||||
| - In contrast to the OsmoNITB, the specific way the new OsmoMSC's VLR accesses | ||||
|   OsmoHLR brings fully asynchronous subscriber database access. | ||||
|  | ||||
| Find the OsmoHLR issue tracker and wiki online at | ||||
|  | ||||
| - https://osmocom.org/projects/osmo-hlr | ||||
| - https://osmocom.org/projects/osmo-hlr/wiki | ||||
|  | ||||
|  | ||||
| [[fig-gsm]] | ||||
| .Typical GSM network architecture used with OsmoHLR | ||||
| [graphviz] | ||||
| ---- | ||||
| digraph G { | ||||
| 	rankdir=LR; | ||||
| 	subgraph cluster_hlr { | ||||
| 		label = "OsmoHLR"; | ||||
| 		GSUP [label="GSUP server"] | ||||
| 		DB [label="SQLite DB"] | ||||
| 		GSUP->DB | ||||
| 		DB->CTRL [dir="back"] | ||||
| 		DB->VTY [dir="back"] | ||||
| 	} | ||||
|  | ||||
| 	Admin [label="Admin and\nMaintenance"] | ||||
| 	SW [label="3rd party software\nintegration"] | ||||
| 	VTY->Admin [dir="back"] | ||||
| 	CTRL->SW [dir="back"] | ||||
| 		 | ||||
| 	MSC [label="MSC/VLR"] | ||||
| 	MSC->GSUP  [label="GSUP"] | ||||
| 	SGSN->GSUP [label="GSUP"] | ||||
|  | ||||
| 	BSC->MSC | ||||
| 	HNBGW->MSC | ||||
| 	HNBGW->SGSN | ||||
| 	PCU->SGSN | ||||
| } | ||||
| ---- | ||||
|  | ||||
							
								
								
									
										87
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| == Running OsmoHLR | ||||
|  | ||||
| The OsmoHLR executable (`osmo-hlr`) offers the following command-line | ||||
| arguments: | ||||
|  | ||||
| === SYNOPSIS | ||||
|  | ||||
| *osmo-hlr* [-h] [-c 'CONFIGFILE'] [-l 'DATABASE'] [-d 'DBGMASK'] [-D] [-s] [-T] [-e 'LOGLEVEL'] [-U] [-V] | ||||
|  | ||||
| === OPTIONS | ||||
|  | ||||
| // Keep the order the same as in osmo-hlr --help! | ||||
|  | ||||
| *-h, --help*:: | ||||
| 	Print a short help message about the supported options | ||||
| *-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. | ||||
| *-l, --database 'DATABASE'*:: | ||||
| 	Specify the file name of the SQLite3 database to use as HLR/AUC | ||||
| 	storage | ||||
| *-d, --debug 'DBGMASK','DBGLEVELS'*:: | ||||
| 	Set the log subsystems and levels for logging to stderr. This | ||||
| 	has mostly been superseded by VTY-based logging configuration, | ||||
| 	see <<logging>> for further information. | ||||
| *-D, --daemonize*:: | ||||
| 	Fork the process as a daemon into background. | ||||
| *-s, --disable-color*:: | ||||
| 	Disable colors for logging to stderr. This has mostly been | ||||
| 	deprecated by VTY based logging configuration, see <<logging>> | ||||
| 	for more information. | ||||
| *-T, --timestamp*:: | ||||
| 	Enable time-stamping of log messages to stderr. This has mostly | ||||
| 	been deprecated by VTY based logging configuration, see | ||||
| 	<<logging>> for more information. | ||||
| *-e, --log-level 'LOGLEVEL'*:: | ||||
| 	Set the global log level for logging to stderr. This has mostly | ||||
| 	been deprecated by VTY based logging configuration, see | ||||
| 	<<logging>> for more information. | ||||
| *-U, --db-upgrade*:: | ||||
| 	Allow HLR database schema upgrades. If OsmoHLR was updated and | ||||
| 	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 | ||||
|  | ||||
| If no database exists yet, OsmoHLR will automatically create and bootstrap a | ||||
| database file with empty tables. If no `-l` command-line option is provided, | ||||
| this database file will be created in the current working directory. | ||||
|  | ||||
| Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along | ||||
| with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data | ||||
| from an old 'OsmoNITB' database. See `osmo-hlr-db-tool --help`. | ||||
|  | ||||
| === Multiple instances | ||||
|  | ||||
| Running multiple instances of `osmo-hlr` on the same computer is possible if | ||||
| all interfaces (VTY, CTRL) are separated using the appropriate configuration | ||||
| options. The IP based interfaces are binding to local host by default. In order | ||||
| to separate the processes, the user has to bind those services to specific but | ||||
| different IP addresses and/or ports. | ||||
|  | ||||
| The VTY and the Control interface can be bound to IP addresses from the loopback | ||||
| address range, for example: | ||||
|  | ||||
| ---- | ||||
| line vty | ||||
|  bind 127.0.0.2 | ||||
| ctrl | ||||
|  bind 127.0.0.2 | ||||
| ---- | ||||
|  | ||||
| The GSUP interface can be bound to a specific IP address by the following | ||||
| configuration options: | ||||
|  | ||||
| ---- | ||||
| hlr | ||||
|  gsup | ||||
|   bind ip 10.23.42.1 | ||||
| ---- | ||||
|  | ||||
| NOTE: At the time of writing, OsmoHLR lacks a config option to change the GSUP | ||||
| port, which is by default TCP port 4222. | ||||
							
								
								
									
										129
									
								
								doc/manuals/chapters/subscribers.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								doc/manuals/chapters/subscribers.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| == Managing Subscribers | ||||
|  | ||||
| Subscribers are kept in a local SQLite database file and can be managed via VTY | ||||
| and CTRL interfaces. | ||||
|  | ||||
| This section provides some examples; also refer to the OsmoHLR VTY reference | ||||
| manual <<vty-ref-osmohlr>> as well as the Control interface described in | ||||
| <<hlr-ctrl>>. | ||||
|  | ||||
| === Example: Add/Update/Delete Subscriber via VTY | ||||
|  | ||||
| The following telnet VTY session adds a subscriber complete with GSM (2G) and | ||||
| UMTS (3G and 2G) authentication tokens, and finally removes the subscriber | ||||
| again; it assumes that osmo-hlr is running and listening for telnet VTY | ||||
| connections on localhost: | ||||
|  | ||||
| ---- | ||||
| $ telnet localhost 4258 | ||||
| include::../example_subscriber_add_update_delete.vty[] | ||||
| ---- | ||||
|  | ||||
| [[subscriber-params]] | ||||
| === Subscriber Parameters | ||||
|  | ||||
| The following parameters are managed for each subscriber of the HLR, modelled | ||||
| roughly after 3GPP TS 23.008, version 13.3.0; note that not all of these | ||||
| parameters are necessarily in active use. | ||||
|  | ||||
| The `aud3g` table also applies to 2G networks: it provides UMTS AKA tokens for | ||||
| Milenage authentication, which is available both on 3G and 2G networks. On 2G, | ||||
| when both MS and network are R99 capable (like OsmoMSC and OsmoSGSN are), the | ||||
| full UMTS AKA with Milenage keys from `aud_3g`, using AUTN and extended RES | ||||
| tokens, is available. With pre-R99 MS or network configurations, the GSM AKA | ||||
| compatible variant of Milenage, still using the Milenage keys from `aud_3g` but | ||||
| transceiving only RAND and SRES, may be applicable. (See 3GPP TS 33.102, chapter | ||||
| 6.8.1, Authentication and key agreement of UMTS subscribers.) | ||||
|  | ||||
| .OsmoHLR's subscriber parameters | ||||
| [options="header",width="100%",cols="20%,20%,60%"] | ||||
| |=== | ||||
| |Name|Example|Description | ||||
| |imsi|901700000014701|identity of the SIM/USIM, 3GPP TS 23.008 chapter 2.1.1.1 | ||||
| |msisdn|2342123|number to dial to reach this subscriber (multiple MSISDNs can be stored per subscriber), 3GPP TS 23.008 chapter 2.1.2 | ||||
| |imeisv|4234234234234275|identity of the mobile device and software version, 3GPP TS 23.008 chapter 2.2.3 | ||||
| |aud2g.algo|comp128v3|Authentication algorithm ID for GSM AKA, corresponds to enum osmo_auth_algo | ||||
| |aud2g.ki||Subscriber's secret key (128bit) | ||||
| |aud3g.algo|milenage|Authentication algorithm ID for UMTS AKA (applies to both 3G and 2G networks), corresponds to enum osmo_auth_algo | ||||
| |aud3g.k|(32 hexadecimal digits)|Subscriber's secret key (128bit) | ||||
| |aud3g.op|(32 hexadecimal digits)|Operator's secret key (128bit) | ||||
| |aud3g.opc|(32 hexadecimal digits)|Secret key derived from OP and K (128bit), alternative to using OP which does not disclose OP to subscribers | ||||
| |aud3g.sqn|123|Sequence number of last used key (64bit unsigned) | ||||
| |aud3g.ind_bitlen|5|Nr of index bits at lower SQN end | ||||
| |apn|| | ||||
| |vlr_number||3GPP TS 23.008 chapter 2.4.5 | ||||
| |hlr_number||3GPP TS 23.008 chapter 2.4.6 | ||||
| |sgsn_number||3GPP TS 23.008 chapter 2.4.8.1 | ||||
| |sgsn_address||3GPP TS 23.008 chapter 2.13.10 | ||||
| |ggsn_number||3GPP TS 23.008 chapter 2.4.8.2 | ||||
| |gmlc_number||3GPP TS 23.008 chapter 2.4.9.2 | ||||
| |smsc_number||3GPP TS 23.008 chapter 2.4.23 | ||||
| |periodic_lu_tmr||3GPP TS 23.008 chapter 2.4.24 | ||||
| |periodic_rau_tau_tmr||3GPP TS 23.008 chapter 2.13.115 | ||||
| |nam_cs|1|Enable/disable voice access (3GPP TS 23.008 chapter 2.1.1.2: network access mode) | ||||
| |nam_ps|0|Enable/disable data access (3GPP TS 23.008 chapter 2.1.1.2: network access mode) | ||||
| |lmsi||3GPP TS 23.008 chapter 2.1.8 | ||||
| |ms_purged_cs|0|3GPP TS 23.008 chapter 2.7.5 | ||||
| |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 | ||||
							
								
								
									
										78
									
								
								doc/manuals/chapters/ussd.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								doc/manuals/chapters/ussd.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| [[ussd]] | ||||
| == Unstructured Supplementary Services Data (USSD) | ||||
|  | ||||
| The _Unstructured Supplementary Services Data (USSD)_ is one service within | ||||
| 2G/3G networks next to other services such as circuit-switched voice, packet-switched | ||||
| data and SMS (Short Message Service). | ||||
|  | ||||
| It is on an abstract level quite similar to SMS in that USSD can be used to send | ||||
| textual messages.  However, there are the following differences: | ||||
|  | ||||
| * USSD is between the MS (phone) and an USSD application on the network, while | ||||
|   SMS is primarily between two subscribers identified by their MSISDN | ||||
| * USSD is faster, as it doesn't suffer from the complicated three-layer CP/RP/TP | ||||
|   protocol stack of SMS with it's acknowledgement of the acknowledged acknowledgement. | ||||
| * USSD is session-oriented, i.e. a dialogue/session between subscriber and application | ||||
|   can persist for the transfer of more than one message.  The dedicated radio channel | ||||
|   on the RAN remains established throughout that dialogue. | ||||
|  | ||||
| === USSD in Osmocom | ||||
|  | ||||
| Until August 2018, OsmoMSC contained some minimalistic internal USSD | ||||
| handling with no | ||||
| ability to attach/extend it with external USSD applications. | ||||
|  | ||||
| From August 2018 onwards, OsmoMSC doesn't contain any internal USSD | ||||
| handlers/applications anymore.  Instead, all USSD is transported to/from | ||||
| OsmoHLR via the GSUP protocol. | ||||
|  | ||||
| OsmoHLR contains some intenal USSD handlers and can route USSD messages | ||||
| to any number of external USSD entities (EUSEs).  The EUSE also use GSUP | ||||
| to communicate USSD from/to OsmoHLR. | ||||
|  | ||||
| Each EUSE is identified by its name.  The name consists of a single-word | ||||
| string preceding a currently fixed ("-00-00-00-00-00-00") suffix. | ||||
| There is no authentication between EUSE and OsmoHLR: Any client program | ||||
| able to connect to the GSUP port of OsmoHLR can register as any EUSE | ||||
| (name). | ||||
|  | ||||
| NOTE:: We plan to remove the requirement for this suffix as soon as we | ||||
| are done resolving all more important issues. | ||||
|  | ||||
| === USSD Configuration | ||||
|  | ||||
| USSD configuration in OsmoHLR happens within the `hlr` VTY node. | ||||
|  | ||||
| `euse foobar-00-00-00-00-00-00` defines an EUSE with the given name `foobar` | ||||
|  | ||||
| `ussd route prefix *123 external foobar-00-00-00-00-00-00` installs a | ||||
| prefix route to the named EUSE.  All USSD short codes starting with *123 will be | ||||
| routed to the named EUSE. | ||||
|  | ||||
| `ussd route prefix *#100# internal own-msisdn` installs a prefix route | ||||
| to the named internal USSD handler.  There above command will restore | ||||
| the old behavior, in which *#100# will return a text message containing | ||||
| the subscribers own phone number.  There is one other handler called | ||||
| `own-imsi` which will return the IMSI instead of the MSISDN. | ||||
|  | ||||
| `ussd default-route external foobar-00-00-00-00-00-00` installs a | ||||
| default route to the named EUSE.  This means that all USSD codes for | ||||
| which no more specific route exists will be routed to the named EUSE. | ||||
|  | ||||
| === Example EUSE program | ||||
|  | ||||
| We have provided an example EUSE developed in C language using existing | ||||
| Osmocom libraries for GSUP protocol handling and USSD encoding/decoding. | ||||
| It will register as `foobar` EUSE to OsmoHLR on localhost.  You can run | ||||
| it on a different machine by specifying e.g. `osmo-euse-demo 1.2.3.4 5678` | ||||
| to make it connect to OsmoHLR on IP address 1.2.3.4 and GSUP/TCP port | ||||
| 5678. | ||||
|  | ||||
| The idea is that you can use this as a template to develop your own USSD | ||||
| applications, or any gateways to other protocols or interfaces. | ||||
|  | ||||
| You can find it in `osmo-hlr/src/osmo-euse-demo.c` or online by | ||||
| following the link to http://git.osmocom.org/osmo-hlr/tree/src/osmo-euse-demo.c | ||||
|  | ||||
| This demonstration program will echo back any USSD message sent/routed | ||||
| to it, quoted like _You sent "..."_. | ||||
							
								
								
									
										34
									
								
								doc/manuals/example_subscriber_add_update_delete.vty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								doc/manuals/example_subscriber_add_update_delete.vty
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| OsmoHLR> enable | ||||
| OsmoHLR# subscriber imsi 123456789023000 create | ||||
| % Created subscriber 123456789023000 | ||||
|     ID: 1 | ||||
|     IMSI: 123456789023000 | ||||
|     MSISDN: none | ||||
|  | ||||
| OsmoHLR# subscriber imsi 123456789023000 update msisdn 423 | ||||
| % Updated subscriber IMSI='123456789023000' to MSISDN='423' | ||||
|  | ||||
| OsmoHLR# subscriber msisdn 423 update aud3g milenage k deaf0ff1ced0d0dabbedd1ced1cef00d opc cededeffacedacefacedbadfadedbeef | ||||
| OsmoHLR# subscriber msisdn 423 show | ||||
|     ID: 1 | ||||
|     IMSI: 123456789023000 | ||||
|     MSISDN: 423 | ||||
|     3G auth: MILENAGE | ||||
|              K=deaf0ff1ced0d0dabbedd1ced1cef00d | ||||
|              OPC=cededeffacedacefacedbadfadedbeef | ||||
|              IND-bitlen=5 | ||||
|  | ||||
| OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki beefedcafefaceacedaddeddecadefee | ||||
| OsmoHLR# subscriber msisdn 423 show | ||||
|     ID: 1 | ||||
|     IMSI: 123456789023000 | ||||
|     MSISDN: 423 | ||||
|     2G auth: COMP128v3 | ||||
|              KI=beefedcafefaceacedaddeddecadefee | ||||
|     3G auth: MILENAGE | ||||
|              K=deaf0ff1ced0d0dabbedd1ced1cef00d | ||||
|              OPC=cededeffacedacefacedbadfadedbeef | ||||
|              IND-bitlen=5 | ||||
|  | ||||
| OsmoHLR# subscriber imsi 123456789023000 delete | ||||
| % Deleted subscriber for IMSI '123456789023000' | ||||
							
								
								
									
										71
									
								
								doc/manuals/example_subscriber_cs_ps_enabled.ctrl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								doc/manuals/example_subscriber_cs_ps_enabled.ctrl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| GET 1 subscriber.by-msisdn-103.info | ||||
| GET_REPLY 1 subscriber.by-msisdn-103.info  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	1 | ||||
| nam_ps	1 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
|  | ||||
| GET 2 subscriber.by-msisdn-103.ps-enabled | ||||
| GET_REPLY 2 subscriber.by-msisdn-103.ps-enabled 1 | ||||
|  | ||||
| SET 3 subscriber.by-msisdn-103.ps-enabled 0 | ||||
| SET_REPLY 3 subscriber.by-msisdn-103.ps-enabled OK | ||||
|  | ||||
| GET 4 subscriber.by-msisdn-103.ps-enabled | ||||
| GET_REPLY 4 subscriber.by-msisdn-103.ps-enabled 0 | ||||
|  | ||||
| GET 5 subscriber.by-msisdn-103.info | ||||
| GET_REPLY 5 subscriber.by-msisdn-103.info  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	1 | ||||
| nam_ps	0 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
|  | ||||
| SET 6 subscriber.by-msisdn-103.cs-enabled 0 | ||||
| SET_REPLY 6 subscriber.by-msisdn-103.cs-enabled OK | ||||
|  | ||||
| GET 7 subscriber.by-msisdn-103.cs-enabled | ||||
| GET_REPLY 7 subscriber.by-msisdn-103.cs-enabled 0 | ||||
|  | ||||
| GET 8 subscriber.by-msisdn-103.info | ||||
| GET_REPLY 8 subscriber.by-msisdn-103.info  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	0 | ||||
| nam_ps	0 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
|  | ||||
| SET 9 subscriber.by-msisdn-103.cs-enabled 1 | ||||
| SET_REPLY 9 subscriber.by-msisdn-103.cs-enabled OK | ||||
| SET 10 subscriber.by-msisdn-103.ps-enabled 1 | ||||
| SET_REPLY 10 subscriber.by-msisdn-103.ps-enabled OK | ||||
|  | ||||
| GET 11 subscriber.by-msisdn-103.info | ||||
| GET_REPLY 11 subscriber.by-msisdn-103.info  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	1 | ||||
| nam_ps	1 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
							
								
								
									
										42
									
								
								doc/manuals/example_subscriber_info.ctrl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								doc/manuals/example_subscriber_info.ctrl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| GET 1 subscriber.by-imsi-901990000000003.info | ||||
| GET_REPLY 1 subscriber.by-imsi-901990000000003.info  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	1 | ||||
| nam_ps	1 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
|  | ||||
| GET 2 subscriber.by-msisdn-103.info-aud | ||||
| GET_REPLY 2 subscriber.by-msisdn-103.info-aud  | ||||
| aud2g.algo	COMP128v1 | ||||
| aud2g.ki	000102030405060708090a0b0c0d0e0f | ||||
| aud3g.algo	MILENAGE | ||||
| aud3g.k	000102030405060708090a0b0c0d0e0f | ||||
| aud3g.opc	101112131415161718191a1b1c1d1e1f | ||||
| aud3g.ind_bitlen	5 | ||||
| aud3g.sqn	0 | ||||
|  | ||||
| GET 3 subscriber.by-id-3.info-all | ||||
| GET_REPLY 3 subscriber.by-id-3.info-all  | ||||
| id	3 | ||||
| imsi	901990000000003 | ||||
| msisdn	103 | ||||
| nam_cs	1 | ||||
| nam_ps	1 | ||||
| ms_purged_cs	0 | ||||
| ms_purged_ps	0 | ||||
| periodic_lu_timer	0 | ||||
| periodic_rau_tau_timer	0 | ||||
| lmsi	00000000 | ||||
| aud2g.algo	COMP128v1 | ||||
| aud2g.ki	000102030405060708090a0b0c0d0e0f | ||||
| aud3g.algo	MILENAGE | ||||
| aud3g.k	000102030405060708090a0b0c0d0e0f | ||||
| aud3g.opc	101112131415161718191a1b1c1d1e1f | ||||
| aud3g.ind_bitlen	5 | ||||
| aud3g.sqn	0 | ||||
							
								
								
									
										47
									
								
								doc/manuals/osmohlr-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								doc/manuals/osmohlr-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <revhistory> | ||||
|   <revision> | ||||
|     <revnumber>1</revnumber> | ||||
|     <date>September 18th, 2017</date> | ||||
|     <authorinitials>NH</authorinitials> | ||||
|     <revremark> | ||||
|       Initial version; based on OsmoNITB manual version 2. | ||||
|     </revremark> | ||||
|   </revision> | ||||
| </revhistory> | ||||
|  | ||||
| <authorgroup> | ||||
|   <author> | ||||
|     <firstname>Neels</firstname> | ||||
|     <surname>Hofmeyr</surname> | ||||
|     <email>nhofmeyr@sysmocom.de</email> | ||||
|     <authorinitials>NH</authorinitials> | ||||
|     <affiliation> | ||||
|       <shortaffil>sysmocom</shortaffil> | ||||
|       <orgname>sysmocom - s.f.m.c. GmbH</orgname> | ||||
|       <jobtitle>Senior Developer</jobtitle> | ||||
|     </affiliation> | ||||
|   </author> | ||||
| </authorgroup> | ||||
|  | ||||
| <copyright> | ||||
|   <year>2017</year> | ||||
|   <holder>sysmocom - s.f.m.c. GmbH</holder> | ||||
| </copyright> | ||||
|  | ||||
| <legalnotice> | ||||
|   <para> | ||||
| 	Permission is granted to copy, distribute and/or modify this | ||||
| 	document under the terms of the GNU Free Documentation License, | ||||
| 	Version 1.3 or any later version published by the Free Software | ||||
| 	Foundation; with the Invariant Sections being just 'Foreword', | ||||
| 	'Acknowledgements' and 'Preface', with no Front-Cover Texts, | ||||
| 	and no Back-Cover Texts.  A copy of the license is included in | ||||
| 	the section entitled "GNU Free Documentation License". | ||||
|   </para> | ||||
|   <para> | ||||
| 	The Asciidoc source code of this manual can be found at | ||||
| 	<ulink url="http://git.osmocom.org/osmo-gsm-manuals/"> | ||||
| 		http://git.osmocom.org/osmo-gsm-manuals/ | ||||
| 	</ulink> | ||||
|   </para> | ||||
| </legalnotice> | ||||
							
								
								
									
										36
									
								
								doc/manuals/osmohlr-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								doc/manuals/osmohlr-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| :gfdl-enabled: | ||||
| :program-name: OsmoHLR | ||||
|  | ||||
| OsmoHLR User Manual | ||||
| ==================== | ||||
| Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  | ||||
|  | ||||
| include::./common/chapters/preface.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/overview.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/running.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/subscribers.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/ussd.adoc[] | ||||
|  | ||||
| include::./common/chapters/vty.adoc[] | ||||
|  | ||||
| include::./common/chapters/logging.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/control.adoc[] | ||||
|  | ||||
| include::./common/chapters/control_if.adoc[] | ||||
|  | ||||
| include::./common/chapters/gsup.adoc[] | ||||
|  | ||||
| include::./common/chapters/port_numbers.adoc[] | ||||
|  | ||||
| include::./common/chapters/bibliography.adoc[] | ||||
|  | ||||
| include::./common/chapters/glossary.adoc[] | ||||
|  | ||||
| include::./common/chapters/gfdl.adoc[] | ||||
|  | ||||
							
								
								
									
										38
									
								
								doc/manuals/osmohlr-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								doc/manuals/osmohlr-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ex:ts=2:sw=42sts=2:et | ||||
|   -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||||
| --> | ||||
| <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN" | ||||
| "http://docbook.org/xml/5.0/dtd/docbook.dtd" [ | ||||
| <!ENTITY chapter-vty      SYSTEM      "./common/chapters/vty.xml" > | ||||
| <!ENTITY sections-vty     SYSTEM      "generated/docbook_vty.xml"  > | ||||
| ]> | ||||
|  | ||||
| <book> | ||||
|   <info> | ||||
|     <revhistory> | ||||
|         <revision> | ||||
|             <revnumber>v1</revnumber> | ||||
|             <date>18th September 2017</date> | ||||
|             <authorinitials>nh</authorinitials> | ||||
|             <revremark>Initial</revremark> | ||||
|         </revision> | ||||
|     </revhistory> | ||||
|  | ||||
|     <title>OsmoHLR VTY Reference</title> | ||||
|  | ||||
|     <copyright> | ||||
|       <year>2017</year> | ||||
|     </copyright> | ||||
|  | ||||
|     <legalnotice> | ||||
|       <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved. | ||||
|       </para> | ||||
|     </legalnotice> | ||||
|   </info> | ||||
|  | ||||
|   <!-- Main chapters--> | ||||
|   &chapter-vty; | ||||
| </book> | ||||
|  | ||||
							
								
								
									
										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" | ||||
							
								
								
									
										2
									
								
								doc/manuals/vty/hlr_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								doc/manuals/vty/hlr_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| <vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'> | ||||
| </vtydoc> | ||||
							
								
								
									
										1431
									
								
								doc/manuals/vty/hlr_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1431
									
								
								doc/manuals/vty/hlr_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										151
									
								
								git-version-gen
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										151
									
								
								git-version-gen
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| #!/bin/sh | ||||
| # Print a version string. | ||||
| scriptversion=2010-01-28.01 | ||||
|  | ||||
| # Copyright (C) 2007-2010 Free Software Foundation, Inc. | ||||
| # | ||||
| # This program is free software: you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 3 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. | ||||
| # It may be run two ways: | ||||
| # - from a git repository in which the "git describe" command below | ||||
| #   produces useful output (thus requiring at least one signed tag) | ||||
| # - from a non-git-repo directory containing a .tarball-version file, which | ||||
| #   presumes this script is invoked like "./git-version-gen .tarball-version". | ||||
|  | ||||
| # In order to use intra-version strings in your project, you will need two | ||||
| # separate generated version string files: | ||||
| # | ||||
| # .tarball-version - present only in a distribution tarball, and not in | ||||
| #   a checked-out repository.  Created with contents that were learned at | ||||
| #   the last time autoconf was run, and used by git-version-gen.  Must not | ||||
| #   be present in either $(srcdir) or $(builddir) for git-version-gen to | ||||
| #   give accurate answers during normal development with a checked out tree, | ||||
| #   but must be present in a tarball when there is no version control system. | ||||
| #   Therefore, it cannot be used in any dependencies.  GNUmakefile has | ||||
| #   hooks to force a reconfigure at distribution time to get the value | ||||
| #   correct, without penalizing normal development with extra reconfigures. | ||||
| # | ||||
| # .version - present in a checked-out repository and in a distribution | ||||
| #   tarball.  Usable in dependencies, particularly for files that don't | ||||
| #   want to depend on config.h but do want to track version changes. | ||||
| #   Delete this file prior to any autoconf run where you want to rebuild | ||||
| #   files to pick up a version string change; and leave it stale to | ||||
| #   minimize rebuild time after unrelated changes to configure sources. | ||||
| # | ||||
| # It is probably wise to add these two files to .gitignore, so that you | ||||
| # don't accidentally commit either generated file. | ||||
| # | ||||
| # Use the following line in your configure.ac, so that $(VERSION) will | ||||
| # automatically be up-to-date each time configure is run (and note that | ||||
| # since configure.ac no longer includes a version string, Makefile rules | ||||
| # should not depend on configure.ac for version updates). | ||||
| # | ||||
| # AC_INIT([GNU project], | ||||
| #         m4_esyscmd([build-aux/git-version-gen .tarball-version]), | ||||
| #         [bug-project@example]) | ||||
| # | ||||
| # Then use the following lines in your Makefile.am, so that .version | ||||
| # will be present for dependencies, and so that .tarball-version will | ||||
| # exist in distribution tarballs. | ||||
| # | ||||
| # BUILT_SOURCES = $(top_srcdir)/.version | ||||
| # $(top_srcdir)/.version: | ||||
| #	echo $(VERSION) > $@-t && mv $@-t $@ | ||||
| # dist-hook: | ||||
| #	echo $(VERSION) > $(distdir)/.tarball-version | ||||
|  | ||||
| case $# in | ||||
|     1) ;; | ||||
|     *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; | ||||
| esac | ||||
|  | ||||
| tarball_version_file=$1 | ||||
| nl=' | ||||
| ' | ||||
|  | ||||
| # First see if there is a tarball-only version file. | ||||
| # then try "git describe", then default. | ||||
| if test -f $tarball_version_file | ||||
| then | ||||
|     v=`cat $tarball_version_file` || exit 1 | ||||
|     case $v in | ||||
| 	*$nl*) v= ;; # reject multi-line output | ||||
| 	[0-9]*) ;; | ||||
| 	*) v= ;; | ||||
|     esac | ||||
|     test -z "$v" \ | ||||
| 	&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 | ||||
| fi | ||||
|  | ||||
| if test -n "$v" | ||||
| then | ||||
|     : # use $v | ||||
| elif | ||||
|        v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ | ||||
| 	  || git describe --abbrev=4 HEAD 2>/dev/null` \ | ||||
|     && case $v in | ||||
| 	 [0-9]*) ;; | ||||
| 	 v[0-9]*) ;; | ||||
| 	 *) (exit 1) ;; | ||||
|        esac | ||||
| then | ||||
|     # Is this a new git that lists number of commits since the last | ||||
|     # tag or the previous older version that did not? | ||||
|     #   Newer: v6.10-77-g0f8faeb | ||||
|     #   Older: v6.10-g0f8faeb | ||||
|     case $v in | ||||
| 	*-*-*) : git describe is okay three part flavor ;; | ||||
| 	*-*) | ||||
| 	    : git describe is older two part flavor | ||||
| 	    # Recreate the number of commits and rewrite such that the | ||||
| 	    # result is the same as if we were using the newer version | ||||
| 	    # of git describe. | ||||
| 	    vtag=`echo "$v" | sed 's/-.*//'` | ||||
| 	    numcommits=`git rev-list "$vtag"..HEAD | wc -l` | ||||
| 	    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; | ||||
| 	    ;; | ||||
|     esac | ||||
|  | ||||
|     # Change the first '-' to a '.', so version-comparing tools work properly. | ||||
|     # Remove the "g" in git describe's output string, to save a byte. | ||||
|     v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; | ||||
| else | ||||
|     v=UNKNOWN | ||||
| fi | ||||
|  | ||||
| v=`echo "$v" |sed 's/^v//'` | ||||
|  | ||||
| # Don't declare a version "dirty" merely because a time stamp has changed. | ||||
| git status > /dev/null 2>&1 | ||||
|  | ||||
| dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= | ||||
| case "$dirty" in | ||||
|     '') ;; | ||||
|     *) # Append the suffix only if there isn't one already. | ||||
| 	case $v in | ||||
| 	  *-dirty) ;; | ||||
| 	  *) v="$v-dirty" ;; | ||||
| 	esac ;; | ||||
| esac | ||||
|  | ||||
| # Omit the trailing newline, so that m4_esyscmd can use the result directly. | ||||
| echo "$v" | tr -d '\012' | ||||
|  | ||||
| # Local variables: | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # End: | ||||
							
								
								
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| nobase_include_HEADERS = osmocom/gsupclient/gsup_client.h | ||||
|  | ||||
							
								
								
									
										77
									
								
								include/osmocom/gsupclient/gsup_client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								include/osmocom/gsupclient/gsup_client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* GPRS Subscriber Update Protocol client */ | ||||
|  | ||||
| /* (C) 2014 by Sysmocom s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Jacob Erlbeck | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <osmocom/gsm/oap_client.h> | ||||
| #include <osmocom/gsm/ipa.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| /* a loss of GSUP between MSC and HLR is considered quite serious, let's try to recover as quickly as | ||||
|  * possible.  Even one new connection attempt per second should be quite acceptable until the link is | ||||
|  * re-established */ | ||||
| #define OSMO_GSUP_CLIENT_RECONNECT_INTERVAL 1 | ||||
| #define OSMO_GSUP_CLIENT_PING_INTERVAL 20 | ||||
|  | ||||
| struct msgb; | ||||
| struct ipa_client_conn; | ||||
| struct osmo_gsup_client; | ||||
|  | ||||
| /* Expects message in msg->l2h */ | ||||
| typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg); | ||||
|  | ||||
| struct osmo_gsup_client { | ||||
| 	const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */ | ||||
|  | ||||
| 	struct ipa_client_conn *link; | ||||
| 	osmo_gsup_client_read_cb_t read_cb; | ||||
| 	void *data; | ||||
|  | ||||
| 	struct osmo_oap_client_state oap_state; | ||||
|  | ||||
| 	struct osmo_timer_list ping_timer; | ||||
| 	struct osmo_timer_list connect_timer; | ||||
| 	int is_connected; | ||||
| 	int got_ipa_pong; | ||||
|  | ||||
| 	struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */ | ||||
| }; | ||||
|  | ||||
| struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx, | ||||
| 						  struct ipaccess_unit *ipa_dev, | ||||
| 						  const char *ip_addr, | ||||
| 						  unsigned int tcp_port, | ||||
| 						  osmo_gsup_client_read_cb_t read_cb, | ||||
| 						  struct osmo_oap_client_config *oapc_config); | ||||
| struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx, | ||||
| 						 const char *unit_name, | ||||
| 						 const char *ip_addr, | ||||
| 						 unsigned int tcp_port, | ||||
| 						 osmo_gsup_client_read_cb_t read_cb, | ||||
| 						 struct osmo_oap_client_config *oapc_config); | ||||
|  | ||||
| void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc); | ||||
| int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg); | ||||
| int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc, | ||||
| 			      const struct osmo_gsup_message *gsup_msg); | ||||
| struct msgb *osmo_gsup_client_msgb_alloc(void); | ||||
|  | ||||
							
								
								
									
										11
									
								
								libosmo-gsup-client.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								libosmo-gsup-client.pc.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| prefix=@prefix@ | ||||
| exec_prefix=@exec_prefix@ | ||||
| libdir=@libdir@ | ||||
| includedir=@includedir@ | ||||
|  | ||||
| Name: Osmocom GSUP Client Library | ||||
| Description: C Utility Library | ||||
| Version: @VERSION@ | ||||
| Libs: -L${libdir} -losmo-gsup-client | ||||
| Cflags: -I${includedir}/ | ||||
|  | ||||
							
								
								
									
										14
									
								
								sql/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								sql/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| EXTRA_DIST = \ | ||||
| 	hlr_data.sql \ | ||||
| 	hlr.sql \ | ||||
| 	$(NULL) | ||||
|  | ||||
| sqldir = $(docdir)/sql | ||||
| sql_DATA = $(srcdir)/hlr.sql $(srcdir)/hlr_data.sql | ||||
|  | ||||
|  | ||||
| install-data-local: | ||||
| 	$(MKDIR_P) $(DESTDIR)$(localstatedir)/lib/osmocom | ||||
|  | ||||
| uninstall-hook: | ||||
| 	rm -rf $(DESTDIR)$(localstatedir)/lib/osmocom | ||||
							
								
								
									
										43
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								sql/hlr.sql
									
									
									
									
									
								
							| @@ -1,13 +1,14 @@ | ||||
| --modelled roughly after TS 23.008 version 13.3.0 | ||||
|  | ||||
| CREATE TABLE subscriber ( | ||||
| -- OsmoHLR's DB scheme is modelled roughly after TS 23.008 version 13.3.0 | ||||
| 	id		INTEGER PRIMARY KEY, | ||||
| 	-- Chapter 2.1.1.1 | ||||
| 	imsi		VARCHAR(15) NOT NULL, | ||||
| 	imsi		VARCHAR(15) UNIQUE NOT NULL, | ||||
| 	-- Chapter 2.1.2 | ||||
| 	msisdn		VARCHAR(15), | ||||
| 	-- Chapter 2.2.3: Most recent / current IMEI | ||||
| 	msisdn		VARCHAR(15) UNIQUE, | ||||
| 	-- Chapter 2.2.3: Most recent / current IMEISV | ||||
| 	imeisv		VARCHAR, | ||||
| 	-- Chapter 2.1.9: Most recent / current IMEI | ||||
| 	imei		VARCHAR(14), | ||||
| 	-- Chapter 2.4.5 | ||||
| 	vlr_number	VARCHAR(15), | ||||
| 	-- Chapter 2.4.6 | ||||
| @@ -37,7 +38,11 @@ CREATE TABLE subscriber ( | ||||
| 	-- Chapter 2.7.5 | ||||
| 	ms_purged_cs	BOOLEAN NOT NULL DEFAULT 0, | ||||
| 	-- Chapter 2.7.6 | ||||
| 	ms_purged_ps	BOOLEAN NOT NULL DEFAULT 0 | ||||
| 	ms_purged_ps	BOOLEAN NOT NULL DEFAULT 0, | ||||
|  | ||||
| 	-- Timestamp of last location update seen from subscriber | ||||
| 	-- The value is a string which encodes a UTC timestamp in granularity of seconds. | ||||
| 	last_lu_seen TIMESTAMP default NULL | ||||
| ); | ||||
|  | ||||
| CREATE TABLE subscriber_apn ( | ||||
| @@ -45,26 +50,30 @@ CREATE TABLE subscriber_apn ( | ||||
| 	apn		VARCHAR(256) NOT NULL | ||||
| ); | ||||
|  | ||||
| -- Chapter 2.1.3 | ||||
| CREATE TABLE subscriber_multi_msisdn ( | ||||
| -- Chapter 2.1.3 | ||||
| 	subscriber_id	INTEGER,		-- subscriber.id | ||||
| 	msisdn		VARCHAR(15) NOT NULL | ||||
| ); | ||||
|  | ||||
| CREATE TABLE auc_2g ( | ||||
| 	subscriber_id	INTEGER PRIMARY KEY,	-- subscriber.id | ||||
| 	algo_id_2g	INTEGER NOT NULL, | ||||
| 	ki		VARCHAR NOT NULL | ||||
| 	algo_id_2g	INTEGER NOT NULL,	-- enum osmo_auth_algo value | ||||
| 	ki		VARCHAR(32) NOT NULL	-- hex string: subscriber's secret key (128bit) | ||||
| ); | ||||
|  | ||||
| CREATE TABLE auc_3g ( | ||||
| 	subscriber_id	INTEGER PRIMARY KEY,	-- subscrbier.id | ||||
| 	algo_id_3g	INTEGER NOT NULL, | ||||
| 	k		INTEGER NOT NULL, | ||||
| 	op		VARCHAR, | ||||
| 	opc		VARCHAR, | ||||
| 	sqn		INTEGER | ||||
| 	subscriber_id	INTEGER PRIMARY KEY,	-- subscriber.id | ||||
| 	algo_id_3g	INTEGER NOT NULL,	-- enum osmo_auth_algo value | ||||
| 	k		VARCHAR(32) NOT NULL,	-- hex string: subscriber's secret key (128bit) | ||||
| 	op		VARCHAR(32),		-- hex string: operator's secret key (128bit) | ||||
| 	opc		VARCHAR(32),		-- hex string: derived from OP and K (128bit) | ||||
| 	sqn		INTEGER NOT NULL DEFAULT 0,	-- sequence number of key usage | ||||
| 	ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end | ||||
| ); | ||||
|  | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS idx_subscr_imsi ON subscriber (imsi); | ||||
| -- SELECT algo_id_2g, ki, algo_id_3g, k, op, opc, sqn FROM subscriber LEFT JOIN auc_2g ON auc_2g.subscriber_id = subscriber.id LEFT JOIN auc_3g ON auc_3g.subscriber_id = subscriber.id WHERE imsi = ? | ||||
| CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi); | ||||
|  | ||||
| -- Set HLR database schema version number | ||||
| -- Note: This constant is currently duplicated in src/db.c and must be kept in sync! | ||||
| PRAGMA user_version = 2; | ||||
|   | ||||
| @@ -5,9 +5,9 @@ INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki) VALUES (1, 1, '00010203040506 | ||||
|  | ||||
| -- 3G only subscriber | ||||
| INSERT INTO subscriber (id, imsi) VALUES (2, '901990000000002'); | ||||
| INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, sqn) VALUES (2, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0); | ||||
| INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (2, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0); | ||||
|  | ||||
| -- 2G + 3G subscriber | ||||
| INSERT INTO subscriber (id, imsi) VALUES (3, '901990000000003'); | ||||
| INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki) VALUES (3, 1, '000102030405060708090a0b0c0d0e0f'); | ||||
| INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, sqn) VALUES (3, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0); | ||||
| INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (3, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0); | ||||
|   | ||||
							
								
								
									
										18
									
								
								src/Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/Makefile
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | ||||
| LDFLAGS += -losmocore -losmogsm -losmoabis -lsqlite3 -ltalloc | ||||
| CFLAGS += -g -Wall | ||||
|  | ||||
| OBJS = auc.o db.o db_auc.o db_hlr.o logging.o | ||||
|  | ||||
| all: db_test hlr | ||||
|  | ||||
| db_test: db_test.o rand_fake.o $(OBJS) | ||||
| 	$(CC) -o $@ $^ $(LDFLAGS) | ||||
|  | ||||
| hlr: hlr.o gsup_server.o gsup_router.o rand_urandom.o $(OBJS) | ||||
| 	$(CC) -o $@ $^ $(LDFLAGS) | ||||
|  | ||||
| %.o: %.c | ||||
| 	$(CC) $(CFLAGS) -o $@ -c $^ | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o db_test | ||||
							
								
								
									
										114
									
								
								src/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| SUBDIRS = gsupclient | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMOCTRL_CFLAGS) \ | ||||
| 	$(LIBOSMOABIS_CFLAGS) \ | ||||
| 	$(SQLITE3_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CPPFLAGS = -I$(top_srcdir)/include \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	populate_hlr_db.pl \ | ||||
| 	db_sql2c.sed \ | ||||
| 	$(NULL) | ||||
|  | ||||
| BUILT_SOURCES = \ | ||||
| 	db_bootstrap.h \ | ||||
| 	$(NULL) | ||||
| CLEANFILES = $(BUILT_SOURCES) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	auc.h \ | ||||
| 	db.h \ | ||||
| 	hlr.h \ | ||||
| 	luop.h \ | ||||
| 	gsup_router.h \ | ||||
| 	gsup_server.h \ | ||||
| 	logging.h \ | ||||
| 	rand.h \ | ||||
| 	ctrl.h \ | ||||
| 	hlr_vty.h \ | ||||
| 	hlr_vty_subscr.h \ | ||||
| 	hlr_ussd.h \ | ||||
| 	db_bootstrap.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| bin_PROGRAMS = \ | ||||
| 	osmo-hlr \ | ||||
| 	osmo-hlr-db-tool \ | ||||
| 	osmo-euse-demo \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_hlr_SOURCES = \ | ||||
| 	auc.c \ | ||||
| 	ctrl.c \ | ||||
| 	db.c \ | ||||
| 	luop.c \ | ||||
| 	db_auc.c \ | ||||
| 	db_hlr.c \ | ||||
| 	gsup_router.c \ | ||||
| 	gsup_server.c \ | ||||
| 	hlr.c \ | ||||
| 	logging.c \ | ||||
| 	rand_urandom.c \ | ||||
| 	hlr_vty.c \ | ||||
| 	hlr_vty_subscr.c \ | ||||
| 	gsup_send.c \ | ||||
| 	hlr_ussd.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_hlr_LDADD = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMOCTRL_LIBS) \ | ||||
| 	$(LIBOSMOABIS_LIBS) \ | ||||
| 	$(SQLITE3_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_hlr_db_tool_SOURCES = \ | ||||
| 	hlr_db_tool.c \ | ||||
| 	db.c \ | ||||
| 	db_hlr.c \ | ||||
| 	logging.c \ | ||||
| 	rand_urandom.c \ | ||||
| 	dbd_decode_binary.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_hlr_db_tool_LDADD = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(SQLITE3_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_euse_demo_SOURCES = \ | ||||
| 	osmo-euse-demo.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_euse_demo_LDADD = \ | ||||
| 	$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(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 | ||||
|  | ||||
| db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed | ||||
| 	echo "/* DO NOT EDIT THIS FILE. It is generated from files in osmo-hlr.git/sql/ */" > "$@" | ||||
| 	echo "#pragma once" >> "$@" | ||||
| 	echo "static const char *stmt_bootstrap_sql[] = {" >> "$@" | ||||
| 	cat "$(BOOTSTRAP_SQL)" \ | ||||
| 		| sed -f "$(srcdir)/db_sql2c.sed" \ | ||||
| 		>> "$@" | ||||
| 	echo "};" >> "$@" | ||||
							
								
								
									
										143
									
								
								src/auc.c
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/auc.c
									
									
									
									
									
								
							| @@ -18,6 +18,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/crypt/auth.h> | ||||
| @@ -25,8 +26,11 @@ | ||||
| #include "logging.h" | ||||
| #include "rand.h" | ||||
|  | ||||
| #define hexb(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) | ||||
| #define hex(buf,sz) osmo_hexdump_nospc((void*)buf, sz) | ||||
|  | ||||
| /* compute given number of vectors using either aud2g or aud2g or a combination | ||||
|  * of both.  Handles re-synchrnization if rand_auts and auts are set */ | ||||
|  * of both.  Handles re-synchronization if rand_auts and auts are set */ | ||||
| int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| 			struct osmo_sub_auth_data *aud2g, | ||||
| 			struct osmo_sub_auth_data *aud3g, | ||||
| @@ -34,17 +38,71 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	uint8_t rand[16]; | ||||
| 	struct osmo_auth_vector vtmp; | ||||
| 	int rc; | ||||
|  | ||||
| 	if (aud2g->algo == OSMO_AUTH_ALG_NONE) | ||||
| 	/* no need to iterate the log categories all the time */ | ||||
| 	int dbg = log_check_level(DAUC, LOGL_DEBUG); | ||||
| #define DBGP(args ...) if (dbg) DEBUGP(DAUC, ##args) | ||||
| #define DBGVB(member) DBGP("vector [%u]: " #member " = %s\n", \ | ||||
| 			   i, hexb(vec[i].member)) | ||||
| #define DBGVV(fmt, member) DBGP("vector [%u]: " #member " = " fmt "\n", \ | ||||
| 			        i, vec[i].member) | ||||
|  | ||||
| 	if (aud2g && (aud2g->algo == OSMO_AUTH_ALG_NONE | ||||
| 		      || aud2g->type == OSMO_AUTH_TYPE_NONE)) | ||||
| 		aud2g = NULL; | ||||
| 	if (aud3g->algo == OSMO_AUTH_ALG_NONE) | ||||
| 	if (aud3g && (aud3g->algo == OSMO_AUTH_ALG_NONE | ||||
| 		      || aud3g->type == OSMO_AUTH_TYPE_NONE)) | ||||
| 		aud3g = NULL; | ||||
|  | ||||
| 	if (!aud2g && !aud3g) | ||||
| 	if (!aud2g && !aud3g) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "auc_compute_vectors() called" | ||||
| 		     " with neither 2G nor 3G auth data available\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (aud2g && aud2g->type != OSMO_AUTH_TYPE_GSM) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "auc_compute_vectors() called" | ||||
| 		     " with non-2G auth data passed for aud2g arg\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (aud3g && aud3g->type != OSMO_AUTH_TYPE_UMTS) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "auc_compute_vectors() called" | ||||
| 		     " with non-3G auth data passed for aud3g arg\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if ((rand_auts != NULL) != (auts != NULL)) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "auc_compute_vectors() with only one" | ||||
| 		     " of AUTS and AUTS_RAND given, need both or neither\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (auts && !aud3g) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "auc_compute_vectors() with AUTS called" | ||||
| 		     " but no 3G auth data passed\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	DBGP("Computing %d auth vector%s: %s%s\n", | ||||
| 	     num_vec, num_vec == 1 ? "" : "s", | ||||
| 	     aud3g? (aud2g? "3G + separate 2G" | ||||
| 		     : "3G only (2G derived from 3G keys)") | ||||
| 	     : "2G only", | ||||
| 	     auts? ", with AUTS resync" : ""); | ||||
| 	if (aud3g) { | ||||
| 		DBGP("3G: k = %s\n", hexb(aud3g->u.umts.k)); | ||||
| 		DBGP("3G: %s = %s\n", | ||||
| 		     aud3g->u.umts.opc_is_op? "OP" : "opc", | ||||
| 		     hexb(aud3g->u.umts.opc)); | ||||
| 		DBGP("3G: for sqn ind %u, previous sqn was %" PRIu64 "\n", | ||||
| 		     aud3g->u.umts.ind, aud3g->u.umts.sqn); | ||||
| 	} | ||||
| 	if (aud2g) | ||||
| 		DBGP("2G: ki = %s\n", hexb(aud2g->u.gsm.ki)); | ||||
|  | ||||
| 	/* compute quintuples */ | ||||
| 	for (i = 0; i < num_vec; i++) { | ||||
| 		rc = rand_get(rand, sizeof(rand)); | ||||
| 		if (rc != sizeof(rand)) { | ||||
| @@ -52,43 +110,76 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| 			     "bytes: rc=%d\n", sizeof(rand), rc); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		DBGP("vector [%u]: rand = %s\n", i, hexb(rand)); | ||||
|  | ||||
| 		if (aud2g && !aud3g) { | ||||
| 			/* 2G only case: output directly to vec */ | ||||
| 			rc = osmo_auth_gen_vec(vec+i, aud2g, rand); | ||||
| 			if (rc < 0) { | ||||
| 				LOGP(DAUC, LOGL_ERROR, "Error in 2G vector " | ||||
| 				     "generation: %d\n", rc); | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} else if (aud3g) { | ||||
| 		if (aud3g) { | ||||
| 			/* 3G or 3G + 2G case */ | ||||
| 			if (rand_auts && auts) | ||||
| 				rc = osmo_auth_gen_vec_auts(vec+i, aud3g, | ||||
| 							    rand_auts, | ||||
| 							    auts, rand); | ||||
| 			else | ||||
|  | ||||
| 			/* Do AUTS only for the first vector or we would use | ||||
| 			 * the same SQN for each following key. */ | ||||
| 			if ((i == 0) && auts) { | ||||
| 				DBGP("vector [%u]: resync: auts = %s\n", | ||||
| 				     i, hex(auts, 14)); | ||||
| 				DBGP("vector [%u]: resync: rand_auts = %s\n", | ||||
| 				     i, hex(rand_auts, 16)); | ||||
|  | ||||
| 				rc = osmo_auth_gen_vec_auts(vec+i, aud3g, auts, | ||||
| 							    rand_auts, rand); | ||||
| 			} else { | ||||
| 				rc = osmo_auth_gen_vec(vec+i, aud3g, rand); | ||||
| 			} | ||||
| 			if (rc < 0) { | ||||
| 				LOGP(DAUC, LOGL_ERROR, "Error in 3G vector " | ||||
| 				     "generation: %d\n", rc); | ||||
| 				     "generation: [%u]: rc = %d\n", i, rc); | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
| 		if (aud2g && aud3g) { | ||||
| 			/* separate 2G + 3G case: patch 2G into 3G */ | ||||
| 			struct osmo_auth_vector vtmp; | ||||
| 			DBGP("vector [%u]: sqn = %" PRIu64 "\n", | ||||
| 			     i, aud3g->u.umts.sqn); | ||||
|  | ||||
| 			DBGVB(autn); | ||||
| 			DBGVB(ck); | ||||
| 			DBGVB(ik); | ||||
| 			DBGVB(res); | ||||
| 			DBGVV("%u", res_len); | ||||
|  | ||||
| 			if (!aud2g) { | ||||
| 				/* use the 2G tokens from 3G keys */ | ||||
| 				DBGP("vector [%u]: deriving 2G from 3G\n", i); | ||||
| 				DBGVB(kc); | ||||
| 				DBGVB(sres); | ||||
| 				DBGVV("0x%x", auth_types); | ||||
| 				continue; | ||||
| 			} | ||||
| 			/* calculate 2G separately */ | ||||
|  | ||||
| 			DBGP("vector [%u]: calculating 2G separately\n", i); | ||||
|  | ||||
| 			rc = osmo_auth_gen_vec(&vtmp, aud2g, rand); | ||||
| 			if (rc < 0) { | ||||
| 				LOGP(DAUC, LOGL_ERROR, "Error in 2G vector " | ||||
| 				     "generation: %d\n", rc); | ||||
| 				LOGP(DAUC, LOGL_ERROR, "Error in 2G vector" | ||||
| 				     "generation: [%u]: rc = %d\n", i, rc); | ||||
| 				goto out; | ||||
| 			} | ||||
| 			memcpy(&vec[i].kc, vtmp.kc, sizeof(vec[i].kc)); | ||||
| 			memcpy(&vec[i].sres, vtmp.sres, sizeof(vec[i].sres)); | ||||
| 			vec[i].auth_types |= OSMO_AUTH_TYPE_GSM; | ||||
| 		} else { | ||||
| 			/* 2G only case */ | ||||
| 			rc = osmo_auth_gen_vec(vec+i, aud2g, rand); | ||||
| 			if (rc < 0) { | ||||
| 				LOGP(DAUC, LOGL_ERROR, "Error in 2G vector " | ||||
| 				     "generation: [%u]: rc = %d\n", i, rc); | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		DBGVB(kc); | ||||
| 		DBGVB(sres); | ||||
| 		DBGVV("0x%x", auth_types); | ||||
| 	} | ||||
| out: | ||||
| 	return i; | ||||
| #undef DBGVV | ||||
| #undef DBGVB | ||||
| #undef DBGP | ||||
| } | ||||
|   | ||||
							
								
								
									
										408
									
								
								src/ctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										408
									
								
								src/ctrl.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,408 @@ | ||||
| /* OsmoHLR Control Interface implementation */ | ||||
|  | ||||
| /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Max Suraev <msuraev@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <errno.h> | ||||
| #include <inttypes.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/gsm/gsm23003.h> | ||||
| #include <osmocom/ctrl/ports.h> | ||||
|  | ||||
| #include "hlr.h" | ||||
| #include "ctrl.h" | ||||
| #include "db.h" | ||||
|  | ||||
| #define SEL_BY "by-" | ||||
| #define SEL_BY_IMSI SEL_BY "imsi-" | ||||
| #define SEL_BY_MSISDN SEL_BY "msisdn-" | ||||
| #define SEL_BY_ID SEL_BY "id-" | ||||
|  | ||||
| #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) | ||||
|  | ||||
| static bool startswith(const char *str, const char *start) | ||||
| { | ||||
| 	return strncmp(str, start, strlen(start)) == 0; | ||||
| } | ||||
|  | ||||
| static int _get_subscriber(struct db_context *dbc, | ||||
| 			   const char *by_selector, | ||||
| 			   struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	const char *val; | ||||
| 	if (startswith(by_selector, SEL_BY_IMSI)) { | ||||
| 		val = by_selector + strlen(SEL_BY_IMSI); | ||||
| 		if (!osmo_imsi_str_valid(val)) | ||||
| 			return -EINVAL; | ||||
| 		return db_subscr_get_by_imsi(dbc, val, subscr); | ||||
| 	} | ||||
| 	if (startswith(by_selector, SEL_BY_MSISDN)) { | ||||
| 		val = by_selector + strlen(SEL_BY_MSISDN); | ||||
| 		if (!osmo_msisdn_str_valid(val)) | ||||
| 			return -EINVAL; | ||||
| 		return db_subscr_get_by_msisdn(dbc, val, subscr); | ||||
| 	} | ||||
| 	if (startswith(by_selector, SEL_BY_ID)) { | ||||
| 		int64_t id; | ||||
| 		char *endptr; | ||||
| 		val = by_selector + strlen(SEL_BY_ID); | ||||
| 		if (*val == '+') | ||||
| 			return -EINVAL; | ||||
| 		errno = 0; | ||||
| 		id = strtoll(val, &endptr, 10); | ||||
| 		if (errno || *endptr) | ||||
| 			return -EINVAL; | ||||
| 		return db_subscr_get_by_id(dbc, id, subscr); | ||||
| 	} | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
|  | ||||
| static bool get_subscriber(struct db_context *dbc, | ||||
| 			   const char *by_selector, | ||||
| 			   struct hlr_subscriber *subscr, | ||||
| 			   struct ctrl_cmd *cmd) | ||||
| { | ||||
| 	int rc = _get_subscriber(dbc, by_selector, subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		return true; | ||||
| 	case -ENOTSUP: | ||||
| 		cmd->reply = "Not a known subscriber 'by-xxx-' selector."; | ||||
| 		return false; | ||||
| 	case -EINVAL: | ||||
| 		cmd->reply = "Invalid value part of 'by-xxx-value' selector."; | ||||
| 		return false; | ||||
| 	case -ENOENT: | ||||
| 		cmd->reply = "No such subscriber."; | ||||
| 		return false; | ||||
| 	default: | ||||
| 		cmd->reply = "An unknown error has occured during get_subscriber()."; | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Optimization: if a subscriber operation is requested by-imsi, just return | ||||
|  * the IMSI right back. */ | ||||
| static const char *get_subscriber_imsi(struct db_context *dbc, | ||||
| 				       const char *by_selector, | ||||
| 				       struct ctrl_cmd *cmd) | ||||
| { | ||||
| 	static struct hlr_subscriber subscr; | ||||
|  | ||||
| 	if (startswith(by_selector, SEL_BY_IMSI)) | ||||
| 		return by_selector + strlen(SEL_BY_IMSI); | ||||
| 	if (!get_subscriber(dbc, by_selector, &subscr, cmd)) | ||||
| 		return NULL; | ||||
| 	return subscr.imsi; | ||||
| } | ||||
|  | ||||
| /* printf fmt and arg to completely omit a string if it is empty. */ | ||||
| #define FMT_S "%s%s%s%s" | ||||
| #define ARG_S(name, val) \ | ||||
| 	(val) && *(val) ? "\n" : "", \ | ||||
| 	(val) && *(val) ? name : "", \ | ||||
| 	(val) && *(val) ? "\t" : "", \ | ||||
| 	(val) && *(val) ? (val) : "" \ | ||||
|  | ||||
| /* printf fmt and arg to completely omit bool of given value. */ | ||||
| #define FMT_BOOL "%s" | ||||
| #define ARG_BOOL(name, val) \ | ||||
| 	val ? "\n" name "\t1" : "\n" name "\t0" | ||||
|  | ||||
| static void print_subscr_info(struct ctrl_cmd *cmd, | ||||
| 			      struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	ctrl_cmd_reply_printf(cmd, | ||||
| 		"\nid\t%" PRIu64 | ||||
| 		FMT_S | ||||
| 		FMT_S | ||||
| 		FMT_BOOL | ||||
| 		FMT_BOOL | ||||
| 		FMT_S | ||||
| 		FMT_S | ||||
| 		FMT_S | ||||
| 		FMT_BOOL | ||||
| 		FMT_BOOL | ||||
| 		"\nperiodic_lu_timer\t%u" | ||||
| 		"\nperiodic_rau_tau_timer\t%u" | ||||
| 		"\nlmsi\t%08x" | ||||
| 		, | ||||
| 		subscr->id, | ||||
| 		ARG_S("imsi", subscr->imsi), | ||||
| 		ARG_S("msisdn", subscr->msisdn), | ||||
| 		ARG_BOOL("nam_cs", subscr->nam_cs), | ||||
| 		ARG_BOOL("nam_ps", subscr->nam_ps), | ||||
| 		ARG_S("vlr_number", subscr->vlr_number), | ||||
| 		ARG_S("sgsn_number", subscr->sgsn_number), | ||||
| 		ARG_S("sgsn_address", subscr->sgsn_address), | ||||
| 		ARG_BOOL("ms_purged_cs", subscr->ms_purged_cs), | ||||
| 		ARG_BOOL("ms_purged_ps", subscr->ms_purged_ps), | ||||
| 		subscr->periodic_lu_timer, | ||||
| 		subscr->periodic_rau_tau_timer, | ||||
| 		subscr->lmsi | ||||
| 		); | ||||
| } | ||||
|  | ||||
| static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud) | ||||
| { | ||||
| 	if (aud->algo == OSMO_AUTH_ALG_NONE) | ||||
| 		return; | ||||
| 	ctrl_cmd_reply_printf(cmd, | ||||
| 		"\naud2g.algo\t%s" | ||||
| 		"\naud2g.ki\t%s" | ||||
| 		, | ||||
| 		osmo_auth_alg_name(aud->algo), | ||||
| 		hexdump_buf(aud->u.gsm.ki)); | ||||
| } | ||||
|  | ||||
| static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud) | ||||
| { | ||||
| 	if (aud->algo == OSMO_AUTH_ALG_NONE) | ||||
| 		return; | ||||
| 	ctrl_cmd_reply_printf(cmd, | ||||
| 		"\naud3g.algo\t%s" | ||||
| 		"\naud3g.k\t%s" | ||||
| 		, | ||||
| 		osmo_auth_alg_name(aud->algo), | ||||
| 		hexdump_buf(aud->u.umts.k)); | ||||
| 	/* hexdump uses a static string buffer, hence only one hexdump per | ||||
| 	 * printf(). */ | ||||
| 	ctrl_cmd_reply_printf(cmd, | ||||
| 		"\naud3g.%s\t%s" | ||||
| 		"\naud3g.ind_bitlen\t%u" | ||||
| 		"\naud3g.sqn\t%" PRIu64 | ||||
| 		, | ||||
| 		aud->u.umts.opc_is_op? "op" : "opc", | ||||
| 		hexdump_buf(aud->u.umts.opc), | ||||
| 		aud->u.umts.ind_bitlen, | ||||
| 		aud->u.umts.sqn); | ||||
| } | ||||
|  | ||||
| CTRL_CMD_DEFINE_RO(subscr_info, "info"); | ||||
| static int get_subscr_info(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	struct hlr *hlr = data; | ||||
| 	const char *by_selector = cmd->node; | ||||
|  | ||||
| 	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd)) | ||||
| 		return CTRL_CMD_ERROR; | ||||
|  | ||||
| 	print_subscr_info(cmd, &subscr); | ||||
|  | ||||
| 	return CTRL_CMD_REPLY; | ||||
| } | ||||
|  | ||||
| CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud"); | ||||
| static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	const char *imsi; | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	struct hlr *hlr = data; | ||||
| 	const char *by_selector = cmd->node; | ||||
| 	int rc; | ||||
|  | ||||
| 	imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd); | ||||
| 	if (!imsi) | ||||
| 		return CTRL_CMD_ERROR; | ||||
|  | ||||
| 	rc = db_get_auth_data(hlr->dbc, imsi, &aud2g, &aud3g, NULL); | ||||
|  | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 	case -ENOKEY: | ||||
| 		/* No auth data found, tell the print*() functions about it. */ | ||||
| 		aud2g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		aud3g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		break; | ||||
| 	default: | ||||
| 		cmd->reply = "Error retrieving authentication data."; | ||||
| 		return CTRL_CMD_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	print_subscr_info_aud2g(cmd, &aud2g); | ||||
| 	print_subscr_info_aud3g(cmd, &aud3g); | ||||
|  | ||||
| 	return CTRL_CMD_REPLY; | ||||
| } | ||||
|  | ||||
| CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all"); | ||||
| static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	struct hlr *hlr = data; | ||||
| 	const char *by_selector = cmd->node; | ||||
| 	int rc; | ||||
|  | ||||
| 	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd)) | ||||
| 		return CTRL_CMD_ERROR; | ||||
|  | ||||
| 	rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g, NULL); | ||||
|  | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 	case -ENOKEY: | ||||
| 		/* No auth data found, tell the print*() functions about it. */ | ||||
| 		aud2g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		aud3g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		break; | ||||
| 	default: | ||||
| 		cmd->reply = "Error retrieving authentication data."; | ||||
| 		return CTRL_CMD_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	print_subscr_info(cmd, &subscr); | ||||
| 	print_subscr_info_aud2g(cmd, &aud2g); | ||||
| 	print_subscr_info_aud3g(cmd, &aud3g); | ||||
|  | ||||
| 	return CTRL_CMD_REPLY; | ||||
| } | ||||
|  | ||||
| static int verify_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data) | ||||
| { | ||||
| 	if (!value || !*value | ||||
| 	    || (strcmp(value, "0") && strcmp(value, "1"))) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int get_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data, | ||||
| 				    bool is_ps) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	struct hlr *hlr = data; | ||||
| 	const char *by_selector = cmd->node; | ||||
|  | ||||
| 	if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd)) | ||||
| 		return CTRL_CMD_ERROR; | ||||
|  | ||||
| 	cmd->reply = (is_ps ? subscr.nam_ps : subscr.nam_cs) | ||||
| 		     ? "1" : "0"; | ||||
| 	return CTRL_CMD_REPLY; | ||||
| } | ||||
|  | ||||
| static int set_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data, | ||||
| 				    bool is_ps) | ||||
| { | ||||
| 	const char *imsi; | ||||
| 	struct hlr *hlr = data; | ||||
| 	const char *by_selector = cmd->node; | ||||
|  | ||||
| 	imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd); | ||||
| 	if (!imsi) | ||||
| 		return CTRL_CMD_ERROR; | ||||
| 	if (db_subscr_nam(hlr->dbc, imsi, strcmp(cmd->value, "1") == 0, is_ps)) | ||||
| 		return CTRL_CMD_ERROR; | ||||
| 	cmd->reply = "OK"; | ||||
| 	return CTRL_CMD_REPLY; | ||||
| } | ||||
|  | ||||
| CTRL_CMD_DEFINE(subscr_ps_enabled, "ps-enabled"); | ||||
| static int verify_subscr_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data) | ||||
| { | ||||
| 	return verify_subscr_cs_ps_enabled(cmd, value, data); | ||||
| } | ||||
| static int get_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	return get_subscr_cs_ps_enabled(cmd, data, true); | ||||
| } | ||||
| static int set_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	return set_subscr_cs_ps_enabled(cmd, data, true); | ||||
| } | ||||
|  | ||||
| CTRL_CMD_DEFINE(subscr_cs_enabled, "cs-enabled"); | ||||
| static int verify_subscr_cs_enabled(struct ctrl_cmd *cmd, const char *value, void *data) | ||||
| { | ||||
| 	return verify_subscr_cs_ps_enabled(cmd, value, data); | ||||
| } | ||||
| static int get_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	return get_subscr_cs_ps_enabled(cmd, data, false); | ||||
| } | ||||
| static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data) | ||||
| { | ||||
| 	return set_subscr_cs_ps_enabled(cmd, data, false); | ||||
| } | ||||
|  | ||||
| int hlr_ctrl_cmds_install() | ||||
| { | ||||
| 	int rc = 0; | ||||
|  | ||||
| 	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info); | ||||
| 	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud); | ||||
| 	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all); | ||||
| 	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled); | ||||
| 	rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled); | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type, | ||||
| 				void **node_data, int *i) | ||||
| { | ||||
| 	const char *token = vector_slot(vline, *i); | ||||
|  | ||||
| 	switch (*node_type) { | ||||
| 	case CTRL_NODE_ROOT: | ||||
| 		if (strcmp(token, "subscriber") != 0) | ||||
| 			return 0; | ||||
| 		*node_data = NULL; | ||||
| 		*node_type = CTRL_NODE_SUBSCR; | ||||
| 		break; | ||||
| 	case CTRL_NODE_SUBSCR: | ||||
| 		if (!startswith(token, "by-")) | ||||
| 			return 0; | ||||
| 		*node_data = (void*)token; | ||||
| 		*node_type = CTRL_NODE_SUBSCR_BY; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr, | ||||
| 							      hlr->ctrl_bind_addr, | ||||
| 							      OSMO_CTRL_PORT_HLR, | ||||
| 							      hlr_ctrl_node_lookup, | ||||
| 							      _LAST_CTRL_NODE_HLR); | ||||
| 	if (!hdl) | ||||
| 		return NULL; | ||||
|  | ||||
| 	rc = hlr_ctrl_cmds_install(); | ||||
| 	if (rc) /* FIXME: close control interface? */ | ||||
| 		return NULL; | ||||
|  | ||||
| 	return hdl; | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/ctrl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/ctrl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* OsmoHLR Control Interface implementation */ | ||||
|  | ||||
| /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Max Suraev <msuraev@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/ctrl/control_if.h> | ||||
|  | ||||
| enum hlr_ctrl_node { | ||||
| 	CTRL_NODE_SUBSCR = _LAST_CTRL_NODE, | ||||
| 	CTRL_NODE_SUBSCR_BY, | ||||
| 	_LAST_CTRL_NODE_HLR | ||||
| }; | ||||
|  | ||||
| int hlr_ctrl_cmds_install(); | ||||
| struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr); | ||||
							
								
								
									
										404
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										404
									
								
								src/db.c
									
									
									
									
									
								
							| @@ -19,19 +19,68 @@ | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <sqlite3.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "db.h" | ||||
| #include "db_bootstrap.h" | ||||
|  | ||||
| /* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */ | ||||
| #define CURRENT_SCHEMA_VERSION	2 | ||||
|  | ||||
| #define SEL_COLUMNS \ | ||||
| 	"id," \ | ||||
| 	"imsi," \ | ||||
| 	"msisdn," \ | ||||
| 	"imei," \ | ||||
| 	"vlr_number," \ | ||||
| 	"sgsn_number," \ | ||||
| 	"sgsn_address," \ | ||||
| 	"periodic_lu_tmr," \ | ||||
| 	"periodic_rau_tau_tmr," \ | ||||
| 	"nam_cs," \ | ||||
| 	"nam_ps," \ | ||||
| 	"lmsi," \ | ||||
| 	"ms_purged_cs," \ | ||||
| 	"ms_purged_ps," \ | ||||
| 	"last_lu_seen" | ||||
|  | ||||
| static const char *stmt_sql[] = { | ||||
| 	[SEL_BY_IMSI] = "SELECT id,imsi,msisdn,vlr_number,sgsn_number,sgsn_address,periodic_lu_tmr,periodic_rau_tau_tmr,nam_cs,nam_ps,lmsi,ms_purged_cs,ms_purged_ps FROM subscriber WHERE imsi = ?", | ||||
| 	[UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = ? WHERE id = ?", | ||||
| 	[UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = ? WHERE id = ?", | ||||
| 	[AUC_BY_IMSI] = "SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn FROM subscriber LEFT JOIN auc_2g ON auc_2g.subscriber_id = subscriber.id LEFT JOIN auc_3g ON auc_3g.subscriber_id = subscriber.id WHERE imsi = ?", | ||||
| 	[AUC_UPD_SQN] = "UPDATE auc_3g SET sqn = ? WHERE subscriber_id = ?", | ||||
| 	[UPD_PURGE_CS_BY_IMSI] = "UPDATE subscriber SET ms_purged_cs=1 WHERE imsi = ?", | ||||
| 	[UPD_PURGE_PS_BY_IMSI] = "UPDATE subscriber SET ms_purged_ps=1 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_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_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] = | ||||
| 		"SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen" | ||||
| 		" FROM subscriber" | ||||
| 		" LEFT JOIN auc_2g ON auc_2g.subscriber_id = subscriber.id" | ||||
| 		" LEFT JOIN auc_3g ON auc_3g.subscriber_id = subscriber.id" | ||||
| 		" WHERE imsi = $imsi", | ||||
| 	[DB_STMT_AUC_UPD_SQN] = "UPDATE auc_3g SET sqn = $sqn WHERE subscriber_id = $subscriber_id", | ||||
| 	[DB_STMT_UPD_PURGE_CS_BY_IMSI] = "UPDATE subscriber SET ms_purged_cs = $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_PS_BY_IMSI] = "UPDATE subscriber SET nam_ps = $val WHERE imsi = $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_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_AUC_2G_INSERT] = | ||||
| 		"INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)" | ||||
| 		" VALUES($subscriber_id, $algo_id_2g, $ki)", | ||||
| 	[DB_STMT_AUC_2G_DELETE] = "DELETE FROM auc_2g WHERE subscriber_id = $subscriber_id", | ||||
| 	[DB_STMT_AUC_3G_INSERT] = | ||||
| 		"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)" | ||||
| 		" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)", | ||||
| 	[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id", | ||||
| 	[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id", | ||||
| 	[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi", | ||||
| 	[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn", | ||||
| }; | ||||
|  | ||||
| static void sql3_error_log_cb(void *arg, int err_code, const char *msg) | ||||
| @@ -46,7 +95,7 @@ static void sql3_sql_log_cb(void *arg, sqlite3 *s3, const char *stmt, int type) | ||||
| 		LOGP(DDB, LOGL_DEBUG, "Opened database\n"); | ||||
| 		break; | ||||
| 	case 1: | ||||
| 		LOGP(DDB, LOGL_DEBUG, stmt); | ||||
| 		LOGP(DDB, LOGL_DEBUG, "%s\n", stmt); | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		LOGP(DDB, LOGL_DEBUG, "Closed database\n"); | ||||
| @@ -57,43 +106,300 @@ static void sql3_sql_log_cb(void *arg, sqlite3 *s3, const char *stmt, int type) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* remove bindings and reset statement to be re-executed */ | ||||
| void db_remove_reset(sqlite3_stmt *stmt) | ||||
| { | ||||
| 	sqlite3_clear_bindings(stmt); | ||||
| 	/* sqlite3_reset() just repeats an error code already evaluated during sqlite3_step(). */ | ||||
| 	/* coverity[CHECKED_RETURN] */ | ||||
| 	sqlite3_reset(stmt); | ||||
| } | ||||
|  | ||||
| /** bind text arg and do proper cleanup in case of failure. If param_name is | ||||
|  * NULL, bind to the first parameter (useful for SQL statements that have only | ||||
|  * one parameter). */ | ||||
| bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text) | ||||
| { | ||||
| 	int rc; | ||||
| 	int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1; | ||||
| 	if (idx < 1) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n", | ||||
| 		     param_name); | ||||
| 		return false; | ||||
| 	} | ||||
| 	rc = sqlite3_bind_text(stmt, idx, text, -1, SQLITE_STATIC); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error binding text to SQL parameter %s: %d\n", | ||||
| 		     param_name ? param_name : "#1", rc); | ||||
| 		db_remove_reset(stmt); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /** bind int arg and do proper cleanup in case of failure. If param_name is | ||||
|  * NULL, bind to the first parameter (useful for SQL statements that have only | ||||
|  * one parameter). */ | ||||
| bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr) | ||||
| { | ||||
| 	int rc; | ||||
| 	int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1; | ||||
| 	if (idx < 1) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n", | ||||
| 		     param_name); | ||||
| 		return false; | ||||
| 	} | ||||
| 	rc = sqlite3_bind_int(stmt, idx, nr); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error binding int64 to SQL parameter %s: %d\n", | ||||
| 		     param_name ? param_name : "#1", rc); | ||||
| 		db_remove_reset(stmt); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /** bind int64 arg and do proper cleanup in case of failure. If param_name is | ||||
|  * NULL, bind to the first parameter (useful for SQL statements that have only | ||||
|  * one parameter). */ | ||||
| bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr) | ||||
| { | ||||
| 	int rc; | ||||
| 	int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1; | ||||
| 	if (idx < 1) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n", | ||||
| 		     param_name); | ||||
| 		return false; | ||||
| 	} | ||||
| 	rc = sqlite3_bind_int64(stmt, idx, nr); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Error binding int64 to SQL parameter %s: %d\n", | ||||
| 		     param_name ? param_name : "#1", rc); | ||||
| 		db_remove_reset(stmt); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void db_close(struct db_context *dbc) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	int rc; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) { | ||||
| 		/* it is ok to call finalize on NULL */ | ||||
| 		sqlite3_finalize(dbc->stmt[i]); | ||||
| 	} | ||||
| 	sqlite3_close(dbc->db); | ||||
|  | ||||
| 	/* Ask sqlite3 to close DB */ | ||||
| 	rc = sqlite3_close(dbc->db); | ||||
| 	if (rc != SQLITE_OK) { /* Make sure it's actually closed! */ | ||||
| 		LOGP(DDB, LOGL_ERROR, "Couldn't close database: (rc=%d) %s\n", | ||||
| 			rc, sqlite3_errmsg(dbc->db)); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(dbc); | ||||
| } | ||||
|  | ||||
| struct db_context *db_open(void *ctx, const char *fname) | ||||
| static int db_bootstrap(struct db_context *dbc) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < ARRAY_SIZE(stmt_bootstrap_sql); i++) { | ||||
| 		int rc; | ||||
| 		sqlite3_stmt *stmt; | ||||
| 		rc = sqlite3_prepare_v2(dbc->db, stmt_bootstrap_sql[i], -1, &stmt, NULL); | ||||
| 		if (rc != SQLITE_OK) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_bootstrap_sql[i]); | ||||
| 			return rc; | ||||
| 		} | ||||
|  | ||||
| 		rc = sqlite3_step(stmt); | ||||
| 		db_remove_reset(stmt); | ||||
| 		sqlite3_finalize(stmt); | ||||
| 		if (rc != SQLITE_DONE) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Cannot bootstrap database: SQL error: (%d) %s," | ||||
| 			     " during stmt '%s'", | ||||
| 			     rc, sqlite3_errmsg(dbc->db), stmt_bootstrap_sql[i]); | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
| 	return SQLITE_OK; | ||||
| } | ||||
|  | ||||
| /* https://www.sqlite.org/fileformat2.html#storage_of_the_sql_database_schema */ | ||||
| static bool db_table_exists(struct db_context *dbc, const char *table_name) | ||||
| { | ||||
| 	const char *table_exists_sql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?"; | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = sqlite3_prepare_v2(dbc->db, table_exists_sql, -1, &stmt, NULL); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", table_exists_sql); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (!db_bind_text(stmt, NULL, table_name)) | ||||
| 		return false; | ||||
|  | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	db_remove_reset(stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return (rc == SQLITE_ROW); | ||||
| } | ||||
|  | ||||
| /* Indicate whether the database is initialized with tables for schema version 0. | ||||
|  * We only check for the 'subscriber' table here because Neels said so. */ | ||||
| static bool db_is_bootstrapped_v0(struct db_context *dbc) | ||||
| { | ||||
| 	if (!db_table_exists(dbc, "subscriber")) { | ||||
| 		LOGP(DDB, LOGL_DEBUG, "Table 'subscriber' not found in database '%s'\n", dbc->fname); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static int | ||||
| db_upgrade_v1(struct db_context *dbc) | ||||
| { | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	int rc; | ||||
| 	const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN last_lu_seen TIMESTAMP default NULL"; | ||||
| 	const char *set_schema_version_sql = "PRAGMA user_version = 1"; | ||||
|  | ||||
| 	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_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_get_user_version(struct db_context *dbc) | ||||
| { | ||||
| 	const char *user_version_sql = "PRAGMA user_version"; | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	int version, rc; | ||||
|  | ||||
| 	rc = sqlite3_prepare_v2(dbc->db, user_version_sql, -1, &stmt, NULL); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", user_version_sql); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc == SQLITE_ROW) { | ||||
| 		version = sqlite3_column_int(stmt, 0); | ||||
| 	} else { | ||||
| 		LOGP(DDB, LOGL_ERROR, "SQL statement '%s' failed: %d\n", user_version_sql, rc); | ||||
| 		version = -1; | ||||
| 	} | ||||
|  | ||||
| 	db_remove_reset(stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return version; | ||||
| } | ||||
|  | ||||
| struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging, bool allow_upgrade) | ||||
| { | ||||
| 	struct db_context *dbc = talloc_zero(ctx, struct db_context); | ||||
| 	unsigned int i; | ||||
| 	int rc; | ||||
| 	bool has_sqlite_config_sqllog = false; | ||||
| 	int version; | ||||
|  | ||||
| 	LOGP(DDB, LOGL_NOTICE, "using database: %s\n", fname); | ||||
| 	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()); | ||||
|  | ||||
| #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); | ||||
|  | ||||
| 	for (i = 0; i < 0xfffff; i++) { | ||||
| 		const char *o = sqlite3_compileoption_get(i); | ||||
| 		if (!o) | ||||
| 			break; | ||||
| 		LOGP(DDB, LOGL_DEBUG, "SQlite3 compiled with '%s'\n", o); | ||||
| 		LOGP(DDB, LOGL_DEBUG, "SQLite3 compiled with '%s'\n", o); | ||||
| 		if (!strcmp(o, "ENABLE_SQLLOG")) | ||||
| 			has_sqlite_config_sqllog = true; | ||||
| 	} | ||||
|  | ||||
| 	rc = sqlite3_config(SQLITE_CONFIG_LOG, sql3_error_log_cb, NULL); | ||||
| 	if (rc != SQLITE_OK) | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Unable to set SQlite3 error log callback\n"); | ||||
| 	if (enable_sqlite_logging) { | ||||
| 		rc = sqlite3_config(SQLITE_CONFIG_LOG, sql3_error_log_cb, NULL); | ||||
| 		if (rc != SQLITE_OK) | ||||
| 			LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 error log callback\n"); | ||||
| 	} | ||||
|  | ||||
| 	rc = sqlite3_config(SQLITE_CONFIG_SQLLOG, sql3_sql_log_cb, NULL); | ||||
| 	if (rc != SQLITE_OK) | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Unable to set SQlite3 SQL statement log callback\n"); | ||||
| 	if (has_sqlite_config_sqllog) { | ||||
| 		rc = sqlite3_config(SQLITE_CONFIG_SQLLOG, sql3_sql_log_cb, NULL); | ||||
| 		if (rc != SQLITE_OK) | ||||
| 			LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 SQL log callback\n"); | ||||
| 	} else | ||||
| 			LOGP(DDB, LOGL_DEBUG, "Not setting SQL log callback:" | ||||
| 			     " SQLite3 compiled without support for it\n"); | ||||
|  | ||||
| 	rc = sqlite3_open(dbc->fname, &dbc->db); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| @@ -105,7 +411,7 @@ struct db_context *db_open(void *ctx, const char *fname) | ||||
| 	/* enable extended result codes */ | ||||
| 	rc = sqlite3_extended_result_codes(dbc->db, 1); | ||||
| 	if (rc != SQLITE_OK) | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to enable SQlite3 extended result codes\n"); | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to enable SQLite3 extended result codes\n"); | ||||
|  | ||||
| 	char *err_msg; | ||||
| 	rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg); | ||||
| @@ -113,6 +419,68 @@ struct db_context *db_open(void *ctx, const char *fname) | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n", | ||||
| 			err_msg); | ||||
|  | ||||
| 	version = db_get_user_version(dbc); | ||||
| 	if (version < 0) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to read user version number from database '%s'\n", dbc->fname); | ||||
| 		goto out_free; | ||||
| 	} | ||||
|  | ||||
| 	/* An empty database will always report version zero. */ | ||||
| 	if (version == 0 && !db_is_bootstrapped_v0(dbc)) { | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Missing database tables detected; Bootstrapping database '%s'\n", dbc->fname); | ||||
| 		rc = db_bootstrap(dbc); | ||||
| 		if (rc != SQLITE_OK) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Failed to bootstrap DB: (rc=%d) %s\n", | ||||
| 			     rc, sqlite3_errmsg(dbc->db)); | ||||
| 			goto out_free; | ||||
| 		} | ||||
| 		version = CURRENT_SCHEMA_VERSION; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version); | ||||
|  | ||||
| 	if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) { | ||||
| 		switch (version) { | ||||
| 		case 0: | ||||
| 			rc = db_upgrade_v1(dbc); | ||||
| 			if (rc != SQLITE_DONE) { | ||||
| 				LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 1: (rc=%d) %s\n", | ||||
| 				     rc, sqlite3_errmsg(dbc->db)); | ||||
| 				goto out_free; | ||||
| 			} | ||||
| 			version = 1; | ||||
| 			/* 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 N: ... */ | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n", | ||||
| 		     dbc->fname, version); | ||||
| 	} | ||||
|  | ||||
| 	if (version != CURRENT_SCHEMA_VERSION) { | ||||
| 		if (version < CURRENT_SCHEMA_VERSION) { | ||||
| 			LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version); | ||||
| 			if (!allow_upgrade) { | ||||
| 				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; " | ||||
| 				     "use the --db-upgrade option to allow HLR database upgrades\n", | ||||
| 				     CURRENT_SCHEMA_VERSION); | ||||
| 			} | ||||
| 		} else | ||||
| 			LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version); | ||||
|  | ||||
| 		goto out_free; | ||||
| 	} | ||||
|  | ||||
| 	/* prepare all SQL statements */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) { | ||||
| 		rc = sqlite3_prepare_v2(dbc->db, stmt_sql[i], -1, | ||||
|   | ||||
							
								
								
									
										142
									
								
								src/db.h
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/db.h
									
									
									
									
									
								
							| @@ -3,25 +3,53 @@ | ||||
| #include <stdbool.h> | ||||
| #include <sqlite3.h> | ||||
|  | ||||
| struct hlr; | ||||
|  | ||||
| enum stmt_idx { | ||||
| 	SEL_BY_IMSI	= 0, | ||||
| 	UPD_VLR_BY_ID	= 1, | ||||
| 	UPD_SGSN_BY_ID	= 2, | ||||
| 	AUC_BY_IMSI	= 3, | ||||
| 	AUC_UPD_SQN	= 4, | ||||
| 	UPD_PURGE_CS_BY_IMSI, | ||||
| 	UPD_PURGE_PS_BY_IMSI, | ||||
| 	_NUM_STMT | ||||
| 	DB_STMT_SEL_BY_IMSI, | ||||
| 	DB_STMT_SEL_BY_MSISDN, | ||||
| 	DB_STMT_SEL_BY_ID, | ||||
| 	DB_STMT_SEL_BY_IMEI, | ||||
| 	DB_STMT_UPD_VLR_BY_ID, | ||||
| 	DB_STMT_UPD_SGSN_BY_ID, | ||||
| 	DB_STMT_UPD_IMEI_BY_IMSI, | ||||
| 	DB_STMT_AUC_BY_IMSI, | ||||
| 	DB_STMT_AUC_UPD_SQN, | ||||
| 	DB_STMT_UPD_PURGE_CS_BY_IMSI, | ||||
| 	DB_STMT_UPD_PURGE_PS_BY_IMSI, | ||||
| 	DB_STMT_UPD_NAM_PS_BY_IMSI, | ||||
| 	DB_STMT_UPD_NAM_CS_BY_IMSI, | ||||
| 	DB_STMT_SUBSCR_CREATE, | ||||
| 	DB_STMT_DEL_BY_ID, | ||||
| 	DB_STMT_SET_MSISDN_BY_IMSI, | ||||
| 	DB_STMT_DELETE_MSISDN_BY_IMSI, | ||||
| 	DB_STMT_AUC_2G_INSERT, | ||||
| 	DB_STMT_AUC_2G_DELETE, | ||||
| 	DB_STMT_AUC_3G_INSERT, | ||||
| 	DB_STMT_AUC_3G_DELETE, | ||||
| 	DB_STMT_SET_LAST_LU_SEEN, | ||||
| 	DB_STMT_EXISTS_BY_IMSI, | ||||
| 	DB_STMT_EXISTS_BY_MSISDN, | ||||
| 	_NUM_DB_STMT | ||||
| }; | ||||
|  | ||||
| struct db_context { | ||||
| 	char *fname; | ||||
| 	sqlite3 *db; | ||||
| 	sqlite3_stmt *stmt[_NUM_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); | ||||
| 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_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr); | ||||
| void db_close(struct db_context *dbc); | ||||
| struct db_context *db_open(void *ctx, const char *fname); | ||||
| struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging, bool allow_upgrades); | ||||
|  | ||||
| #include <osmocom/crypt/auth.h> | ||||
|  | ||||
| @@ -29,14 +57,15 @@ struct db_context *db_open(void *ctx, const char *fname); | ||||
| int db_get_auth_data(struct db_context *dbc, const char *imsi, | ||||
| 		     struct osmo_sub_auth_data *aud2g, | ||||
| 		     struct osmo_sub_auth_data *aud3g, | ||||
| 		     uint64_t *suscr_id); | ||||
| 		     int64_t *subscr_id); | ||||
|  | ||||
| int db_update_sqn(struct db_context *dbc, uint64_t id, | ||||
| int db_update_sqn(struct db_context *dbc, int64_t id, | ||||
| 		      uint64_t new_sqn); | ||||
|  | ||||
| int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| 	    struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| 	    const uint8_t *rand_auts, const uint8_t *auts); | ||||
| 	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec, | ||||
| 	       unsigned int num_vec, const uint8_t *rand_auts, | ||||
| 	       const uint8_t *auts); | ||||
|  | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/gsm/protocol/gsm_23_003.h> | ||||
| @@ -47,12 +76,13 @@ int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| struct hlr_subscriber { | ||||
| 	struct llist_head list; | ||||
|  | ||||
| 	uint64_t	id; | ||||
| 	int64_t		id; | ||||
| 	char		imsi[GSM23003_IMSI_MAX_DIGITS+1]; | ||||
| 	char		msisdn[GT_MAX_DIGITS+1]; | ||||
| 	char		msisdn[GSM23003_MSISDN_MAX_DIGITS+1]; | ||||
| 	/* imeisv? */ | ||||
| 	char		vlr_number[GT_MAX_DIGITS+1]; | ||||
| 	char		sgsn_number[GT_MAX_DIGITS+1]; | ||||
| 	char		imei[GSM23003_IMEI_NUM_DIGITS+1]; | ||||
| 	char		vlr_number[32]; | ||||
| 	char		sgsn_number[32]; | ||||
| 	char		sgsn_address[GT_MAX_DIGITS+1]; | ||||
| 	/* ggsn number + address */ | ||||
| 	/* gmlc number */ | ||||
| @@ -64,15 +94,75 @@ struct hlr_subscriber { | ||||
| 	uint32_t	lmsi; | ||||
| 	bool		ms_purged_cs; | ||||
| 	bool		ms_purged_ps; | ||||
| 	time_t		last_lu_seen; | ||||
| }; | ||||
|  | ||||
| int db_subscr_get(struct db_context *dbc, const char *imsi, | ||||
| 		  struct hlr_subscriber *subscr); | ||||
| /* A format string for use with strptime(3). This format string is | ||||
|  * used to parse the last_lu_seen column stored in the HLR database. | ||||
|  * See https://sqlite.org/lang_datefunc.html, function datetime(). */ | ||||
| #define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S" | ||||
|  | ||||
| int db_subscr_lu(struct db_context *dbc, | ||||
| 		 const struct hlr_subscriber *subscr, | ||||
| 		 const char *vlr_or_sgsn_number, | ||||
| 		 bool lu_is_ps); | ||||
| /* Like struct osmo_sub_auth_data, but the keys are in hexdump representation. | ||||
|  * This is useful because SQLite requires them in hexdump format, and callers | ||||
|  * like the VTY and CTRL interface also have them available as hexdump to begin | ||||
|  * with. In the binary format, a VTY command would first need to hexparse, | ||||
|  * after which the db function would again hexdump, copying to separate | ||||
|  * buffers. The roundtrip can be saved by providing char* to begin with. */ | ||||
| struct sub_auth_data_str { | ||||
| 	enum osmo_sub_auth_type type; | ||||
| 	enum osmo_auth_algo algo; | ||||
| 	union { | ||||
| 		struct { | ||||
| 			const char *opc; | ||||
| 			const char *k; | ||||
| 			uint64_t sqn; | ||||
| 			int opc_is_op; | ||||
| 			unsigned int ind_bitlen; | ||||
| 		} umts; | ||||
| 		struct { | ||||
| 			const char *ki; | ||||
| 		} gsm; | ||||
| 	} u; | ||||
| }; | ||||
|  | ||||
| int db_subscr_purge(struct db_context *dbc, | ||||
| 		const char *imsi, bool is_ps); | ||||
| #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_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi, | ||||
| 				    const char *msisdn); | ||||
| int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id, | ||||
| 			       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, | ||||
| 			  struct hlr_subscriber *subscr); | ||||
| int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn, | ||||
| 			    struct hlr_subscriber *subscr); | ||||
| int db_subscr_get_by_id(struct db_context *dbc, int64_t id, | ||||
| 			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_lu(struct db_context *dbc, int64_t subscr_id, | ||||
| 		 const char *vlr_or_sgsn_number, bool is_ps); | ||||
|  | ||||
| int db_subscr_purge(struct db_context *dbc, const char *by_imsi, | ||||
| 		    bool purge_val, bool is_ps); | ||||
|  | ||||
| int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps); | ||||
|  | ||||
| /*! Call sqlite3_column_text() and copy result to a char[]. | ||||
|  * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target. | ||||
|  * \param[in] stmt  An sqlite3_stmt*. | ||||
|  * \param[in] idx   Index in stmt's returned columns. | ||||
|  */ | ||||
| #define copy_sqlite3_text_to_buf(buf, stmt, idx) \ | ||||
| 	do { \ | ||||
| 		const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \ | ||||
| 		osmo_strlcpy(buf, _txt, sizeof(buf)); \ | ||||
| 	} while (0) | ||||
|   | ||||
							
								
								
									
										192
									
								
								src/db_auc.c
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								src/db_auc.c
									
									
									
									
									
								
							| @@ -18,6 +18,8 @@ | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/crypt/auth.h> | ||||
| @@ -29,80 +31,102 @@ | ||||
| #include "auc.h" | ||||
| #include "rand.h" | ||||
|  | ||||
| #define LOGAUC(imsi, level, fmt, args ...)	LOGP(DAUC, level, "%s: " fmt, imsi, ## args) | ||||
| #define LOGAUC(imsi, level, fmt, args ...)	LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args) | ||||
|  | ||||
| /* update the SQN for a given subscriber ID */ | ||||
| int db_update_sqn(struct db_context *dbc, uint64_t id, | ||||
| 		      uint64_t new_sqn) | ||||
| int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn) | ||||
| { | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[AUC_UPD_SQN]; | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_UPD_SQN]; | ||||
| 	int rc; | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	/* bind new SQN and subscriber ID */ | ||||
| 	rc = sqlite3_bind_int64(stmt, 1, new_sqn); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Error binding SQN: %d\n", rc); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!db_bind_int64(stmt, "$sqn", new_sqn)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	rc = sqlite3_bind_int64(stmt, 2, id); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Error binding Subscrber ID: %d\n", rc); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!db_bind_int64(stmt, "$subscriber_id", subscr_id)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	/* execute the statement */ | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc != SQLITE_DONE) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Error updating SQN: %d\n", rc); | ||||
| 		return -2; | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64 | ||||
| 		     ": SQL error: (%d) %s\n", | ||||
| 		     subscr_id, rc, sqlite3_errmsg(dbc->db)); | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* remove bindings and reset statement to be re-executed */ | ||||
| 	rc = sqlite3_clear_bindings(stmt); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Error clerearing bindings: %d\n", rc); | ||||
| 	} | ||||
| 	rc = sqlite3_reset(stmt); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Error in sqlite3_reset: %d\n", rc); | ||||
| 	/* verify execution result */ | ||||
| 	rc = sqlite3_changes(dbc->db); | ||||
| 	if (!rc) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64 | ||||
| 		     ": no auc_3g entry for such subscriber\n", subscr_id); | ||||
| 		ret = -ENOENT; | ||||
| 	} else if (rc != 1) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%" PRId64 | ||||
| 		     ": SQL modified %d rows (expected 1)\n", subscr_id, rc); | ||||
| 		ret = -EIO; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	db_remove_reset(stmt); | ||||
| 	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 | ||||
|  * returns -1 in case of error, 0 for unknown IMSI, 1 for success */ | ||||
|  * 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, | ||||
|  * -EIO on db failure */ | ||||
| int db_get_auth_data(struct db_context *dbc, const char *imsi, | ||||
| 		     struct osmo_sub_auth_data *aud2g, | ||||
| 		     struct osmo_sub_auth_data *aud3g, | ||||
| 		     uint64_t *subscr_id) | ||||
| 		     int64_t *subscr_id) | ||||
| { | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[AUC_BY_IMSI]; | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_BY_IMSI]; | ||||
| 	int ret = 0; | ||||
| 	int rc; | ||||
|  | ||||
| 	memset(aud2g, 0, sizeof(*aud2g)); | ||||
| 	memset(aud3g, 0, sizeof(*aud3g)); | ||||
|  | ||||
| 	/* bind the IMSI value */ | ||||
| 	rc = sqlite3_bind_text(stmt, 1, imsi, -1, | ||||
| 				SQLITE_STATIC); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGAUC(imsi, LOGL_ERROR, "Error binding IMSI: %d\n", rc); | ||||
| 		ret = -1; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (!db_bind_text(stmt, "$imsi", imsi)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	/* execute the statement */ | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc == SQLITE_DONE) { | ||||
| 		LOGAUC(imsi, LOGL_INFO, "Unknown\n"); | ||||
| 		ret = 0; | ||||
| 		LOGAUC(imsi, LOGL_INFO, "No such subscriber\n"); | ||||
| 		ret = -ENOENT; | ||||
| 		goto out; | ||||
| 	} else if (rc != SQLITE_ROW) { | ||||
| 		LOGAUC(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc); | ||||
| 		ret = -1; | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| @@ -115,89 +139,78 @@ int db_get_auth_data(struct db_context *dbc, const char *imsi, | ||||
| 	/* obtain result values using sqlite3_column_*() */ | ||||
| 	if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) { | ||||
| 		/* we do have some 2G authentication data */ | ||||
| 		const uint8_t *ki; | ||||
|  | ||||
| 		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); | ||||
| 		if (hexparse_stmt(aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki), stmt, 2, "Ki", imsi)) | ||||
| 			goto end_2g; | ||||
| 		} | ||||
| #endif | ||||
| 		osmo_hexparse(ki, &aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki)); | ||||
| 		aud2g->algo = sqlite3_column_int(stmt, 1); | ||||
| 		aud2g->type = OSMO_AUTH_TYPE_GSM; | ||||
| 	} else | ||||
| 		LOGAUC(imsi, LOGL_DEBUG, "No 2G Auth Data\n"); | ||||
| //end_2g: | ||||
| end_2g: | ||||
| 	if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) { | ||||
| 		/* we do have some 3G authentication data */ | ||||
| 		const uint8_t *k, *op, *opc; | ||||
|  | ||||
| 		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); | ||||
| 		if (hexparse_stmt(aud3g->u.umts.k, sizeof(aud3g->u.umts.k), stmt, 4, "K", imsi)) { | ||||
| 			ret = -EIO; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		osmo_hexparse(k, &aud3g->u.umts.k, sizeof(aud3g->u.umts.k)); | ||||
| 		aud3g->algo = sqlite3_column_int(stmt, 3); | ||||
|  | ||||
| 		/* UMTS Subscribers can have either OP or OPC */ | ||||
| 		op = sqlite3_column_text(stmt, 5); | ||||
| 		if (!op) { | ||||
| 			opc = sqlite3_column_text(stmt, 6); | ||||
| 			if (!opc) { | ||||
| 				LOGAUC(imsi, LOGL_ERROR, "Error reading OPC: %d\n", rc); | ||||
| 		if (sqlite3_column_text(stmt, 5)) { | ||||
| 			if (hexparse_stmt(aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc), stmt, 5, "OP", imsi)) { | ||||
| 				ret = -EIO; | ||||
| 				goto out; | ||||
| 			} | ||||
| 			osmo_hexparse(opc, &aud3g->u.umts.opc, | ||||
| 					sizeof(aud3g->u.umts.opc)); | ||||
| 			aud3g->u.umts.opc_is_op = 0; | ||||
| 		} else { | ||||
| 			osmo_hexparse(op, &aud3g->u.umts.opc, | ||||
| 					sizeof(aud3g->u.umts.opc)); | ||||
| 			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.ind_bitlen = sqlite3_column_int(stmt, 8); | ||||
| 		/* FIXME: amf? */ | ||||
| 		aud3g->type = OSMO_AUTH_TYPE_UMTS; | ||||
| 	} else | ||||
| 		LOGAUC(imsi, LOGL_DEBUG, "No 3G Auth Data\n"); | ||||
|  | ||||
| 	if (aud2g->type == 0 && aud3g->type == 0) | ||||
| 		ret = -1; | ||||
| 	else | ||||
| 		ret = 1; | ||||
| 		ret = -ENOKEY; | ||||
|  | ||||
| out: | ||||
| 	/* remove bindings and reset statement to be re-executed */ | ||||
| 	rc = sqlite3_clear_bindings(stmt); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGAUC(imsi, LOGL_ERROR, "Error in sqlite3_clear_bindings(): %d\n", rc); | ||||
| 	} | ||||
| 	rc = sqlite3_reset(stmt); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGAUC(imsi, LOGL_ERROR, "Error in sqlite3_reset(): %d\n", rc); | ||||
| 	} | ||||
|  | ||||
| 	db_remove_reset(stmt); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| /* return -1 in case of error, 0 for unknown imsi, positive for number | ||||
|  * of vectors generated */ | ||||
| /* return number of vectors generated, negative value on error: | ||||
|  * -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data, | ||||
|  * -EIO on db failure */ | ||||
| int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| 	    struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| 	    const uint8_t *rand_auts, const uint8_t *auts) | ||||
| 	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec, | ||||
| 	       unsigned int num_vec, const uint8_t *rand_auts, | ||||
| 	       const uint8_t *auts) | ||||
| { | ||||
| 	struct osmo_sub_auth_data aud2g, aud3g; | ||||
| 	uint64_t subscr_id; | ||||
| 	int64_t subscr_id; | ||||
| 	int ret = 0; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_get_auth_data(dbc, imsi, &aud2g, &aud3g, &subscr_id); | ||||
| 	if (rc <= 0) | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
|  | ||||
| 	LOGAUC(imsi, LOGL_INFO, "Calling to generate %u vectors\n", num_vec); | ||||
| 	aud3g.u.umts.ind = auc_3g_ind; | ||||
| 	if (aud3g.type == OSMO_AUTH_TYPE_UMTS | ||||
| 	    && aud3g.u.umts.ind >= (1U << aud3g.u.umts.ind_bitlen)) { | ||||
| 		LOGAUC(imsi, LOGL_NOTICE, "3G auth: SQN's IND bitlen %u is" | ||||
| 		       " too small to hold an index of %u. Truncating. This" | ||||
| 		       " may cause numerous additional AUTS resyncing.\n", | ||||
| 		       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind); | ||||
| 		aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1; | ||||
| 	} | ||||
|  | ||||
| 	LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec); | ||||
| 	rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts); | ||||
| 	if (rc < 0) { | ||||
| 		num_vec = 0; | ||||
| @@ -210,7 +223,8 @@ int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
|  | ||||
| 	/* Update SQN in database, as needed */ | ||||
| 	if (aud3g.algo) { | ||||
| 		LOGAUC(imsi, LOGL_DEBUG, "Updating SQN in DB\n"); | ||||
| 		LOGAUC(imsi, LOGL_DEBUG, "Updating SQN=%" PRIu64 " in DB\n", | ||||
| 		       aud3g.u.umts.sqn); | ||||
| 		rc = db_update_sqn(dbc, subscr_id, aud3g.u.umts.sqn); | ||||
| 		/* don't tell caller we generated any triplets in case of | ||||
| 		 * update error */ | ||||
|   | ||||
							
								
								
									
										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); | ||||
| } | ||||
							
								
								
									
										949
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										949
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										25
									
								
								src/db_sql2c.sed
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/db_sql2c.sed
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # Input to this are sql/*.sql files. | ||||
| # | ||||
| # We want each SQL statement line wrapped in "...\n", and each end (";") to | ||||
| # become a comma: | ||||
| # | ||||
| #   SOME SQL COMMAND ( | ||||
| #     that may span ) | ||||
| #   MULTIPLE LINES; | ||||
| #   MORE; | ||||
| # | ||||
| # --> | ||||
| # | ||||
| #   "SOME SQL COMMAND (\n" | ||||
| #   "  that may span )\n" | ||||
| #   "MULTIPLE LINES\n",   <--note the comma here | ||||
| #   "MORE\n", | ||||
| # | ||||
| # just replacing ';' with '\n,' won't work, since sed is bad in printing | ||||
| # multiple lines. Also, how to input newlines to sed is not portable across | ||||
| # platforms. | ||||
|  | ||||
| # Match excluding a trailing ';' as \1, keep any trailing ';' in \2 | ||||
| s/^\(.*[^;]\)\(;\|\)$/"\1\\n"\2/ | ||||
| # Replace trailing ';' as ',' | ||||
| s/;$/,/ | ||||
| @@ -1,84 +0,0 @@ | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/application.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "rand.h" | ||||
| #include "logging.h" | ||||
|  | ||||
| static struct db_context *g_dbc; | ||||
|  | ||||
| static int test(const char *imsi) | ||||
| { | ||||
| 	struct osmo_auth_vector vec[3]; | ||||
| 	int rc, i; | ||||
|  | ||||
| 	/* initialize all vectors with a known token pattern */ | ||||
| 	memset(vec, 0x55, sizeof(vec)); | ||||
| 	for (i = 0; i < ARRAY_SIZE(vec); i++) | ||||
| 		vec[i].res_len = 0; | ||||
|  | ||||
| 	rc = db_get_auc(g_dbc, imsi, vec, ARRAY_SIZE(vec), NULL, NULL); | ||||
| 	if (rc <= 0) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "Cannot obtain auth tuples for '%s'\n", imsi); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	LOGP(DMAIN, LOGL_INFO, "Obtained %u tuples for subscriber IMSI %s\n", | ||||
| 		rc, imsi); | ||||
|  | ||||
| 	for (i = 0; i < rc; i++) { | ||||
| 		struct osmo_auth_vector *v = vec + i; | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "Tuple %u, auth_types=0x%x\n", i, v->auth_types); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "RAND=%s\n", osmo_hexdump_nospc(v->rand, sizeof(v->rand))); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "AUTN=%s\n", osmo_hexdump_nospc(v->autn, sizeof(v->autn))); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "CK=%s\n", osmo_hexdump_nospc(v->ck, sizeof(v->ck))); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "IK=%s\n", osmo_hexdump_nospc(v->ik, sizeof(v->ik))); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "RES=%s\n", osmo_hexdump_nospc(v->res, v->res_len)); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "Kc=%s\n", osmo_hexdump_nospc(v->kc, sizeof(v->kc))); | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "SRES=%s\n", osmo_hexdump_nospc(v->sres, sizeof(v->sres))); | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = osmo_init_logging(&hlr_log_info); | ||||
| 	if (rc < 0) { | ||||
| 		fprintf(stderr, "Error initializing logging\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n"); | ||||
|  | ||||
| 	rc = rand_init(); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "Error initializing random source\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	g_dbc = db_open(NULL, "hlr.db"); | ||||
| 	if (!g_dbc) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "Error opening database\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	/* non-existing subscriber */ | ||||
| 	rc = test("901990123456789"); | ||||
| 	/* 2G only AUC data (COMP128v1 / MILENAGE) */ | ||||
| 	rc = test("901990000000001"); | ||||
| 	/* 2G + 3G AUC data (COMP128v1 / MILENAGE) */ | ||||
| 	rc = test("901990000000002"); | ||||
| 	/* 3G AUC data (MILENAGE) */ | ||||
| 	rc = test("901990000000003"); | ||||
|  | ||||
| 	LOGP(DMAIN, LOGL_NOTICE, "Exiting\n"); | ||||
|  | ||||
| 	db_close(g_dbc); | ||||
|  | ||||
| 	log_fini(); | ||||
|  | ||||
| 	exit(0); | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/dbd_decode_binary.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/dbd_decode_binary.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* This function is blatantly copied from libdbi, from | ||||
|  * https://sourceforge.net/p/libdbi/libdbi/ci/master/tree/src/dbd_helper.c | ||||
|  * to save having to depend on the entire libdbi just for KI BLOB decoding. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * libdbi - database independent abstraction layer for C. | ||||
|  * Copyright (C) 2001-2003, David Parker and Mark Tobenkin. | ||||
|  * http://libdbi.sourceforge.net | ||||
|  *  | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  *  | ||||
|  * This library 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 | ||||
|  * Lesser General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  *  | ||||
|  * $Id: dbd_helper.c,v 1.44 2011/08/09 11:14:14 mhoenicka Exp $ | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out){ | ||||
|   int i, e; | ||||
|   unsigned char c; | ||||
|   e = *(in++); | ||||
|   i = 0; | ||||
|   while( (c = *(in++))!=0 ){ | ||||
|     if( c==1 ){ | ||||
|       c = *(in++) - 1; | ||||
|     } | ||||
|     out[i++] = c + e; | ||||
|   } | ||||
|   return (size_t)i; | ||||
| } | ||||
| @@ -23,16 +23,17 @@ | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
|  | ||||
| struct gsup_route { | ||||
| 	struct llist_head list; | ||||
|  | ||||
| 	uint8_t *addr; | ||||
| 	struct osmo_gsup_conn *conn; | ||||
| }; | ||||
|  | ||||
| /* find a route for the given address */ | ||||
| /*! Find a route for the given address. | ||||
|  * \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). | ||||
|  */ | ||||
| struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs, | ||||
| 					const uint8_t *addr, size_t addrlen) | ||||
| { | ||||
| @@ -46,6 +47,22 @@ struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs, | ||||
| 	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 */ | ||||
| int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen) | ||||
| { | ||||
| @@ -60,6 +77,8 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr | ||||
| 	if (!gr) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	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->conn = conn; | ||||
| 	llist_add_tail(&gr->list, &conn->server->routes); | ||||
| @@ -75,6 +94,8 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn) | ||||
|  | ||||
| 	llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) { | ||||
| 		if (gr->conn == conn) { | ||||
| 			LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n", | ||||
| 			     gr->addr); | ||||
| 			llist_del(&gr->list); | ||||
| 			talloc_free(gr); | ||||
| 			num_deleted++; | ||||
|   | ||||
| @@ -1,8 +1,26 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.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, | ||||
| 					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 */ | ||||
| int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen); | ||||
|  | ||||
| /* delete all routes for the given connection */ | ||||
| int gsup_route_del_conn(struct osmo_gsup_conn *conn); | ||||
|  | ||||
| int osmo_gsup_addr_send(struct osmo_gsup_server *gs, | ||||
| 			const uint8_t *addr, size_t addrlen, | ||||
| 			struct msgb *msg); | ||||
|   | ||||
							
								
								
									
										52
									
								
								src/gsup_send.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/gsup_send.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /* This is kept separate to be able to override the actual sending functions from unit tests. */ | ||||
|  | ||||
| #include <errno.h> | ||||
|  | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| /*! 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, | ||||
| 			const uint8_t *addr, size_t addrlen, | ||||
| 			struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_gsup_conn *conn; | ||||
|  | ||||
| 	conn = gsup_route_find(gs, addr, addrlen); | ||||
| 	if (!conn) { | ||||
| 		DEBUGP(DLGSUP, "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen)); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
|  | ||||
| 	return osmo_gsup_conn_send(conn, msg); | ||||
| } | ||||
|  | ||||
| @@ -24,8 +24,11 @@ | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/abis/ipa.h> | ||||
| #include <osmocom/abis/ipaccess.h> | ||||
| #include <osmocom/gsm/gsm48_ie.h> | ||||
| #include <osmocom/gsm/apn.h> | ||||
|  | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
|  | ||||
| static void osmo_gsup_server_send(struct osmo_gsup_conn *conn, | ||||
| 			     int proto_ext, struct msgb *msg_tx) | ||||
| @@ -50,9 +53,9 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg) | ||||
| static int osmo_gsup_conn_oap_handle(struct osmo_gsup_conn *conn, | ||||
| 				struct msgb *msg_rx) | ||||
| { | ||||
| #if 0 | ||||
| 	int rc; | ||||
| 	struct msgb *msg_tx; | ||||
| #if 0 | ||||
| 	rc = oap_handle(&conn->oap_state, msg_rx, &msg_tx); | ||||
| 	msgb_free(msg_rx); | ||||
| 	if (rc < 0) | ||||
| @@ -173,7 +176,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn, | ||||
| 				   struct ipaccess_unit *unit) | ||||
| { | ||||
| 	struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data; | ||||
| 	uint8_t *addr; | ||||
| 	uint8_t *addr = NULL; | ||||
| 	size_t addr_len; | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n"); | ||||
| @@ -184,9 +187,15 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn, | ||||
| 	osmo_tlvp_dump(tlvp, DLGSUP, LOGL_INFO); | ||||
|  | ||||
| 	addr_len = osmo_gsup_conn_ccm_get(clnt, &addr, IPAC_IDTAG_SERNR); | ||||
| 	if (addr_len) | ||||
| 		gsup_route_add(clnt, addr, addr_len); | ||||
| 	if (addr_len <= 0) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "GSUP client %s:%u has no %s IE and" | ||||
| 		     " cannot be routed\n", | ||||
| 		     conn->addr, conn->port, | ||||
| 		     ipa_ccm_idtag_name(IPAC_IDTAG_SERNR)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	gsup_route_add(clnt, addr, addr_len); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -204,6 +213,43 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Add conn to the clients list in a way that conn->auc_3g_ind takes the lowest | ||||
|  * unused integer and the list of clients remains sorted by auc_3g_ind. | ||||
|  * Keep this function non-static to allow linking in a unit test. */ | ||||
| void osmo_gsup_server_add_conn(struct llist_head *clients, | ||||
| 			       struct osmo_gsup_conn *conn) | ||||
| { | ||||
| 	struct osmo_gsup_conn *c; | ||||
| 	struct osmo_gsup_conn *prev_conn; | ||||
|  | ||||
| 	c = llist_first_entry_or_null(clients, struct osmo_gsup_conn, list); | ||||
|  | ||||
| 	/* Is the first index, 0, unused? */ | ||||
| 	if (!c || c->auc_3g_ind > 0) { | ||||
| 		conn->auc_3g_ind = 0; | ||||
| 		llist_add(&conn->list, clients); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Look for a gap later on */ | ||||
| 	prev_conn = NULL; | ||||
| 	llist_for_each_entry(c, clients, list) { | ||||
| 		/* skip first item, we know it has auc_3g_ind == 0. */ | ||||
| 		if (!prev_conn) { | ||||
| 			prev_conn = c; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (c->auc_3g_ind > prev_conn->auc_3g_ind + 1) | ||||
| 			break; | ||||
| 		prev_conn = c; | ||||
| 	} | ||||
|  | ||||
| 	OSMO_ASSERT(prev_conn); | ||||
|  | ||||
| 	conn->auc_3g_ind = prev_conn->auc_3g_ind + 1; | ||||
| 	llist_add(&conn->list, &prev_conn->list); | ||||
| } | ||||
|  | ||||
| /* a client has connected to the server socket and we have accept()ed it */ | ||||
| static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd) | ||||
| { | ||||
| @@ -218,15 +264,15 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd) | ||||
| 	conn->conn = ipa_server_conn_create(gsups, link, fd, | ||||
| 					   osmo_gsup_server_read_cb, | ||||
| 					   osmo_gsup_server_closed_cb, conn); | ||||
| 	conn->conn->ccm_cb = osmo_gsup_server_ccm_cb; | ||||
| 	OSMO_ASSERT(conn->conn); | ||||
| 	conn->conn->ccm_cb = osmo_gsup_server_ccm_cb; | ||||
|  | ||||
| 	/* link data structure with server structure */ | ||||
| 	conn->server = gsups; | ||||
| 	llist_add_tail(&conn->list, &gsups->clients); | ||||
| 	osmo_gsup_server_add_conn(&gsups->clients, conn); | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d\n", | ||||
| 		conn->conn->addr, conn->conn->port); | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n", | ||||
| 	     conn->conn->addr, conn->conn->port, conn->auc_3g_ind); | ||||
|  | ||||
| 	/* request the identity of the client */ | ||||
| 	rc = ipa_ccm_send_id_req(fd); | ||||
| @@ -244,9 +290,9 @@ failed: | ||||
| } | ||||
|  | ||||
| struct osmo_gsup_server * | ||||
| osmo_gsup_server_create(void *ctx, const char *ip_addr, | ||||
| 			uint16_t tcp_port, | ||||
| 			osmo_gsup_read_cb_t read_cb) | ||||
| osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port, | ||||
| 			osmo_gsup_read_cb_t read_cb, | ||||
| 			struct llist_head *lu_op_lst, void *priv) | ||||
| { | ||||
| 	struct osmo_gsup_server *gsups; | ||||
| 	int rc; | ||||
| @@ -266,11 +312,14 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, | ||||
| 		goto failed; | ||||
|  | ||||
| 	gsups->read_cb = read_cb; | ||||
| 	gsups->priv = priv; | ||||
|  | ||||
| 	rc = ipa_server_link_open(gsups->link); | ||||
| 	if (rc < 0) | ||||
| 		goto failed; | ||||
|  | ||||
| 	gsups->luop = lu_op_lst; | ||||
|  | ||||
| 	return gsups; | ||||
|  | ||||
| failed: | ||||
| @@ -287,3 +336,79 @@ void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups) | ||||
| 	} | ||||
| 	talloc_free(gsups); | ||||
| } | ||||
|  | ||||
| /* Set GSUP message's pdp_infos[0] to a wildcard APN. | ||||
|  * Use the provided apn_buf to store the produced APN data. This must remain valid until | ||||
|  * osmo_gsup_encode() is done. Return 0 if an entry was added, -ENOMEM if the provided buffer is too | ||||
|  * small. */ | ||||
| int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup, | ||||
| 				     uint8_t *apn_buf, size_t apn_buf_size) | ||||
| { | ||||
| 	int l; | ||||
|  | ||||
| 	l = osmo_apn_from_str(apn_buf, apn_buf_size, "*"); | ||||
| 	if (l <= 0) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	gsup->pdp_infos[0].apn_enc = apn_buf; | ||||
| 	gsup->pdp_infos[0].apn_enc_len = l; | ||||
| 	gsup->pdp_infos[0].have_info = 1; | ||||
| 	gsup->num_pdp_infos = 1; | ||||
| 	/* FIXME: use real value: */ | ||||
| 	gsup->pdp_infos[0].context_id = 1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Populate a gsup message structure with an Insert Subscriber Data Message. | ||||
|  * All required memory buffers for data pointed to by pointers in struct omso_gsup_message | ||||
|  * must be allocated by the caller and should have the same lifetime as the gsup parameter. | ||||
|  * | ||||
|  * \param[out] gsup  The gsup message to populate. | ||||
|  * \param[in] imsi  The subscriber's IMSI. | ||||
|  * \param[in] msisdn The subscriber's MSISDN. | ||||
|  * \param[out] msisdn_enc A buffer large enough to store the MSISDN in encoded form. | ||||
|  * \param[in] msisdn_enc_size Size of the buffer (must be >= OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN). | ||||
|  * \param[out] apn_buf A buffer large enough to store an APN (required if cn_domain is OSMO_GSUP_CN_DOMAIN_PS). | ||||
|  * \param[in] apn_buf_size Size of APN buffer (must be >= APN_MAXLEN). | ||||
|  * \param[in] cn_domain The CN Domain of the subscriber connection. | ||||
|  * \returns 0 on success, and negative on error. | ||||
|  */ | ||||
| int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn, | ||||
| 						uint8_t *msisdn_enc, size_t msisdn_enc_size, | ||||
| 						uint8_t *apn_buf, size_t apn_buf_size, | ||||
| 						enum osmo_gsup_cn_domain cn_domain) | ||||
| { | ||||
| 	int len; | ||||
|  | ||||
| 	OSMO_ASSERT(gsup); | ||||
|  | ||||
| 	gsup->message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST; | ||||
| 	osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi)); | ||||
|  | ||||
| 	if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN) | ||||
| 		return -ENOSPC; | ||||
|  | ||||
| 	OSMO_ASSERT(msisdn_enc); | ||||
| 	len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn); | ||||
| 	if (len < 1) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn); | ||||
| 		return -ENOSPC; | ||||
| 	} | ||||
| 	gsup->msisdn_enc = msisdn_enc; | ||||
| 	gsup->msisdn_enc_len = len; | ||||
|  | ||||
| 	#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc" | ||||
|  | ||||
| 	gsup->cn_domain = cn_domain; | ||||
| 	if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) { | ||||
| 		OSMO_ASSERT(apn_buf_size >= APN_MAXLEN); | ||||
| 		OSMO_ASSERT(apn_buf); | ||||
| 		/* FIXME: PDP infos - use more fine-grained access control | ||||
| 		   instead of wildcard APN */ | ||||
| 		osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,11 @@ | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/abis/ipa.h> | ||||
| #include <osmocom/abis/ipaccess.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN | ||||
| #define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN	43 /* TS 24.008 10.5.4.7 */ | ||||
| #endif | ||||
|  | ||||
| struct osmo_gsup_conn; | ||||
|  | ||||
| @@ -11,9 +16,15 @@ struct osmo_gsup_conn; | ||||
| typedef int (*osmo_gsup_read_cb_t)(struct osmo_gsup_conn *conn, struct msgb *msg); | ||||
|  | ||||
| struct osmo_gsup_server { | ||||
| 	/* private data of the application/user */ | ||||
| 	void *priv; | ||||
|  | ||||
| 	/* list of osmo_gsup_conn */ | ||||
| 	struct llist_head clients; | ||||
|  | ||||
| 	/* lu_operations list */ | ||||
| 	struct llist_head *luop; | ||||
|  | ||||
| 	struct ipa_server_link *link; | ||||
| 	osmo_gsup_read_cb_t read_cb; | ||||
| 	struct llist_head routes; | ||||
| @@ -28,15 +39,31 @@ struct osmo_gsup_conn { | ||||
| 	struct ipa_server_conn *conn; | ||||
| 	//struct oap_state oap_state; | ||||
| 	struct tlv_parsed ccm; | ||||
|  | ||||
| 	unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */ | ||||
|  | ||||
| 	/* Set when Location Update is received: */ | ||||
| 	bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */ | ||||
| 	bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */ | ||||
| }; | ||||
|  | ||||
|  | ||||
| int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg); | ||||
| int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr, | ||||
| 			   uint8_t tag); | ||||
|  | ||||
| struct osmo_gsup_server *osmo_gsup_server_create(void *ctx, | ||||
| 					const char *ip_addr, | ||||
| 					uint16_t tcp_port, | ||||
| 					osmo_gsup_read_cb_t read_cb); | ||||
| 						 const char *ip_addr, | ||||
| 						 uint16_t tcp_port, | ||||
| 						 osmo_gsup_read_cb_t read_cb, | ||||
| 						 struct llist_head *lu_op_lst, | ||||
| 						 void *priv); | ||||
|  | ||||
| void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups); | ||||
|  | ||||
| int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup, | ||||
| 				     uint8_t *apn_buf, size_t apn_buf_size); | ||||
| int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn, | ||||
| 					    uint8_t *msisdn_enc, size_t msisdn_enc_size, | ||||
| 				            uint8_t *apn_buf, size_t apn_buf_size, | ||||
| 					    enum osmo_gsup_cn_domain cn_domain); | ||||
|   | ||||
							
								
								
									
										20
									
								
								src/gsupclient/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/gsupclient/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # This is _NOT_ the library release version, it's an API version. | ||||
| # Please read chapter "Library interface versions" of the libtool documentation | ||||
| # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html | ||||
| LIBVERSION=0:0:0 | ||||
|  | ||||
| AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \ | ||||
| 	    $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS) | ||||
|  | ||||
| lib_LTLIBRARIES = libosmo-gsup-client.la | ||||
|  | ||||
| libosmo_gsup_client_la_SOURCES = gsup_client.c | ||||
|  | ||||
| libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined | ||||
| libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) | ||||
|  | ||||
| noinst_PROGRAMS = gsup-test-client | ||||
|  | ||||
| gsup_test_client_SOURCES = gsup_test_client.c | ||||
| gsup_test_client_LDADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ | ||||
| 			 libosmo-gsup-client.la | ||||
							
								
								
									
										402
									
								
								src/gsupclient/gsup_client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								src/gsupclient/gsup_client.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,402 @@ | ||||
| /* Generic Subscriber Update Protocol client */ | ||||
|  | ||||
| /* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Jacob Erlbeck | ||||
|  * Author: Neels Hofmeyr | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/gsupclient/gsup_client.h> | ||||
|  | ||||
| #include <osmocom/abis/ipa.h> | ||||
| #include <osmocom/gsm/oap_client.h> | ||||
| #include <osmocom/gsm/protocol/ipaccess.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static void start_test_procedure(struct osmo_gsup_client *gsupc); | ||||
|  | ||||
| static void gsup_client_send_ping(struct osmo_gsup_client *gsupc) | ||||
| { | ||||
| 	struct msgb *msg = osmo_gsup_client_msgb_alloc(); | ||||
|  | ||||
| 	msg->l2h = msgb_put(msg, 1); | ||||
| 	msg->l2h[0] = IPAC_MSGT_PING; | ||||
| 	ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); | ||||
| 	ipa_client_conn_send(gsupc->link, msg); | ||||
| } | ||||
|  | ||||
| static int gsup_client_connect(struct osmo_gsup_client *gsupc) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	if (gsupc->is_connected) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (osmo_timer_pending(&gsupc->connect_timer)) { | ||||
| 		LOGP(DLGSUP, LOGL_DEBUG, | ||||
| 		     "GSUP connect: connect timer already running\n"); | ||||
| 		osmo_timer_del(&gsupc->connect_timer); | ||||
| 	} | ||||
|  | ||||
| 	if (osmo_timer_pending(&gsupc->ping_timer)) { | ||||
| 		LOGP(DLGSUP, LOGL_DEBUG, | ||||
| 		     "GSUP connect: ping timer already running\n"); | ||||
| 		osmo_timer_del(&gsupc->ping_timer); | ||||
| 	} | ||||
|  | ||||
| 	if (ipa_client_conn_clear_queue(gsupc->link) > 0) | ||||
| 		LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); | ||||
|  | ||||
| 	rc = ipa_client_conn_open(gsupc->link); | ||||
|  | ||||
| 	if (rc >= 0) { | ||||
| 		LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n", | ||||
| 		     gsupc->link->addr, gsupc->link->port); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n", | ||||
| 	     gsupc->link->addr, gsupc->link->port, strerror(-rc)); | ||||
|  | ||||
| 	if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || | ||||
| 	    rc == -EINVAL) | ||||
| 		return rc; | ||||
|  | ||||
| 	osmo_timer_schedule(&gsupc->connect_timer, | ||||
| 			    OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0); | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", | ||||
| 	     gsupc->link->addr, gsupc->link->port); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void connect_timer_cb(void *gsupc_) | ||||
| { | ||||
| 	struct osmo_gsup_client *gsupc = gsupc_; | ||||
|  | ||||
| 	if (gsupc->is_connected) | ||||
| 		return; | ||||
|  | ||||
| 	gsup_client_connect(gsupc); | ||||
| } | ||||
|  | ||||
| static void client_send(struct osmo_gsup_client *gsupc, int proto_ext, | ||||
| 			struct msgb *msg_tx) | ||||
| { | ||||
| 	ipa_prepend_header_ext(msg_tx, proto_ext); | ||||
| 	ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); | ||||
| 	ipa_client_conn_send(gsupc->link, msg_tx); | ||||
| 	/* msg_tx is now queued and will be freed. */ | ||||
| } | ||||
|  | ||||
| static void gsup_client_oap_register(struct osmo_gsup_client *gsupc) | ||||
| { | ||||
| 	struct msgb *msg_tx; | ||||
| 	int rc; | ||||
| 	rc = osmo_oap_client_register(&gsupc->oap_state, &msg_tx); | ||||
|  | ||||
| 	if ((rc < 0) || (!msg_tx)) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); | ||||
| } | ||||
|  | ||||
| static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) | ||||
| { | ||||
| 	struct osmo_gsup_client *gsupc = link->data; | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", | ||||
| 		     link->addr, link->port, up ? "UP" : "DOWN"); | ||||
|  | ||||
| 	gsupc->is_connected = up; | ||||
|  | ||||
| 	if (up) { | ||||
| 		start_test_procedure(gsupc); | ||||
|  | ||||
| 		if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED) | ||||
| 			gsup_client_oap_register(gsupc); | ||||
|  | ||||
| 		osmo_timer_del(&gsupc->connect_timer); | ||||
| 	} else { | ||||
| 		osmo_timer_del(&gsupc->ping_timer); | ||||
|  | ||||
| 		osmo_timer_schedule(&gsupc->connect_timer, | ||||
| 				    OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int gsup_client_oap_handle(struct osmo_gsup_client *gsupc, struct msgb *msg_rx) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct msgb *msg_tx; | ||||
|  | ||||
| 	/* If the oap_state is disabled, this will reject the messages. */ | ||||
| 	rc = osmo_oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); | ||||
| 	msgb_free(msg_rx); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| 	if (msg_tx) | ||||
| 		client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) | ||||
| { | ||||
| 	struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; | ||||
| 	struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); | ||||
| 	struct osmo_gsup_client *gsupc = (struct osmo_gsup_client *)link->data; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(gsupc->unit_name); | ||||
|  | ||||
| 	msg->l2h = &hh->data[0]; | ||||
|  | ||||
| 	rc = ipaccess_bts_handle_ccm(link, gsupc->ipa_dev, msg); | ||||
|  | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DLGSUP, LOGL_NOTICE, | ||||
| 		     "GSUP received an invalid IPA/CCM message from %s:%d\n", | ||||
| 		     link->addr, link->port); | ||||
| 		/* Link has been closed */ | ||||
| 		gsupc->is_connected = 0; | ||||
| 		msgb_free(msg); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (rc == 1) { | ||||
| 		uint8_t msg_type = *(msg->l2h); | ||||
| 		/* CCM message */ | ||||
| 		if (msg_type == IPAC_MSGT_PONG) { | ||||
| 			LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); | ||||
| 			gsupc->got_ipa_pong = 1; | ||||
| 		} | ||||
|  | ||||
| 		msgb_free(msg); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (hh->proto != IPAC_PROTO_OSMO) | ||||
| 		goto invalid; | ||||
|  | ||||
| 	if (!he || msgb_l2len(msg) < sizeof(*he)) | ||||
| 		goto invalid; | ||||
|  | ||||
| 	msg->l2h = &he->data[0]; | ||||
|  | ||||
| 	if (he->proto == IPAC_PROTO_EXT_GSUP) { | ||||
| 		OSMO_ASSERT(gsupc->read_cb != NULL); | ||||
| 		gsupc->read_cb(gsupc, msg); | ||||
| 		/* expecting read_cb() to free msg */ | ||||
| 	} else if (he->proto == IPAC_PROTO_EXT_OAP) { | ||||
| 		return gsup_client_oap_handle(gsupc, msg); | ||||
| 		/* gsup_client_oap_handle frees msg */ | ||||
| 	} else | ||||
| 		goto invalid; | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| invalid: | ||||
| 	LOGP(DLGSUP, LOGL_NOTICE, | ||||
| 	     "GSUP received an invalid IPA message from %s:%d, size = %d\n", | ||||
| 	     link->addr, link->port, msgb_length(msg)); | ||||
|  | ||||
| 	msgb_free(msg); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static void ping_timer_cb(void *gsupc_) | ||||
| { | ||||
| 	struct osmo_gsup_client *gsupc = gsupc_; | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", | ||||
| 	     gsupc->is_connected ? "connected" : "not connected", | ||||
| 	     gsupc->got_ipa_pong ? "got" : "didn't get"); | ||||
|  | ||||
| 	if (gsupc->got_ipa_pong) { | ||||
| 		start_test_procedure(gsupc); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); | ||||
| 	ipa_client_conn_close(gsupc->link); | ||||
| 	gsupc->is_connected = 0; | ||||
|  | ||||
| 	gsup_client_connect(gsupc); | ||||
| } | ||||
|  | ||||
| static void start_test_procedure(struct osmo_gsup_client *gsupc) | ||||
| { | ||||
| 	osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); | ||||
|  | ||||
| 	gsupc->got_ipa_pong = 0; | ||||
| 	osmo_timer_schedule(&gsupc->ping_timer, OSMO_GSUP_CLIENT_PING_INTERVAL, 0); | ||||
| 	LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); | ||||
| 	gsup_client_send_ping(gsupc); | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * Create a gsup client connecting to the specified IP address and TCP port. | ||||
|  * Use the provided ipaccess unit as the client-side identifier; ipa_dev should | ||||
|  * be allocated in talloc_ctx talloc_ctx as well. | ||||
|  * \param[in] talloc_ctx talloc context. | ||||
|  * \param[in] ipa_dev IP access unit which contains client identification information; must be allocated | ||||
|  *                    in talloc_ctx as well to ensure it lives throughout the lifetime of the connection. | ||||
|  * \param[in] ip_addr GSUP server IP address. | ||||
|  * \param[in] tcp_port GSUP server TCP port. | ||||
|  * \param[in] read_cb callback for reading from the GSUP connection. | ||||
|  * \param[in] oapc_config OPA client configuration. | ||||
|  *  \returns a GSUP client connection or NULL on failure. | ||||
|  */ | ||||
| struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx, | ||||
| 						  struct ipaccess_unit *ipa_dev, | ||||
| 						  const char *ip_addr, | ||||
| 						  unsigned int tcp_port, | ||||
| 						  osmo_gsup_client_read_cb_t read_cb, | ||||
| 						  struct osmo_oap_client_config *oapc_config) | ||||
| { | ||||
| 	struct osmo_gsup_client *gsupc; | ||||
| 	int rc; | ||||
|  | ||||
| 	gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client); | ||||
| 	OSMO_ASSERT(gsupc); | ||||
| 	gsupc->unit_name = (const char *)ipa_dev->unit_name; /* API backwards compat */ | ||||
| 	gsupc->ipa_dev = ipa_dev; | ||||
|  | ||||
| 	/* a NULL oapc_config will mark oap_state disabled. */ | ||||
| 	rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state); | ||||
| 	if (rc != 0) | ||||
| 		goto failed; | ||||
|  | ||||
| 	gsupc->link = ipa_client_conn_create(gsupc, | ||||
| 					     /* no e1inp */ NULL, | ||||
| 					     0, | ||||
| 					     ip_addr, tcp_port, | ||||
| 					     gsup_client_updown_cb, | ||||
| 					     gsup_client_read_cb, | ||||
| 					     /* default write_cb */ NULL, | ||||
| 					     gsupc); | ||||
| 	if (!gsupc->link) | ||||
| 		goto failed; | ||||
|  | ||||
| 	osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); | ||||
|  | ||||
| 	rc = gsup_client_connect(gsupc); | ||||
|  | ||||
| 	if (rc < 0) | ||||
| 		goto failed; | ||||
|  | ||||
| 	gsupc->read_cb = read_cb; | ||||
|  | ||||
| 	return gsupc; | ||||
|  | ||||
| failed: | ||||
| 	osmo_gsup_client_destroy(gsupc); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Like osmo_gsup_client_create2() except it expects a unit name instead | ||||
|  * of a full-blown ipacess_unit as the client-side identifier. | ||||
|  */ | ||||
| struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx, | ||||
| 						 const char *unit_name, | ||||
| 						 const char *ip_addr, | ||||
| 						 unsigned int tcp_port, | ||||
| 						 osmo_gsup_client_read_cb_t read_cb, | ||||
| 						 struct osmo_oap_client_config *oapc_config) | ||||
| { | ||||
| 	struct ipaccess_unit *ipa_dev = talloc_zero(talloc_ctx, struct ipaccess_unit); | ||||
| 	ipa_dev->unit_name = talloc_strdup(ipa_dev, unit_name); | ||||
| 	return osmo_gsup_client_create2(talloc_ctx, ipa_dev, ip_addr, tcp_port, read_cb, oapc_config); | ||||
| } | ||||
|  | ||||
| void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc) | ||||
| { | ||||
| 	osmo_timer_del(&gsupc->connect_timer); | ||||
| 	osmo_timer_del(&gsupc->ping_timer); | ||||
|  | ||||
| 	if (gsupc->link) { | ||||
| 		ipa_client_conn_close(gsupc->link); | ||||
| 		ipa_client_conn_destroy(gsupc->link); | ||||
| 		gsupc->link = NULL; | ||||
| 	} | ||||
| 	talloc_free(gsupc); | ||||
| } | ||||
|  | ||||
| int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg) | ||||
| { | ||||
| 	if (!gsupc || !gsupc->is_connected) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg)); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENOTCONN; | ||||
| 	} | ||||
|  | ||||
| 	client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Encode and send a GSUP message. | ||||
|  * \param[in] gsupc    GSUP client. | ||||
|  * \param[in] gsup_msg GSUP message to be sent. | ||||
|  * \returns 0 in case of success, negative on error. | ||||
|  */ | ||||
| int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc, | ||||
| 			      const struct osmo_gsup_message *gsup_msg) | ||||
| { | ||||
| 	struct msgb *gsup_msgb; | ||||
| 	int rc; | ||||
|  | ||||
| 	gsup_msgb = osmo_gsup_client_msgb_alloc(); | ||||
| 	if (!gsup_msgb) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "Couldn't allocate GSUP message\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	rc = osmo_gsup_encode(gsup_msgb, gsup_msg); | ||||
| 	if (rc) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "Couldn't encode GSUP message\n"); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	rc = osmo_gsup_client_send(gsupc, gsup_msgb); | ||||
| 	if (rc) { | ||||
| 		LOGP(DLGSUP, LOGL_ERROR, "Couldn't send GSUP message\n"); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| error: | ||||
| 	talloc_free(gsup_msgb); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| struct msgb *osmo_gsup_client_msgb_alloc(void) | ||||
| { | ||||
| 	return msgb_alloc_headroom(4000, 64, __func__); | ||||
| } | ||||
							
								
								
									
										321
									
								
								src/gsupclient/gsup_test_client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								src/gsupclient/gsup_test_client.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,321 @@ | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <signal.h> | ||||
|  | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #include <osmocom/gsupclient/gsup_client.h> | ||||
|  | ||||
| static struct osmo_gsup_client *g_gc; | ||||
|  | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * IMSI Operation | ||||
|  ***********************************************************************/ | ||||
| static LLIST_HEAD(g_imsi_ops); | ||||
|  | ||||
| struct imsi_op_stats { | ||||
| 	uint32_t num_alloc; | ||||
| 	uint32_t num_released; | ||||
| 	uint32_t num_rx_success; | ||||
| 	uint32_t num_rx_error; | ||||
| 	uint32_t num_timeout; | ||||
| }; | ||||
|  | ||||
| enum imsi_op_type { | ||||
| 	IMSI_OP_SAI, | ||||
| 	IMSI_OP_LU, | ||||
| 	IMSI_OP_ISD, | ||||
| 	_NUM_IMSI_OP | ||||
| }; | ||||
|  | ||||
| static const struct value_string imsi_op_names[] = { | ||||
| 	{ IMSI_OP_SAI, "SAI" }, | ||||
| 	{ IMSI_OP_LU, "LU" }, | ||||
| 	{ IMSI_OP_ISD, "ISD" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; | ||||
|  | ||||
| struct imsi_op { | ||||
| 	struct llist_head list; | ||||
| 	char imsi[17]; | ||||
| 	enum imsi_op_type type; | ||||
| 	struct osmo_timer_list timer; | ||||
| }; | ||||
|  | ||||
| static struct imsi_op *imsi_op_find(const char *imsi, | ||||
| 			     enum imsi_op_type type) | ||||
| { | ||||
| 	struct imsi_op *io; | ||||
|  | ||||
| 	llist_for_each_entry(io, &g_imsi_ops, list) { | ||||
| 		if (!strcmp(io->imsi, imsi) && io->type == type) | ||||
| 			return io; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void imsi_op_timer_cb(void *data); | ||||
|  | ||||
| static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, | ||||
| 				enum imsi_op_type type) | ||||
| { | ||||
| 	struct imsi_op *io; | ||||
|  | ||||
| 	if (imsi_op_find(imsi, type)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	io = talloc_zero(ctx, struct imsi_op); | ||||
| 	OSMO_STRLCPY_ARRAY(io->imsi, imsi); | ||||
| 	io->type = type; | ||||
| 	osmo_timer_setup(&io->timer, imsi_op_timer_cb, io); | ||||
| 	llist_add(&io->list, &g_imsi_ops); | ||||
| 	imsi_op_stats[type].num_alloc++; | ||||
|  | ||||
| 	return io; | ||||
| } | ||||
|  | ||||
| static void imsi_op_release(struct imsi_op *io) | ||||
| { | ||||
| 	osmo_timer_del(&io->timer); | ||||
| 	llist_del(&io->list); | ||||
| 	imsi_op_stats[io->type].num_released++; | ||||
| 	talloc_free(io); | ||||
| } | ||||
|  | ||||
| static void imsi_op_timer_cb(void *data) | ||||
| { | ||||
| 	struct imsi_op *io = data; | ||||
| 	printf("%s: Timer expiration\n", io->imsi); | ||||
| 	imsi_op_stats[io->type].num_timeout++; | ||||
| 	imsi_op_release(io); | ||||
| } | ||||
|  | ||||
| /* allocate + generate + send Send-Auth-Info */ | ||||
| static int req_auth_info(const char *imsi) | ||||
| { | ||||
| 	struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); | ||||
| 	struct osmo_gsup_message gsup = {0}; | ||||
| 	struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi); | ||||
| 	gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; | ||||
|  | ||||
| 	rc = osmo_gsup_encode(msg, &gsup); | ||||
| 	if (rc < 0) { | ||||
| 		printf("%s: encoding failure (%s)\n", imsi, strerror(-rc)); | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	return osmo_gsup_client_send(g_gc, msg); | ||||
| } | ||||
|  | ||||
| /* allocate + generate + send Send-Auth-Info */ | ||||
| static int req_loc_upd(const char *imsi) | ||||
| { | ||||
| 	struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); | ||||
| 	struct osmo_gsup_message gsup = {0}; | ||||
| 	struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi); | ||||
| 	gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; | ||||
|  | ||||
| 	rc = osmo_gsup_encode(msg, &gsup); | ||||
| 	if (rc < 0) { | ||||
| 		printf("%s: encoding failure (%s)\n", imsi, strerror(-rc)); | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	return osmo_gsup_client_send(g_gc, msg); | ||||
| } | ||||
|  | ||||
| static int resp_isd(struct imsi_op *io) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup = {0}; | ||||
| 	struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi); | ||||
| 	gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; | ||||
|  | ||||
| 	rc = osmo_gsup_encode(msg, &gsup); | ||||
| 	if (rc < 0) { | ||||
| 		printf("%s: encoding failure (%s)\n", io->imsi, strerror(-rc)); | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	imsi_op_release(io); | ||||
|  | ||||
| 	return osmo_gsup_client_send(g_gc, msg); | ||||
| } | ||||
|  | ||||
| /* receive an incoming GSUP message */ | ||||
| static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) | ||||
| { | ||||
| 	int is_error = 0, rc; | ||||
|  | ||||
| 	if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { | ||||
| 		imsi_op_stats[io->type].num_rx_error++; | ||||
| 		is_error = 1; | ||||
| 	} else | ||||
| 		imsi_op_stats[io->type].num_rx_success++; | ||||
|  | ||||
| 	switch (io->type) { | ||||
| 	case IMSI_OP_SAI: | ||||
| 		printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); | ||||
| 		/* now that we have auth tuples, request LU */ | ||||
| 		rc = req_loc_upd(io->imsi); | ||||
| 		if (rc < 0) | ||||
| 			printf("Failed to request Location Update for %s\n", io->imsi); | ||||
| 		imsi_op_release(io); | ||||
| 		break; | ||||
| 	case IMSI_OP_LU: | ||||
| 		printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); | ||||
| 		imsi_op_release(io); | ||||
| 		break; | ||||
| 	case IMSI_OP_ISD: | ||||
| 		printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); | ||||
| 		rc = resp_isd(io); | ||||
| 		if (rc < 0) | ||||
| 			printf("Failed to insert subscriber data for %s\n", io->imsi); | ||||
| 		break; | ||||
| 	default: | ||||
| 		printf("%s: Unknown\n", io->imsi); | ||||
| 		imsi_op_release(io); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) | ||||
| { | ||||
| 	switch (msg_type) { | ||||
| 	case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: | ||||
| 	case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: | ||||
| 		return IMSI_OP_SAI; | ||||
| 	case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: | ||||
| 	case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: | ||||
| 		return IMSI_OP_LU; | ||||
| 	case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: | ||||
| 		return IMSI_OP_ISD; | ||||
| 	default: | ||||
| 		printf("Unknown GSUP msg_type %u\n", msg_type); | ||||
| 		return -1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup_msg = {0}; | ||||
| 	struct imsi_op *io = NULL; | ||||
| 	int rc; | ||||
|  | ||||
| 	DEBUGP(DLGSUP, "Rx GSUP %s\n", msgb_hexdump(msg)); | ||||
|  | ||||
| 	rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| 	if (!gsup_msg.imsi[0]) | ||||
| 		return -1; | ||||
|  | ||||
| 	rc = op_type_by_gsup_msgt(gsup_msg.message_type); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| 	switch (rc) { | ||||
| 	case IMSI_OP_SAI: | ||||
| 	case IMSI_OP_LU: | ||||
| 		io = imsi_op_find(gsup_msg.imsi, rc); | ||||
| 		break; | ||||
| 	case IMSI_OP_ISD: | ||||
| 		/* ISD is an inbound transaction */ | ||||
| 		io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); | ||||
| 		break; | ||||
| 	} | ||||
| 	if (!io) | ||||
| 		return -1; | ||||
|  | ||||
| 	imsi_op_rx_gsup(io, &gsup_msg); | ||||
| 	msgb_free(msg); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void print_report(void) | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { | ||||
| 		struct imsi_op_stats *st = &imsi_op_stats[i]; | ||||
| 		const char *name = get_value_string(imsi_op_names, i); | ||||
| 		printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", | ||||
| 			name, st->num_alloc, st->num_released, st->num_rx_success, | ||||
| 			st->num_rx_error, st->num_timeout); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void sig_cb(int sig) | ||||
| { | ||||
| 	switch (sig) { | ||||
| 	case SIGINT: | ||||
| 		print_report(); | ||||
| 		exit(0); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* default categories */ | ||||
| static struct log_info_cat default_categories[] = { | ||||
| }; | ||||
|  | ||||
| static const struct log_info gsup_test_client_log_info = { | ||||
| 	.cat = default_categories, | ||||
| 	.num_cat = ARRAY_SIZE(default_categories), | ||||
| }; | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	unsigned long long i; | ||||
| 	char *server_host = "127.0.0.1"; | ||||
| 	uint16_t server_port = OSMO_GSUP_PORT; | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "gsup_test_client"); | ||||
|  | ||||
| 	osmo_init_logging2(ctx, &gsup_test_client_log_info); | ||||
|  | ||||
| 	g_gc = osmo_gsup_client_create(ctx, "GSUPTEST", server_host, server_port, | ||||
| 					gsupc_read_cb, NULL); | ||||
|  | ||||
|  | ||||
| 	signal(SIGINT, sig_cb); | ||||
|  | ||||
| 	for (i = 0; i < 10000; i++) { | ||||
| 		unsigned long long imsi = 901790000000000 + i; | ||||
| 		char imsi_buf[17] = { 0 }; | ||||
| 		int rc; | ||||
|  | ||||
| 		snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); | ||||
| 		rc = req_auth_info(imsi_buf); | ||||
| 		if (rc < 0) | ||||
| 			printf("Failed to request Auth Info for %s\n", imsi_buf); | ||||
|  | ||||
| 		osmo_select_main(0); | ||||
| 	} | ||||
|  | ||||
| 	while (1) { | ||||
| 		osmo_select_main(0); | ||||
| 	} | ||||
|  | ||||
| 	print_report(); | ||||
| 	exit(0); | ||||
| } | ||||
							
								
								
									
										70
									
								
								src/hlr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/hlr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* OsmoHLR generic header */ | ||||
|  | ||||
| /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Max Suraev <msuraev@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <osmocom/core/linuxlist.h> | ||||
|  | ||||
| #define HLR_DEFAULT_DB_FILE_PATH "hlr.db" | ||||
|  | ||||
| struct hlr_euse; | ||||
|  | ||||
| struct hlr { | ||||
| 	/* GSUP server pointer */ | ||||
| 	struct osmo_gsup_server *gs; | ||||
|  | ||||
| 	/* DB context */ | ||||
| 	char *db_file_path; | ||||
| 	struct db_context *dbc; | ||||
|  | ||||
| 	/* Control Interface */ | ||||
| 	struct ctrl_handle *ctrl; | ||||
| 	const char *ctrl_bind_addr; | ||||
|  | ||||
| 	/* Local bind addr */ | ||||
| 	char *gsup_bind_addr; | ||||
|  | ||||
| 	struct llist_head euse_list; | ||||
| 	struct hlr_euse *euse_default; | ||||
| 	struct llist_head iuse_list; | ||||
|  | ||||
| 	/* NCSS (call independent) session guard timeout value */ | ||||
| 	int ncss_guard_timeout; | ||||
|  | ||||
| 	struct llist_head ussd_routes; | ||||
|  | ||||
| 	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; | ||||
|  | ||||
| struct hlr_subscriber; | ||||
|  | ||||
| void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr); | ||||
							
								
								
									
										446
									
								
								src/hlr_db_tool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								src/hlr_db_tool.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,446 @@ | ||||
| /* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <getopt.h> | ||||
| #include <inttypes.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/application.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "db.h" | ||||
| #include "rand.h" | ||||
|  | ||||
| struct hlr_db_tool_ctx { | ||||
| 	/* DB context */ | ||||
| 	struct db_context *dbc; | ||||
| }; | ||||
|  | ||||
| struct hlr_db_tool_ctx *g_hlr_db_tool_ctx; | ||||
|  | ||||
| static struct { | ||||
| 	const char *db_file; | ||||
| 	bool bootstrap; | ||||
| 	const char *import_nitb_db; | ||||
| 	bool db_upgrade; | ||||
| } cmdline_opts = { | ||||
| 	.db_file = "hlr.db", | ||||
| 	.db_upgrade = false, | ||||
| }; | ||||
|  | ||||
| static void print_help() | ||||
| { | ||||
| 	printf("\n"); | ||||
| 	printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n"); | ||||
| 	printf("  -l --database db-name      The OsmoHLR database to use, default '%s'.\n", | ||||
| 	       cmdline_opts.db_file); | ||||
| 	printf("  -h --help                  This text.\n"); | ||||
| 	printf("  -d option --debug=DMAIN:DDB:DAUC  Enable debugging.\n"); | ||||
| 	printf("  -s --disable-color         Do not print ANSI colors in the log\n"); | ||||
| 	printf("  -T --timestamp             Prefix every log line with a timestamp.\n"); | ||||
| 	printf("  -e --log-level number      Set a global loglevel.\n"); | ||||
| 	printf("  -U --db-upgrade            Allow HLR database schema upgrades.\n"); | ||||
| 	printf("  -V --version               Print the version of OsmoHLR-db-tool.\n"); | ||||
| 	printf("\n"); | ||||
| 	printf("Commands:\n"); | ||||
| 	printf("\n"); | ||||
| 	printf("  create                     Create an empty OsmoHLR database.\n"); | ||||
| 	printf("                             (All commands imply this if none exists yet.)\n"); | ||||
| 	printf("\n"); | ||||
| 	printf("  import-nitb-db <nitb.db>   Add OsmoNITB db's subscribers to OsmoHLR db.\n"); | ||||
| 	printf("                             Be aware that the import is lossy, only the\n"); | ||||
| 	printf("                             IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n"); | ||||
| } | ||||
|  | ||||
| static void print_version(int print_copyright) | ||||
| { | ||||
| 	printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION); | ||||
| 	if (print_copyright) | ||||
| 		printf("\n" | ||||
|        "Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n" | ||||
|        "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n" | ||||
|        "This is free software: you are free to change and redistribute it.\n" | ||||
|        "There is NO WARRANTY, to the extent permitted by law.\n" | ||||
|        "\n"); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv) | ||||
| { | ||||
| 	const char *cmd; | ||||
|  | ||||
| 	while (1) { | ||||
| 		int option_index = 0, c; | ||||
| 		static struct option long_options[] = { | ||||
| 			{"help", 0, 0, 'h'}, | ||||
| 			{"database", 1, 0, 'l'}, | ||||
| 			{"debug", 1, 0, 'd'}, | ||||
| 			{"disable-color", 0, 0, 's'}, | ||||
| 			{"timestamp", 0, 0, 'T'}, | ||||
| 			{"log-level", 1, 0, 'e'}, | ||||
| 			{"db-upgrade", 0, 0, 'U' }, | ||||
| 			{"version", 0, 0, 'V' }, | ||||
| 			{0, 0, 0, 0} | ||||
| 		}; | ||||
|  | ||||
| 		c = getopt_long(argc, argv, "hl:d:sTe:UV", | ||||
| 				long_options, &option_index); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
|  | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			print_help(); | ||||
| 			exit(EXIT_SUCCESS); | ||||
| 		case 'l': | ||||
| 			cmdline_opts.db_file = optarg; | ||||
| 			break; | ||||
| 		case 'd': | ||||
| 			log_parse_category_mask(osmo_stderr_target, optarg); | ||||
| 			break; | ||||
| 		case 's': | ||||
| 			log_set_use_color(osmo_stderr_target, 0); | ||||
| 			break; | ||||
| 		case 'T': | ||||
| 			log_set_print_timestamp(osmo_stderr_target, 1); | ||||
| 			break; | ||||
| 		case 'e': | ||||
| 			log_set_log_level(osmo_stderr_target, atoi(optarg)); | ||||
| 			break; | ||||
| 		case 'U': | ||||
| 			cmdline_opts.db_upgrade = true; | ||||
| 			break; | ||||
| 		case 'V': | ||||
| 			print_version(1); | ||||
| 			exit(EXIT_SUCCESS); | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* catch unknown options *as well as* missing arguments. */ | ||||
| 			fprintf(stderr, "Error in command line options. Exiting.\n"); | ||||
| 			exit(EXIT_FAILURE); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (argc - optind <= 0) { | ||||
| 		fprintf(stderr, "Error: You must specify a command.\n"); | ||||
| 		print_help(); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	cmd = argv[optind++]; | ||||
|  | ||||
| 	if (!strcmp(cmd, "create")) { | ||||
| 		/* Nothing to do, just run the main program to open the database without running any | ||||
| 		 * action, which will bootstrap all tables. */ | ||||
| 	} else if (!strcmp(cmd, "import-nitb-db")) { | ||||
| 		if (argc - optind < 1) { | ||||
| 			fprintf(stderr, "You must specify an input db file\n"); | ||||
| 			print_help(); | ||||
| 			exit(EXIT_FAILURE); | ||||
| 		} | ||||
| 		cmdline_opts.import_nitb_db = argv[optind++]; | ||||
| 	} else { | ||||
| 		fprintf(stderr, "Error: Unknown command `%s'\n", cmd); | ||||
| 		print_help(); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	if (argc - optind > 0) { | ||||
| 		fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]); | ||||
| 		print_help(); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void signal_hdlr(int signal) | ||||
| { | ||||
| 	switch (signal) { | ||||
| 	case SIGINT: | ||||
| 		LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n"); | ||||
| 		db_close(g_hlr_db_tool_ctx->dbc); | ||||
| 		log_fini(); | ||||
| 		talloc_report_full(g_hlr_db_tool_ctx, stderr); | ||||
| 		exit(EXIT_SUCCESS); | ||||
| 		break; | ||||
| 	case SIGUSR1: | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n"); | ||||
| 		talloc_report_full(g_hlr_db_tool_ctx, stderr); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| sqlite3 *open_nitb_db(const char *filename) | ||||
| { | ||||
| 	int rc; | ||||
| 	sqlite3 *nitb_db = NULL; | ||||
|  | ||||
| 	rc = sqlite3_open(filename, &nitb_db); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return nitb_db; | ||||
| } | ||||
|  | ||||
| enum nitb_stmt { | ||||
| 	NITB_SELECT_SUBSCR, | ||||
| 	NITB_SELECT_AUTH_KEYS, | ||||
| }; | ||||
|  | ||||
| static const char *nitb_stmt_sql[] = { | ||||
| 	[NITB_SELECT_SUBSCR] = | ||||
| 		"SELECT imsi, id, extension, authorized" | ||||
| 		" FROM Subscriber" | ||||
| 		" ORDER BY id", | ||||
| 	[NITB_SELECT_AUTH_KEYS] = | ||||
| 		"SELECT algorithm_id, a3a8_ki from authkeys" | ||||
| 		" WHERE subscriber_id = $subscr_id", | ||||
| }; | ||||
|  | ||||
| sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {}; | ||||
|  | ||||
| size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out); | ||||
|  | ||||
| void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct db_context *dbc = g_hlr_db_tool_ctx->dbc; | ||||
| 	sqlite3_stmt *stmt; | ||||
|  | ||||
| 	int count = 0; | ||||
|  | ||||
| 	stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS]; | ||||
| 	if (!db_bind_int(stmt, NULL, nitb_id)) | ||||
| 		return; | ||||
|  | ||||
| 	while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { | ||||
| 		const void *blob; | ||||
| 		unsigned int blob_size; | ||||
| 		static unsigned char buf[4096]; | ||||
| 		static char ki[128]; | ||||
| 		int decoded_size; | ||||
| 		struct sub_auth_data_str aud2g = { | ||||
| 			.type = OSMO_AUTH_TYPE_GSM, | ||||
| 			.algo = OSMO_AUTH_ALG_NONE, | ||||
| 			.u.gsm.ki = ki, | ||||
| 		}; | ||||
|  | ||||
| 		aud2g.algo = sqlite3_column_int(stmt, 0); | ||||
|  | ||||
| 		if (count) { | ||||
| 			LOGP(DDB, LOGL_ERROR, | ||||
| 			     "Warning: subscriber has more than one auth key," | ||||
| 			     " importing only the first key, for IMSI=%s\n", | ||||
| 			     imsi); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		blob = sqlite3_column_blob(stmt, 1); | ||||
| 		blob_size = sqlite3_column_bytes(stmt, 1); | ||||
|  | ||||
| 		if (blob_size > sizeof(buf)) { | ||||
| 			LOGP(DDB, LOGL_ERROR, | ||||
| 			     "OsmoNITB import to %s: Cannot import auth data for IMSI %s:" | ||||
| 			     " too large blob: %u\n", | ||||
| 			     dbc->fname, imsi, blob_size); | ||||
| 			db_remove_reset(stmt); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		decoded_size = _dbd_decode_binary(blob, buf); | ||||
| 		osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki)); | ||||
|  | ||||
| 		db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g); | ||||
| 		count ++; | ||||
| 	} | ||||
|  | ||||
| 	if (rc != SQLITE_DONE && rc != SQLITE_ROW) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s," | ||||
| 		     " during stmt '%s'", | ||||
| 		     rc, sqlite3_errmsg(nitb_db), | ||||
| 		     nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]); | ||||
| 	} | ||||
|  | ||||
| 	db_remove_reset(stmt); | ||||
| } | ||||
|  | ||||
| void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt) | ||||
| { | ||||
| 	struct db_context *dbc = g_hlr_db_tool_ctx->dbc; | ||||
| 	int rc; | ||||
| 	struct hlr_subscriber subscr; | ||||
|  | ||||
| 	int64_t nitb_id; | ||||
| 	int64_t imsi; | ||||
| 	char imsi_str[32]; | ||||
| 	bool authorized; | ||||
|  | ||||
| 	imsi = sqlite3_column_int64(stmt, 0); | ||||
|  | ||||
| 	snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi); | ||||
|  | ||||
| 	rc = db_subscr_create(dbc, imsi_str, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n", | ||||
| 		     dbc->fname, | ||||
| 		     imsi_str, | ||||
| 		     rc, | ||||
| 		     strerror(-rc)); | ||||
| 		/* on error, still attempt to continue */ | ||||
| 	} | ||||
|  | ||||
| 	nitb_id = sqlite3_column_int64(stmt, 1); | ||||
| 	copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2); | ||||
| 	authorized = sqlite3_column_int(stmt, 3) ? true : false; | ||||
|  | ||||
| 	db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn); | ||||
| 	db_subscr_nam(dbc, imsi_str, authorized, true); | ||||
| 	db_subscr_nam(dbc, imsi_str, authorized, false); | ||||
|  | ||||
| 	/* find the just created id */ | ||||
| 	rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s," | ||||
| 		     " but failed to get new subscriber id: %d: %s\n", | ||||
| 		     dbc->fname, | ||||
| 		     imsi_str, | ||||
| 		     rc, | ||||
| 		     strerror(-rc)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi)); | ||||
|  | ||||
| 	import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id); | ||||
| } | ||||
|  | ||||
| int import_nitb_db(void) | ||||
| { | ||||
| 	int i; | ||||
| 	int ret; | ||||
| 	int rc; | ||||
| 	const char *sql; | ||||
| 	sqlite3_stmt *stmt; | ||||
|  | ||||
| 	sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db); | ||||
|  | ||||
| 	if (!nitb_db) | ||||
| 		return -1; | ||||
| 	ret = 0; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) { | ||||
| 		sql = nitb_stmt_sql[i]; | ||||
| 		rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL); | ||||
| 		if (rc != SQLITE_OK) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql); | ||||
| 			ret = -1; | ||||
| 			goto out_free; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	stmt = nitb_stmt[NITB_SELECT_SUBSCR]; | ||||
|  | ||||
| 	while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { | ||||
| 		import_nitb_subscr(nitb_db, stmt); | ||||
| 		/* On failure, carry on with the rest. */ | ||||
| 	} | ||||
| 	if (rc != SQLITE_DONE) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s," | ||||
| 		     " during stmt '%s'", | ||||
| 		     rc, sqlite3_errmsg(nitb_db), | ||||
| 		     nitb_stmt_sql[NITB_SELECT_SUBSCR]); | ||||
| 		goto out_free; | ||||
| 	} | ||||
|  | ||||
| 	db_remove_reset(stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
|  | ||||
| out_free: | ||||
| 	sqlite3_close(nitb_db); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int rc; | ||||
| 	int (*main_action)(void); | ||||
| 	main_action = NULL; | ||||
|  | ||||
| 	g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx); | ||||
| 	OSMO_ASSERT(g_hlr_db_tool_ctx); | ||||
| 	talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool"); | ||||
|  | ||||
| 	rc = osmo_init_logging2(g_hlr_db_tool_ctx, &hlr_log_info); | ||||
| 	if (rc < 0) { | ||||
| 		fprintf(stderr, "Error initializing logging\n"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	handle_options(argc, argv); | ||||
|  | ||||
| 	if (cmdline_opts.import_nitb_db) { | ||||
| 		if (main_action) | ||||
| 			goto too_many_actions; | ||||
| 		main_action = import_nitb_db; | ||||
| 	} | ||||
| 	/* Future: add more main_actions, besides import-nitb-db, here. | ||||
| 	 * For command 'create', no action is required. */ | ||||
|  | ||||
| 	/* Just in case any db actions need randomness */ | ||||
| 	rc = rand_init(); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade); | ||||
| 	if (!g_hlr_db_tool_ctx->dbc) { | ||||
| 		LOGP(DMAIN, LOGL_FATAL, "Error opening database\n"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	osmo_init_ignore_signals(); | ||||
| 	signal(SIGINT, &signal_hdlr); | ||||
| 	signal(SIGUSR1, &signal_hdlr); | ||||
|  | ||||
| 	rc = 0; | ||||
| 	if (main_action) | ||||
| 		rc = (*main_action)(); | ||||
|  | ||||
| 	db_close(g_hlr_db_tool_ctx->dbc); | ||||
| 	log_fini(); | ||||
| 	exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); | ||||
|  | ||||
| too_many_actions: | ||||
| 	fprintf(stderr, "Too many actions requested.\n"); | ||||
| 	log_fini(); | ||||
| 	exit(EXIT_FAILURE); | ||||
| } | ||||
|  | ||||
| /* stubs */ | ||||
| void lu_op_alloc_conn(void) { OSMO_ASSERT(0); } | ||||
| void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); } | ||||
| void lu_op_free(void) { OSMO_ASSERT(0); } | ||||
							
								
								
									
										645
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										645
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,645 @@ | ||||
| /* OsmoHLR SS/USSD implementation */ | ||||
|  | ||||
| /* (C) 2018 Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include <osmocom/gsm/gsm0480.h> | ||||
| #include <osmocom/gsm/protocol/gsm_04_80.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include "hlr.h" | ||||
| #include "hlr_ussd.h" | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
| #include "logging.h" | ||||
| #include "db.h" | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * core data structures expressing config from VTY | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| struct hlr_euse *euse_find(struct hlr *hlr, const char *name) | ||||
| { | ||||
| 	struct hlr_euse *euse; | ||||
|  | ||||
| 	llist_for_each_entry(euse, &hlr->euse_list, list) { | ||||
| 		if (!strcmp(euse->name, name)) | ||||
| 			return euse; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name) | ||||
| { | ||||
| 	struct hlr_euse *euse = euse_find(hlr, name); | ||||
| 	if (euse) | ||||
| 		return NULL; | ||||
|  | ||||
| 	euse = talloc_zero(hlr, struct hlr_euse); | ||||
| 	euse->name = talloc_strdup(euse, name); | ||||
| 	euse->hlr = hlr; | ||||
| 	llist_add_tail(&euse->list, &hlr->euse_list); | ||||
|  | ||||
| 	return euse; | ||||
| } | ||||
|  | ||||
| void euse_del(struct hlr_euse *euse) | ||||
| { | ||||
| 	llist_del(&euse->list); | ||||
| 	talloc_free(euse); | ||||
| } | ||||
|  | ||||
|  | ||||
| struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix) | ||||
| { | ||||
| 	struct hlr_ussd_route *rt; | ||||
|  | ||||
| 	llist_for_each_entry(rt, &hlr->ussd_routes, list) { | ||||
| 		if (!strcmp(rt->prefix, prefix)) | ||||
| 			return rt; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix, | ||||
| 						   const struct hlr_iuse *iuse) | ||||
| { | ||||
| 	struct hlr_ussd_route *rt; | ||||
|  | ||||
| 	if (ussd_route_find_prefix(hlr, prefix)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	rt = talloc_zero(hlr, struct hlr_ussd_route); | ||||
| 	rt->prefix = talloc_strdup(rt, prefix); | ||||
| 	rt->u.iuse = iuse; | ||||
| 	llist_add_tail(&rt->list, &hlr->ussd_routes); | ||||
|  | ||||
| 	return rt; | ||||
| } | ||||
|  | ||||
| struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix, | ||||
| 						   struct hlr_euse *euse) | ||||
| { | ||||
| 	struct hlr_ussd_route *rt; | ||||
|  | ||||
| 	if (ussd_route_find_prefix(hlr, prefix)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	rt = talloc_zero(hlr, struct hlr_ussd_route); | ||||
| 	rt->prefix = talloc_strdup(rt, prefix); | ||||
| 	rt->is_external = true; | ||||
| 	rt->u.euse = euse; | ||||
| 	llist_add_tail(&rt->list, &hlr->ussd_routes); | ||||
|  | ||||
| 	return rt; | ||||
| } | ||||
|  | ||||
| void ussd_route_del(struct hlr_ussd_route *rt) | ||||
| { | ||||
| 	llist_del(&rt->list); | ||||
| 	talloc_free(rt); | ||||
| } | ||||
|  | ||||
| static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char *ussd_code) | ||||
| { | ||||
| 	struct hlr_ussd_route *rt; | ||||
| 	llist_for_each_entry(rt, &hlr->ussd_routes, list) { | ||||
| 		if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) { | ||||
| 			LOGP(DSS, LOGL_DEBUG, "Found %s '%s' (prefix '%s') for USSD " | ||||
| 				"Code '%s'\n", rt->is_external ? "EUSE" : "IUSE", | ||||
| 				rt->is_external ? rt->u.euse->name : rt->u.iuse->name, | ||||
| 				rt->prefix, ussd_code); | ||||
| 			return rt; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DSS, LOGL_DEBUG, "Could not find Route for USSD Code '%s'\n", ussd_code); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * handling functions for individual GSUP messages | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| #define LOGPSS(ss, lvl, fmt, args...) \ | ||||
| 	LOGP(DSS, lvl, "%s/0x%08x: " fmt, (ss)->imsi, (ss)->session_id, ## args) | ||||
|  | ||||
| struct ss_session { | ||||
| 	/* link us to hlr->ss_sessions */ | ||||
| 	struct llist_head list; | ||||
| 	/* imsi of this session */ | ||||
| 	char imsi[OSMO_IMSI_BUF_SIZE]; | ||||
| 	/* ID of this session (unique per IMSI) */ | ||||
| 	uint32_t session_id; | ||||
| 	/* state of the session */ | ||||
| 	enum osmo_gsup_session_state state; | ||||
| 	/* time-out when we will delete the session */ | ||||
| 	struct osmo_timer_list timeout; | ||||
|  | ||||
| 	/* is this USSD for an external handler (EUSE): true */ | ||||
| 	bool is_external; | ||||
| 	union { | ||||
| 		/* external USSD Entity responsible for this session */ | ||||
| 		struct hlr_euse *euse; | ||||
| 		/* internal USSD Entity responsible for this session */ | ||||
| 		const struct hlr_iuse *iuse; | ||||
| 	} 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, | ||||
| 	 * 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 */ | ||||
| }; | ||||
|  | ||||
| struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id) | ||||
| { | ||||
| 	struct ss_session *ss; | ||||
| 	llist_for_each_entry(ss, &hlr->ss_sessions, list) { | ||||
| 		if (!strcmp(ss->imsi, imsi) && ss->session_id == session_id) | ||||
| 			return ss; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| void ss_session_free(struct ss_session *ss) | ||||
| { | ||||
| 	osmo_timer_del(&ss->timeout); | ||||
| 	llist_del(&ss->list); | ||||
| 	talloc_free(ss); | ||||
| } | ||||
|  | ||||
| static void ss_session_timeout(void *data) | ||||
| { | ||||
| 	struct ss_session *ss = data; | ||||
|  | ||||
| 	LOGPSS(ss, LOGL_NOTICE, "SS Session Timeout, destroying\n"); | ||||
| 	/* FIXME: should we send a ReturnError component to the MS? */ | ||||
| 	ss_session_free(ss); | ||||
| } | ||||
|  | ||||
| struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t session_id) | ||||
| { | ||||
| 	struct ss_session *ss; | ||||
|  | ||||
| 	OSMO_ASSERT(!ss_session_find(hlr, imsi, session_id)); | ||||
|  | ||||
| 	ss = talloc_zero(hlr, struct ss_session); | ||||
| 	OSMO_ASSERT(ss); | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(ss->imsi, imsi); | ||||
| 	ss->session_id = session_id; | ||||
|  | ||||
| 	/* Schedule self-destruction timer */ | ||||
| 	osmo_timer_setup(&ss->timeout, ss_session_timeout, ss); | ||||
| 	if (g_hlr->ncss_guard_timeout > 0) | ||||
| 		osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0); | ||||
|  | ||||
| 	llist_add_tail(&ss->list, &hlr->ss_sessions); | ||||
| 	return ss; | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * 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, | ||||
| 			bool final, struct msgb *ss_msg) | ||||
|  | ||||
| { | ||||
| 	struct osmo_gsup_message resp = {0}; | ||||
| 	struct msgb *resp_msg; | ||||
|  | ||||
| 	resp.message_type = gsup_msg_type; | ||||
| 	OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi); | ||||
| 	if (final) | ||||
| 		resp.session_state = OSMO_GSUP_SESSION_STATE_END; | ||||
| 	else | ||||
| 		resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE; | ||||
| 	resp.session_id = ss->session_id; | ||||
| 	if (ss_msg) { | ||||
| 		resp.ss_info = msgb_data(ss_msg); | ||||
| 		resp.ss_info_len = msgb_length(ss_msg); | ||||
| 	} | ||||
|  | ||||
| 	resp_msg = msgb_alloc_headroom(4000, 64, __func__); | ||||
| 	OSMO_ASSERT(resp_msg); | ||||
| 	osmo_gsup_encode(resp_msg, &resp); | ||||
| 	msgb_free(ss_msg); | ||||
|  | ||||
| 	return ss_gsup_send(ss, g_hlr->gs, resp_msg); | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_tag, | ||||
| 			uint8_t problem_code) | ||||
| { | ||||
| 	struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code); | ||||
| 	LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id, | ||||
| 		problem_tag, problem_code); | ||||
| 	OSMO_ASSERT(msg); | ||||
| 	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code) | ||||
| { | ||||
| 	struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code); | ||||
| 	LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code); | ||||
| 	OSMO_ASSERT(msg); | ||||
| 	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg); | ||||
| } | ||||
|  | ||||
| static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text) | ||||
| { | ||||
| 	struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); | ||||
| 	LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text); | ||||
| 	OSMO_ASSERT(msg); | ||||
| 	return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg); | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * Internal USSD Handlers | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| #include "db.h" | ||||
|  | ||||
| static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 				  const struct osmo_gsup_message *gsup, const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		if (strlen(subscr.msisdn) == 0) | ||||
| 			snprintf(buf, sizeof(buf), "You have no MSISDN!"); | ||||
| 		else | ||||
| 			snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn); | ||||
| 		ss_tx_ussd_7bit(ss, true, req->invoke_id, buf); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 				const struct osmo_gsup_message *gsup, const struct ss_request *req) | ||||
| { | ||||
| 	char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; | ||||
| 	snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi); | ||||
| 	ss_tx_ussd_7bit(ss, true, req->invoke_id, buf); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static const struct hlr_iuse hlr_iuses[] = { | ||||
| 	{ | ||||
| 		.name = "own-msisdn", | ||||
| 		.handle_ussd = handle_ussd_own_msisdn, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "own-imsi", | ||||
| 		.handle_ussd = handle_ussd_own_imsi, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| const struct hlr_iuse *iuse_find(const char *name) | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(hlr_iuses); i++) { | ||||
| 		const struct hlr_iuse *iuse = &hlr_iuses[i]; | ||||
| 		if (!strcmp(name, iuse->name)) | ||||
| 			return iuse; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * handling functions for individual GSUP messages | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| static bool ss_op_is_ussd(uint8_t opcode) | ||||
| { | ||||
| 	switch (opcode) { | ||||
| 	case GSM0480_OP_CODE_PROCESS_USS_DATA: | ||||
| 	case GSM0480_OP_CODE_PROCESS_USS_REQ: | ||||
| 	case GSM0480_OP_CODE_USS_REQUEST: | ||||
| 	case GSM0480_OP_CODE_USS_NOTIFY: | ||||
| 		return true; | ||||
| 	default: | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* is this GSUP connection an EUSE (true) or not (false)? */ | ||||
| static bool conn_is_euse(struct osmo_gsup_conn *conn) | ||||
| { | ||||
| 	int rc; | ||||
| 	uint8_t *addr; | ||||
|  | ||||
| 	rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR); | ||||
| 	if (rc <= 5) | ||||
| 		return false; | ||||
| 	if (!strncmp((char *)addr, "EUSE-", 5)) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
|  | ||||
| static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn) | ||||
| { | ||||
| 	int rc; | ||||
| 	char *addr; | ||||
| 	struct hlr *hlr = conn->server->priv; | ||||
|  | ||||
| 	rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR); | ||||
| 	if (rc <= 5) | ||||
| 		return NULL; | ||||
| 	if (strncmp(addr, "EUSE-", 5)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return euse_find(hlr, addr+5); | ||||
| } | ||||
|  | ||||
| static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup, | ||||
| 			const struct ss_request *req) | ||||
| { | ||||
| 	uint8_t comp_type = gsup->ss_info[0]; | ||||
|  | ||||
| 	LOGPSS(ss, LOGL_INFO, "SS CompType=%s, OpCode=%s\n", | ||||
| 		gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode)); | ||||
|  | ||||
| 	/** | ||||
| 	 * FIXME: As we don't store any SS related information | ||||
| 	 * (e.g. call forwarding preferences) in the database, | ||||
| 	 * we don't handle "structured" SS requests at all. | ||||
| 	 */ | ||||
| 	LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n"); | ||||
| 	ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED); | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
|  | ||||
| /* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */ | ||||
| static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			const struct osmo_gsup_message *gsup, const struct ss_request *req) | ||||
| { | ||||
| 	uint8_t comp_type = gsup->ss_info[0]; | ||||
| 	struct msgb *msg_out; | ||||
| 	bool is_euse_originated = conn_is_euse(conn); | ||||
|  | ||||
| 	LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n", | ||||
| 		gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode), | ||||
| 		req->ussd_text); | ||||
|  | ||||
| 	if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) { | ||||
| 		LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text); | ||||
| 		ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (is_euse_originated) { | ||||
| 		msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW"); | ||||
| 		OSMO_ASSERT(msg_out); | ||||
| 		/* Received from EUSE, Forward to VLR */ | ||||
| 		osmo_gsup_encode(msg_out, gsup); | ||||
| 		ss_gsup_send(ss, conn->server, msg_out); | ||||
| 	} else { | ||||
| 		/* Received from VLR (MS) */ | ||||
| 		if (ss->is_external) { | ||||
| 			/* Forward to EUSE */ | ||||
| 			char addr[128]; | ||||
| 			strcpy(addr, "EUSE-"); | ||||
| 			osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5); | ||||
| 			conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1); | ||||
| 			if (!conn) { | ||||
| 				LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr); | ||||
| 				ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 			} else { | ||||
| 				msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW"); | ||||
| 				OSMO_ASSERT(msg_out); | ||||
| 				osmo_gsup_encode(msg_out, gsup); | ||||
| 				osmo_gsup_conn_send(conn, msg_out); | ||||
| 			} | ||||
| 		} else { | ||||
| 			/* Handle internally */ | ||||
| 			ss->u.iuse->handle_ussd(conn, ss, gsup, req); | ||||
| 			/* Release session immediately */ | ||||
| 			ss_session_free(ss); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well | ||||
|  * as from the EUSE side */ | ||||
| int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup) | ||||
| { | ||||
| 	struct hlr *hlr = conn->server->priv; | ||||
| 	struct ss_session *ss; | ||||
| 	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, | ||||
| 		osmo_gsup_session_state_name(gsup->session_state)); | ||||
|  | ||||
| 	/* decode and find out what kind of SS message it is */ | ||||
| 	if (gsup->ss_info && gsup->ss_info_len) { | ||||
| 		if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) { | ||||
| 			LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n", | ||||
| 				gsup->imsi, gsup->session_id, | ||||
| 				osmo_hexdump(gsup->ss_info, gsup->ss_info_len)); | ||||
| 			/* FIXME: Send a Reject component? */ | ||||
| 			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) { | ||||
| 	case OSMO_GSUP_SESSION_STATE_BEGIN: | ||||
| 		/* Check for overlapping Session ID usage */ | ||||
| 		if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) { | ||||
| 			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n", | ||||
| 				gsup->imsi, gsup->session_id); | ||||
| 			goto out_err; | ||||
| 		} | ||||
| 		ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id); | ||||
| 		if (!ss) { | ||||
| 			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n", | ||||
| 				gsup->imsi, gsup->session_id); | ||||
| 			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 (conn_is_euse(conn)) { | ||||
| 				/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */ | ||||
| 				ss->u.euse = euse_by_conn(conn); | ||||
| 			} else { | ||||
| 				/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */ | ||||
| 				struct hlr_ussd_route *rt; | ||||
| 				rt = ussd_route_lookup_7bit(hlr, (const char *) req.ussd_text); | ||||
| 				if (rt) { | ||||
| 					if (rt->is_external) { | ||||
| 						ss->is_external = true; | ||||
| 						ss->u.euse = rt->u.euse; | ||||
| 					} else if (rt) { | ||||
| 						ss->is_external = false; | ||||
| 						ss->u.iuse = rt->u.iuse; | ||||
| 					} | ||||
| 				} else { | ||||
| 					if (hlr->euse_default) { | ||||
| 						ss->is_external = true; | ||||
| 						ss->u.euse = hlr->euse_default; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			/* dispatch unstructured SS to routing */ | ||||
| 			handle_ussd(conn, ss, gsup, &req); | ||||
| 		} else { | ||||
| 			/* dispatch non-call SS to internal code */ | ||||
| 			handle_ss(ss, gsup, &req); | ||||
| 		} | ||||
| 		break; | ||||
| 	case OSMO_GSUP_SESSION_STATE_CONTINUE: | ||||
| 		ss = ss_session_find(hlr, gsup->imsi, gsup->session_id); | ||||
| 		if (!ss) { | ||||
| 			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n", | ||||
| 				gsup->imsi, gsup->session_id); | ||||
| 			goto out_err; | ||||
| 		} | ||||
|  | ||||
| 		/* Reschedule self-destruction timer */ | ||||
| 		if (g_hlr->ncss_guard_timeout > 0) | ||||
| 			osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0); | ||||
|  | ||||
| 		if (ss_op_is_ussd(req.opcode)) { | ||||
| 			/* dispatch unstructured SS to routing */ | ||||
| 			handle_ussd(conn, ss, gsup, &req); | ||||
| 		} else { | ||||
| 			/* dispatch non-call SS to internal code */ | ||||
| 			handle_ss(ss, gsup, &req); | ||||
| 		} | ||||
| 		break; | ||||
| 	case OSMO_GSUP_SESSION_STATE_END: | ||||
| 		ss = ss_session_find(hlr, gsup->imsi, gsup->session_id); | ||||
| 		if (!ss) { | ||||
| 			LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n", | ||||
| 				gsup->imsi, gsup->session_id); | ||||
| 			goto out_err; | ||||
| 		} | ||||
|  | ||||
| 		/* SS payload is optional for END */ | ||||
| 		if (gsup->ss_info && gsup->ss_info_len) { | ||||
| 			if (ss_op_is_ussd(req.opcode)) { | ||||
| 				/* dispatch unstructured SS to routing */ | ||||
| 				handle_ussd(conn, ss, gsup, &req); | ||||
| 			} else { | ||||
| 				/* dispatch non-call SS to internal code */ | ||||
| 				handle_ss(ss, gsup, &req); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		ss_session_free(ss); | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi, | ||||
| 			gsup->session_id, gsup->session_state); | ||||
| 		goto out_err; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| out_err: | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup) | ||||
| { | ||||
| 	LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id, | ||||
| 		osmo_gsup_session_state_name(gsup->session_state)); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| #define NCSS_GUARD_TIMEOUT_DEFAULT 30 | ||||
|  | ||||
| struct hlr_ussd_route { | ||||
| 	/* g_hlr.routes */ | ||||
| 	struct llist_head list; | ||||
| 	const char *prefix; | ||||
| 	bool is_external; | ||||
| 	union { | ||||
| 		struct hlr_euse *euse; | ||||
| 		const struct hlr_iuse *iuse; | ||||
| 	} u; | ||||
| }; | ||||
|  | ||||
| struct hlr_euse { | ||||
| 	/* list in the per-hlr list of EUSEs */ | ||||
| 	struct llist_head list; | ||||
| 	struct hlr *hlr; | ||||
| 	/* name (must match the IPA ID tag) */ | ||||
| 	const char *name; | ||||
| 	/* human-readable description */ | ||||
| 	const char *description; | ||||
|  | ||||
| 	/* GSUP connection to the EUSE, if any */ | ||||
| 	struct osmo_gsup_conn *conn; | ||||
| }; | ||||
|  | ||||
| struct hlr_euse *euse_find(struct hlr *hlr, const char *name); | ||||
| struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name); | ||||
| void euse_del(struct hlr_euse *euse); | ||||
|  | ||||
| const struct hlr_iuse *iuse_find(const char *name); | ||||
|  | ||||
| struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix); | ||||
| struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix, | ||||
| 						   const struct hlr_iuse *iuse); | ||||
| struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix, | ||||
| 						   struct hlr_euse *euse); | ||||
| void ussd_route_del(struct hlr_ussd_route *rt); | ||||
|  | ||||
| int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup); | ||||
| int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup); | ||||
|  | ||||
| struct ss_session; | ||||
| struct ss_request; | ||||
|  | ||||
| /* Internal USSD Handler */ | ||||
| struct hlr_iuse { | ||||
| 	const char *name; | ||||
| 	/* call-back to be called for any incoming USSD messages for this IUSE */ | ||||
| 	int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			   const struct osmo_gsup_message *gsup, const struct ss_request *req); | ||||
| }; | ||||
							
								
								
									
										465
									
								
								src/hlr_vty.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								src/hlr_vty.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,465 @@ | ||||
| /* OsmoHLR VTY implementation */ | ||||
|  | ||||
| /* (C) 2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * (C) 2018 Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * (C) 2018 Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/stats.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/abis/ipa.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "hlr.h" | ||||
| #include "hlr_vty.h" | ||||
| #include "hlr_vty_subscr.h" | ||||
| #include "hlr_ussd.h" | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| struct cmd_node hlr_node = { | ||||
| 	HLR_NODE, | ||||
| 	"%s(config-hlr)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| DEFUN(cfg_hlr, | ||||
|       cfg_hlr_cmd, | ||||
|       "hlr", | ||||
|       "Configure the HLR") | ||||
| { | ||||
| 	vty->node = HLR_NODE; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| struct cmd_node gsup_node = { | ||||
| 	GSUP_NODE, | ||||
| 	"%s(config-hlr-gsup)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| DEFUN(cfg_gsup, | ||||
|       cfg_gsup_cmd, | ||||
|       "gsup", | ||||
|       "Configure GSUP options") | ||||
| { | ||||
| 	vty->node = GSUP_NODE; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int config_write_hlr(struct vty *vty) | ||||
| { | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| static int config_write_hlr_gsup(struct vty *vty) | ||||
| { | ||||
| 	vty_out(vty, " gsup%s", VTY_NEWLINE); | ||||
| 	if (g_hlr->gsup_bind_addr) | ||||
| 		vty_out(vty, "  bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn) | ||||
| { | ||||
| 	const struct ipa_server_conn *isc = conn->conn; | ||||
| 	char *name; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR); | ||||
| 	OSMO_ASSERT(rc); | ||||
|  | ||||
| 	vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u, 3G_IND=%u%s", | ||||
| 		name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind, | ||||
| 		VTY_NEWLINE); | ||||
| } | ||||
|  | ||||
| DEFUN(show_gsup_conn, show_gsup_conn_cmd, | ||||
| 	"show gsup-connections", | ||||
| 	SHOW_STR "GSUP Connections from VLRs, SGSNs, EUSEs\n") | ||||
| { | ||||
| 	struct osmo_gsup_server *gs = g_hlr->gs; | ||||
| 	struct osmo_gsup_conn *conn; | ||||
|  | ||||
| 	llist_for_each_entry(conn, &gs->clients, list) | ||||
| 		show_one_conn(vty, conn); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_hlr_gsup_bind_ip, | ||||
|       cfg_hlr_gsup_bind_ip_cmd, | ||||
|       "bind ip A.B.C.D", | ||||
|       "Listen/Bind related socket option\n" | ||||
|       IP_STR | ||||
|       "IPv4 Address to bind the GSUP interface to\n") | ||||
| { | ||||
| 	if(g_hlr->gsup_bind_addr) | ||||
| 		talloc_free(g_hlr->gsup_bind_addr); | ||||
| 	g_hlr->gsup_bind_addr = talloc_strdup(g_hlr, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * USSD Entity | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| #include "hlr_ussd.h" | ||||
|  | ||||
| #define USSD_STR "USSD Configuration\n" | ||||
| #define UROUTE_STR "Routing Configuration\n" | ||||
| #define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n" | ||||
|  | ||||
| #define INT_CHOICE "(own-msisdn|own-imsi)" | ||||
| #define INT_STR "Internal USSD Handler\n" \ | ||||
| 		"Respond with subscribers' own MSISDN\n" \ | ||||
| 		"Respond with subscribers' own IMSI\n" | ||||
|  | ||||
| #define EXT_STR "External USSD Handler\n" \ | ||||
| 		"Name of External USSD Handler (IPA CCM ID)\n" | ||||
|  | ||||
| DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd, | ||||
| 	"ussd route prefix PREFIX internal " INT_CHOICE, | ||||
| 	USSD_STR UROUTE_STR PREFIX_STR INT_STR) | ||||
| { | ||||
| 	const struct hlr_iuse *iuse = iuse_find(argv[1]); | ||||
| 	struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); | ||||
| 	if (rt) { | ||||
| 		vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ussd_route_prefix_alloc_int(g_hlr, argv[0], iuse); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ussd_route_pfx_ext, cfg_ussd_route_pfx_ext_cmd, | ||||
| 	"ussd route prefix PREFIX external EUSE", | ||||
| 	USSD_STR UROUTE_STR PREFIX_STR EXT_STR) | ||||
| { | ||||
| 	struct hlr_euse *euse = euse_find(g_hlr, argv[1]); | ||||
| 	struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); | ||||
| 	if (rt) { | ||||
| 		vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	if (!euse) { | ||||
| 		vty_out(vty, "%% Cannot find euse '%s'%s", argv[1], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ussd_route_prefix_alloc_ext(g_hlr, argv[0], euse); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ussd_no_route_pfx, cfg_ussd_no_route_pfx_cmd, | ||||
| 	"no ussd route prefix PREFIX", | ||||
| 	NO_STR USSD_STR UROUTE_STR PREFIX_STR) | ||||
| { | ||||
| 	struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]); | ||||
| 	if (!rt) { | ||||
| 		vty_out(vty, "%% Cannot find route for prefix %s%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ussd_route_del(rt); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd, | ||||
| 	"ussd default-route external EUSE", | ||||
| 	USSD_STR "Configure default-route for all USSD to unknown destinations\n" | ||||
| 	EXT_STR) | ||||
| { | ||||
| 	struct hlr_euse *euse; | ||||
|  | ||||
| 	euse = euse_find(g_hlr, argv[0]); | ||||
| 	if (!euse) { | ||||
| 		vty_out(vty, "%% Cannot find EUSE %s%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (g_hlr->euse_default != euse) { | ||||
| 		vty_out(vty, "Switching default route from %s to %s%s", | ||||
| 			g_hlr->euse_default ? g_hlr->euse_default->name : "<none>", | ||||
| 			euse->name, VTY_NEWLINE); | ||||
| 		g_hlr->euse_default = euse; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd, | ||||
| 	"no ussd default-route", | ||||
| 	NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n") | ||||
| { | ||||
| 	g_hlr->euse_default = NULL; | ||||
|  | ||||
| 	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 = { | ||||
| 	EUSE_NODE, | ||||
| 	"%s(config-hlr-euse)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| DEFUN(cfg_euse, cfg_euse_cmd, | ||||
| 	"euse NAME", | ||||
| 	"Configure a particular External USSD Entity\n" | ||||
| 	"Alphanumeric name of the External USSD Entity\n") | ||||
| { | ||||
| 	struct hlr_euse *euse; | ||||
| 	const char *id = argv[0]; | ||||
|  | ||||
| 	euse = euse_find(g_hlr, id); | ||||
| 	if (!euse) { | ||||
| 		euse = euse_alloc(g_hlr, id); | ||||
| 		if (!euse) | ||||
| 			return CMD_WARNING; | ||||
| 	} | ||||
| 	vty->index = euse; | ||||
| 	vty->index_sub = &euse->description; | ||||
| 	vty->node = EUSE_NODE; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_no_euse, cfg_no_euse_cmd, | ||||
| 	"no euse NAME", | ||||
| 	NO_STR "Remove a particular External USSD Entity\n" | ||||
| 	"Alphanumeric name of the External USSD Entity\n") | ||||
| { | ||||
| 	struct hlr_euse *euse = euse_find(g_hlr, argv[0]); | ||||
| 	if (!euse) { | ||||
| 		vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	if (g_hlr->euse_default == euse) { | ||||
| 		vty_out(vty, "%% Cannot remove EUSE %s, it is the default route%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	euse_del(euse); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static void dump_one_euse(struct vty *vty, struct hlr_euse *euse) | ||||
| { | ||||
| 	vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE); | ||||
| } | ||||
|  | ||||
| static int config_write_euse(struct vty *vty) | ||||
| { | ||||
| 	struct hlr_euse *euse; | ||||
| 	struct hlr_ussd_route *rt; | ||||
|  | ||||
| 	llist_for_each_entry(euse, &g_hlr->euse_list, list) | ||||
| 		dump_one_euse(vty, euse); | ||||
|  | ||||
| 	llist_for_each_entry(rt, &g_hlr->ussd_routes, list) { | ||||
| 		vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix, | ||||
| 			rt->is_external ? "external" : "internal", | ||||
| 			rt->is_external ? rt->u.euse->name : rt->u.iuse->name, | ||||
| 			VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	if (g_hlr->euse_default) | ||||
| 		vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE); | ||||
|  | ||||
| 	if (g_hlr->ncss_guard_timeout != NCSS_GUARD_TIMEOUT_DEFAULT) | ||||
| 		vty_out(vty, " ncss-guard-timeout %i%s", | ||||
| 			g_hlr->ncss_guard_timeout, VTY_NEWLINE); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd, | ||||
| 	"ncss-guard-timeout <0-255>", | ||||
| 	"Set guard timer for NCSS (call independent SS) session activity\n" | ||||
| 	"Guard timer value (sec.), or 0 to disable") | ||||
| { | ||||
| 	g_hlr->ncss_guard_timeout = atoi(argv[0]); | ||||
| 	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 | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| int hlr_vty_go_parent(struct vty *vty) | ||||
| { | ||||
| 	switch (vty->node) { | ||||
| 	case GSUP_NODE: | ||||
| 	case EUSE_NODE: | ||||
| 		vty->node = HLR_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 		break; | ||||
| 	default: | ||||
| 	case HLR_NODE: | ||||
| 		vty->node = CONFIG_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		break; | ||||
| 	case CONFIG_NODE: | ||||
| 		vty->node = ENABLE_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return vty->node; | ||||
| } | ||||
|  | ||||
| int hlr_vty_is_config_node(struct vty *vty, int node) | ||||
| { | ||||
| 	switch (node) { | ||||
| 	/* add items that are not config */ | ||||
| 	case CONFIG_NODE: | ||||
| 		return 0; | ||||
|  | ||||
| 	default: | ||||
| 		return 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void hlr_vty_init(void) | ||||
| { | ||||
| 	logging_vty_add_cmds(); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	osmo_stats_vty_add_cmds(); | ||||
|  | ||||
| 	install_element_ve(&show_gsup_conn_cmd); | ||||
|  | ||||
| 	install_element(CONFIG_NODE, &cfg_hlr_cmd); | ||||
| 	install_node(&hlr_node, config_write_hlr); | ||||
|  | ||||
| 	install_element(HLR_NODE, &cfg_gsup_cmd); | ||||
| 	install_node(&gsup_node, config_write_hlr_gsup); | ||||
|  | ||||
| 	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_no_euse_cmd); | ||||
| 	install_node(&euse_node, config_write_euse); | ||||
| 	install_element(HLR_NODE, &cfg_ussd_route_pfx_int_cmd); | ||||
| 	install_element(HLR_NODE, &cfg_ussd_route_pfx_ext_cmd); | ||||
| 	install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd); | ||||
| 	install_element(HLR_NODE, &cfg_ussd_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_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(); | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/hlr_vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/hlr_vty.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| /* OsmoHLR VTY implementation */ | ||||
|  | ||||
| /* (C) 2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include "hlr.h" | ||||
|  | ||||
| enum hlr_vty_node { | ||||
| 	HLR_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	GSUP_NODE, | ||||
| 	EUSE_NODE, | ||||
| }; | ||||
|  | ||||
| int hlr_vty_is_config_node(struct vty *vty, int node); | ||||
| int hlr_vty_go_parent(struct vty *vty); | ||||
| void hlr_vty_init(void); | ||||
							
								
								
									
										621
									
								
								src/hlr_vty_subscr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										621
									
								
								src/hlr_vty_subscr.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,621 @@ | ||||
| /* OsmoHLR subscriber management VTY implementation */ | ||||
| /* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <inttypes.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| #include <sys/types.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #include <osmocom/gsm/gsm23003.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #include "hlr.h" | ||||
| #include "db.h" | ||||
|  | ||||
| struct vty; | ||||
|  | ||||
| #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) | ||||
|  | ||||
| static char * | ||||
| get_datestr(const time_t *t, char *datebuf) | ||||
| { | ||||
| 	char *p, *s = ctime_r(t, datebuf); | ||||
|  | ||||
| 	/* Strip trailing newline. */ | ||||
| 	p = strchr(s, '\n'); | ||||
| 	if (p) | ||||
| 		*p = '\0'; | ||||
| 	return s; | ||||
| } | ||||
|  | ||||
| static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	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, "    IMSI: %s%s", *subscr->imsi ? subscr->imsi : "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) | ||||
| 		vty_out(vty, "    VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE); | ||||
| 	if (*subscr->sgsn_number) | ||||
| 		vty_out(vty, "    SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE); | ||||
| 	if (*subscr->sgsn_address) | ||||
| 		vty_out(vty, "    SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE); | ||||
| 	if (subscr->periodic_lu_timer) | ||||
| 		vty_out(vty, "    Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE); | ||||
| 	if (subscr->periodic_rau_tau_timer) | ||||
| 		vty_out(vty, "    Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE); | ||||
| 	if (subscr->lmsi) | ||||
| 		vty_out(vty, "    LMSI: %x%s", subscr->lmsi, VTY_NEWLINE); | ||||
| 	if (!subscr->nam_cs) | ||||
| 		vty_out(vty, "    CS disabled%s", VTY_NEWLINE); | ||||
| 	if (subscr->ms_purged_cs) | ||||
| 		vty_out(vty, "    CS purged%s", VTY_NEWLINE); | ||||
| 	if (!subscr->nam_ps) | ||||
| 		vty_out(vty, "    PS disabled%s", VTY_NEWLINE); | ||||
| 	if (subscr->ms_purged_ps) | ||||
| 		vty_out(vty, "    PS purged%s", VTY_NEWLINE); | ||||
| 	if (subscr->last_lu_seen) | ||||
| 		vty_out(vty, "    last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE); | ||||
|  | ||||
| 	if (!*subscr->imsi) | ||||
| 		return; | ||||
|  | ||||
| 	OSMO_ASSERT(g_hlr); | ||||
| 	rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL); | ||||
|  | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 	case -ENOKEY: | ||||
| 		aud2g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		aud3g.algo = OSMO_AUTH_ALG_NONE; | ||||
| 		break; | ||||
| 	default: | ||||
| 		vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) { | ||||
| 		vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE); | ||||
| 		aud2g = (struct osmo_sub_auth_data){}; | ||||
| 	} | ||||
|  | ||||
| 	if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) { | ||||
| 		vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE); | ||||
| 		aud3g = (struct osmo_sub_auth_data){}; | ||||
| 	} | ||||
|  | ||||
| 	if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) { | ||||
| 		vty_out(vty, "    2G auth: %s%s", | ||||
| 			osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE); | ||||
| 		vty_out(vty, "             KI=%s%s", | ||||
| 			hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) { | ||||
| 		vty_out(vty, "    3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE); | ||||
| 		vty_out(vty, "             K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE); | ||||
| 		vty_out(vty, "             %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC", | ||||
| 			hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE); | ||||
| 		vty_out(vty, "             IND-bitlen=%u", aud3g.u.umts.ind_bitlen); | ||||
| 		if (aud3g.u.umts.sqn) | ||||
| 			vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn); | ||||
| 		vty_out(vty, VTY_NEWLINE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	if (strcmp(type, "imsi") == 0) | ||||
| 		rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr); | ||||
| 	else if (strcmp(type, "msisdn") == 0) | ||||
| 		rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr); | ||||
| 	else if (strcmp(type, "id") == 0) | ||||
| 		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) | ||||
| 		vty_out(vty, "%% No subscriber for %s = '%s'%s", | ||||
| 			type, id, VTY_NEWLINE); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| #define SUBSCR_CMD "subscriber " | ||||
| #define SUBSCR_CMD_HELP "Subscriber management commands\n" | ||||
|  | ||||
| #define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT" | ||||
| #define SUBSCR_ID_HELP \ | ||||
| 	"Identify subscriber by IMSI\n" \ | ||||
| 	"Identify subscriber by MSISDN (phone number)\n" \ | ||||
| 	"Identify subscriber by database ID\n" \ | ||||
| 	"Identify subscriber by IMEI\n" \ | ||||
| 	"IMSI/MSISDN/ID/IMEI of the subscriber\n" | ||||
|  | ||||
| #define SUBSCR 		SUBSCR_CMD SUBSCR_ID " " | ||||
| #define SUBSCR_HELP	SUBSCR_CMD_HELP SUBSCR_ID_HELP | ||||
|  | ||||
| #define SUBSCR_UPDATE		SUBSCR "update " | ||||
| #define SUBSCR_UPDATE_HELP	SUBSCR_HELP "Set or update subscriber data\n" | ||||
| #define SUBSCR_MSISDN_HELP	"Set MSISDN (phone number) of the subscriber\n" | ||||
|  | ||||
| DEFUN(subscriber_show, | ||||
|       subscriber_show_cmd, | ||||
|       SUBSCR "show", | ||||
|       SUBSCR_HELP "Show subscriber information\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	subscr_dump_full_vty(vty, &subscr); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| ALIAS(subscriber_show, show_subscriber_cmd, | ||||
|       "show " SUBSCR_CMD SUBSCR_ID, | ||||
|       SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP); | ||||
|  | ||||
| DEFUN(subscriber_create, | ||||
|       subscriber_create_cmd, | ||||
|       SUBSCR_CMD "imsi IDENT create", | ||||
|       SUBSCR_CMD_HELP | ||||
|       "Identify subscriber by IMSI\n" | ||||
|       "IMSI/MSISDN/ID of the subscriber\n" | ||||
|       "Create subscriber by IMSI\n") | ||||
| { | ||||
| 	int rc; | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	const char *imsi = argv[0]; | ||||
| 	 | ||||
| 	if (!osmo_imsi_str_valid(imsi)) { | ||||
| 		vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS); | ||||
|  | ||||
| 	if (rc) { | ||||
| 		if (rc == -EEXIST) | ||||
| 			vty_out(vty, "%% Subscriber already exists for IMSI = %s%s", | ||||
| 				imsi, VTY_NEWLINE); | ||||
| 		else | ||||
| 			vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s", | ||||
| 				rc, imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr); | ||||
| 	vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE); | ||||
|  | ||||
| 	subscr_dump_full_vty(vty, &subscr); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_delete, | ||||
|       subscriber_delete_cmd, | ||||
|       SUBSCR "delete", | ||||
|       SUBSCR_HELP "Delete subscriber from database\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
|  | ||||
| 	/* Find out the IMSI regardless of which way the caller decided to | ||||
| 	 * identify the subscriber by. */ | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id); | ||||
| 	if (rc) { | ||||
| 		vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_msisdn, | ||||
|       subscriber_msisdn_cmd, | ||||
|       SUBSCR_UPDATE "msisdn (none|MSISDN)", | ||||
|       SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP | ||||
|       "Remove MSISDN (phone number)\n" | ||||
|       "New MSISDN (phone number)\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	const char *msisdn = argv[2]; | ||||
|  | ||||
| 	if (strcmp(msisdn, "none") == 0) | ||||
| 		msisdn = NULL; | ||||
| 	else { | ||||
| 		if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) { | ||||
| 			vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s", | ||||
| 				sizeof(subscr.msisdn)-1, VTY_NEWLINE); | ||||
| 			return CMD_WARNING; | ||||
| 		} | ||||
|  | ||||
| 		if (!osmo_msisdn_str_valid(msisdn)) { | ||||
| 			vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE); | ||||
| 			return CMD_WARNING; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) { | ||||
| 		vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (msisdn) { | ||||
| 		vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s", | ||||
| 			subscr.imsi, msisdn, VTY_NEWLINE); | ||||
|  | ||||
| 		if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0) | ||||
| 			osmo_hlr_subscriber_update_notify(&subscr); | ||||
| 	} else { | ||||
| 		vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
|  | ||||
| 		osmo_hlr_subscriber_update_notify(&subscr); | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static bool is_hexkey_valid(struct vty *vty, const char *label, | ||||
| 			    const char *hex_str, int minlen, int maxlen) | ||||
| { | ||||
| 	if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true)) | ||||
| 		return true; | ||||
| 	vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| #define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)" | ||||
| #define AUTH_ALG_TYPES_2G_HELP \ | ||||
| 	"Use COMP128v1 algorithm\n" \ | ||||
| 	"Use COMP128v2 algorithm\n" \ | ||||
| 	"Use COMP128v3 algorithm\n" \ | ||||
| 	"Use XOR algorithm\n" | ||||
|  | ||||
| #define AUTH_ALG_TYPES_3G "milenage" | ||||
| #define AUTH_ALG_TYPES_3G_HELP \ | ||||
| 	"Use Milenage algorithm\n" | ||||
|  | ||||
| #define A38_XOR_MIN_KEY_LEN	12 | ||||
| #define A38_XOR_MAX_KEY_LEN	16 | ||||
| #define A38_COMP128_KEY_LEN	16 | ||||
|  | ||||
| #define MILENAGE_KEY_LEN 16 | ||||
|  | ||||
| static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo, | ||||
| 			    int *minlen, int *maxlen) | ||||
| { | ||||
| 	if (!strcasecmp(alg_str, "none")) { | ||||
| 		*algo = OSMO_AUTH_ALG_NONE; | ||||
| 		*minlen = *maxlen = 0; | ||||
| 	} else if (!strcasecmp(alg_str, "comp128v1")) { | ||||
| 		*algo = OSMO_AUTH_ALG_COMP128v1; | ||||
| 		*minlen = *maxlen = A38_COMP128_KEY_LEN; | ||||
| 	} else if (!strcasecmp(alg_str, "comp128v2")) { | ||||
| 		*algo = OSMO_AUTH_ALG_COMP128v2; | ||||
| 		*minlen = *maxlen = A38_COMP128_KEY_LEN; | ||||
| 	} else if (!strcasecmp(alg_str, "comp128v3")) { | ||||
| 		*algo = OSMO_AUTH_ALG_COMP128v3; | ||||
| 		*minlen = *maxlen = A38_COMP128_KEY_LEN; | ||||
| 	} else if (!strcasecmp(alg_str, "xor")) { | ||||
| 		*algo = OSMO_AUTH_ALG_XOR; | ||||
| 		*minlen = A38_XOR_MIN_KEY_LEN; | ||||
| 		*maxlen = A38_XOR_MAX_KEY_LEN; | ||||
| 	} else if (!strcasecmp(alg_str, "milenage")) { | ||||
| 		*algo = OSMO_AUTH_ALG_MILENAGE; | ||||
| 		*minlen = *maxlen = MILENAGE_KEY_LEN; | ||||
| 	} else | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_no_aud2g, | ||||
|       subscriber_no_aud2g_cmd, | ||||
|       SUBSCR_UPDATE "aud2g none", | ||||
|       SUBSCR_UPDATE_HELP | ||||
|       "Set 2G authentication data\n" | ||||
|       "Delete 2G authentication data\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	struct sub_auth_data_str aud = { | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = OSMO_AUTH_ALG_NONE, | ||||
| 	}; | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud); | ||||
|  | ||||
| 	if (rc && rc != -ENOENT) { | ||||
| 		vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_aud2g, | ||||
|       subscriber_aud2g_cmd, | ||||
|       SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI", | ||||
|       SUBSCR_UPDATE_HELP | ||||
|       "Set 2G authentication data\n" | ||||
|       AUTH_ALG_TYPES_2G_HELP | ||||
|       "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
| 	int minlen = 0; | ||||
| 	int maxlen = 0; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	const char *alg_type = argv[2]; | ||||
| 	const char *ki = argv[3]; | ||||
| 	struct sub_auth_data_str aud2g = { | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.u.gsm.ki = ki, | ||||
| 	}; | ||||
|  | ||||
| 	if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) { | ||||
| 		vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g); | ||||
|  | ||||
| 	if (rc) { | ||||
| 		vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_no_aud3g, | ||||
|       subscriber_no_aud3g_cmd, | ||||
|       SUBSCR_UPDATE "aud3g none", | ||||
|       SUBSCR_UPDATE_HELP | ||||
|       "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n" | ||||
|       "Delete 3G authentication data\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	struct sub_auth_data_str aud = { | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = OSMO_AUTH_ALG_NONE, | ||||
| 	}; | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud); | ||||
|  | ||||
| 	if (rc && rc != -ENOENT) { | ||||
| 		vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_aud3g, | ||||
|       subscriber_aud3g_cmd, | ||||
|       SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G | ||||
|       " k K" | ||||
|       " (op|opc) OP_C" | ||||
|       " [ind-bitlen] [<0-28>]", | ||||
|       SUBSCR_UPDATE_HELP | ||||
|       "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n" | ||||
|       AUTH_ALG_TYPES_3G_HELP | ||||
|       "Set Encryption Key K\n" "K as 32 hexadecimal characters\n" | ||||
|       "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n" | ||||
|       "Set IND bit length\n" "IND bit length value (default: 5)\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int minlen = 0; | ||||
| 	int maxlen = 0; | ||||
| 	int rc; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	const char *alg_type = AUTH_ALG_TYPES_3G; | ||||
| 	const char *k = argv[2]; | ||||
| 	bool opc_is_op = (strcasecmp("op", argv[3]) == 0); | ||||
| 	const char *op_opc = argv[4]; | ||||
| 	int ind_bitlen = argc > 6? atoi(argv[6]) : 5; | ||||
| 	struct sub_auth_data_str aud3g = { | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.u.umts = { | ||||
| 			.k = k, | ||||
| 			.opc_is_op = opc_is_op, | ||||
| 			.opc = op_opc, | ||||
| 			.ind_bitlen = ind_bitlen, | ||||
| 		}, | ||||
| 	}; | ||||
| 	 | ||||
| 	if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) { | ||||
| 		vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc, | ||||
| 			     MILENAGE_KEY_LEN, MILENAGE_KEY_LEN)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g); | ||||
|  | ||||
| 	if (rc) { | ||||
| 		vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s", | ||||
| 			subscr.imsi, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(subscriber_imei, | ||||
|       subscriber_imei_cmd, | ||||
|       SUBSCR_UPDATE "imei (none|IMEI)", | ||||
|       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; | ||||
| } | ||||
|  | ||||
|  | ||||
| void hlr_vty_subscriber_init(void) | ||||
| { | ||||
| 	install_element_ve(&subscriber_show_cmd); | ||||
| 	install_element_ve(&show_subscriber_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_create_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_delete_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_msisdn_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_aud2g_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_aud3g_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_imei_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_nam_cmd); | ||||
| } | ||||
							
								
								
									
										3
									
								
								src/hlr_vty_subscr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/hlr_vty_subscr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| #pragma once | ||||
|  | ||||
| void hlr_vty_subscriber_init(void); | ||||
| @@ -5,26 +5,27 @@ const struct log_info_cat hlr_log_info_cat[] = { | ||||
| 	[DMAIN] = { | ||||
| 		.name = "DMAIN", | ||||
| 		.description = "Main Program", | ||||
| 		.enabled = 1, .loglevel = LOGL_DEBUG, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DDB] = { | ||||
| 		.name = "DDB", | ||||
| 		.description = "Database Layer", | ||||
| 		.color = "\033[1;31m", | ||||
| 		.enabled = 1, .loglevel = LOGL_DEBUG, | ||||
| 	}, | ||||
| 	[DLGSUP] = { | ||||
| 		.name = "DLGSUP", | ||||
| 		.description = "GSUP Protocol", | ||||
| 		.color = "\033[1;32m", | ||||
| 		.enabled = 1, .loglevel = LOGL_INFO, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DAUC] = { | ||||
| 		.name = "DAUC", | ||||
| 		.description = "Authentication Center", | ||||
| 		.color = "\033[1;33m", | ||||
| 		.enabled = 1, .loglevel = LOGL_DEBUG, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DSS] = { | ||||
| 		.name = "DSS", | ||||
| 		.description = "Supplementary Services", | ||||
| 		.color = "\033[1;34m", | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
|  | ||||
| }; | ||||
|  | ||||
| const struct log_info hlr_log_info = { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ enum { | ||||
| 	DDB, | ||||
| 	DGSUP, | ||||
| 	DAUC, | ||||
| 	DSS, | ||||
| }; | ||||
|  | ||||
| extern const struct log_info hlr_log_info; | ||||
|   | ||||
							
								
								
									
										259
									
								
								src/luop.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/luop.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,259 @@ | ||||
| /* OsmoHLR TX/RX lu operations */ | ||||
|  | ||||
| /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include <osmocom/gsm/apn.h> | ||||
|  | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
| #include "logging.h" | ||||
| #include "luop.h" | ||||
|  | ||||
| const struct value_string lu_state_names[] = { | ||||
| 	{ LU_S_NULL,			"NULL" }, | ||||
| 	{ LU_S_LU_RECEIVED,		"LU RECEIVED" }, | ||||
| 	{ LU_S_CANCEL_SENT,		"CANCEL SENT" }, | ||||
| 	{ LU_S_CANCEL_ACK_RECEIVED,	"CANCEL-ACK RECEIVED" }, | ||||
| 	{ LU_S_ISD_SENT,		"ISD SENT" }, | ||||
| 	{ LU_S_ISD_ACK_RECEIVED,	"ISD-ACK RECEIVED" }, | ||||
| 	{ LU_S_COMPLETE,		"COMPLETE" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| /* Transmit a given GSUP message for the given LU operation */ | ||||
| static void _luop_tx_gsup(struct lu_operation *luop, | ||||
| 			  const struct osmo_gsup_message *gsup) | ||||
| { | ||||
| 	struct msgb *msg_out; | ||||
|  | ||||
| 	msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP"); | ||||
| 	OSMO_ASSERT(msg_out); | ||||
| 	osmo_gsup_encode(msg_out, gsup); | ||||
|  | ||||
| 	osmo_gsup_addr_send(luop->gsup_server, luop->peer, | ||||
| 			    talloc_total_size(luop->peer), | ||||
| 			    msg_out); | ||||
| } | ||||
|  | ||||
| static inline void fill_gsup_msg(struct osmo_gsup_message *out, | ||||
| 				 const struct lu_operation *lu, | ||||
| 				 enum osmo_gsup_message_type mt) | ||||
| { | ||||
| 	memset(out, 0, sizeof(struct osmo_gsup_message)); | ||||
| 	if (lu) | ||||
| 		osmo_strlcpy(out->imsi, lu->subscr.imsi, | ||||
| 			     GSM23003_IMSI_MAX_DIGITS + 1); | ||||
| 	out->message_type = mt; | ||||
| } | ||||
|  | ||||
| /* timer call-back in case LU operation doesn't receive an response */ | ||||
| static void lu_op_timer_cb(void *data) | ||||
| { | ||||
| 	struct lu_operation *luop = data; | ||||
|  | ||||
| 	DEBUGP(DMAIN, "LU OP timer expired in state %s\n", | ||||
| 		get_value_string(lu_state_names, luop->state)); | ||||
|  | ||||
| 	switch (luop->state) { | ||||
| 	case LU_S_CANCEL_SENT: | ||||
| 		break; | ||||
| 	case LU_S_ISD_SENT: | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL); | ||||
| } | ||||
|  | ||||
| bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc, | ||||
| 		       const char *imsi) | ||||
| { | ||||
| 	struct hlr_subscriber *subscr = &luop->subscr; | ||||
|  | ||||
| 	if (db_subscr_get_by_imsi(dbc, imsi, subscr) < 0) | ||||
| 		return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv) | ||||
| { | ||||
| 	struct lu_operation *luop; | ||||
|  | ||||
| 	luop = talloc_zero(srv, struct lu_operation); | ||||
| 	OSMO_ASSERT(luop); | ||||
| 	luop->gsup_server = srv; | ||||
| 	osmo_timer_setup(&luop->timer, lu_op_timer_cb, luop); | ||||
|  | ||||
| 	return luop; | ||||
| } | ||||
|  | ||||
| void lu_op_free(struct lu_operation *luop) | ||||
| { | ||||
| 	/* Only attempt to remove when it was ever added to a list. */ | ||||
| 	if (luop->list.next) | ||||
| 		llist_del(&luop->list); | ||||
|  | ||||
| 	/* Delete timer just in case it is still pending. */ | ||||
| 	osmo_timer_del(&luop->timer); | ||||
|  | ||||
| 	talloc_free(luop); | ||||
| } | ||||
|  | ||||
| struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn) | ||||
| { | ||||
| 	uint8_t *peer_addr; | ||||
| 	struct lu_operation *luop = lu_op_alloc(conn->server); | ||||
| 	int rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR); | ||||
| 	if (rc < 0) { | ||||
| 		lu_op_free(luop); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	luop->peer = talloc_memdup(luop, peer_addr, rc); | ||||
|  | ||||
| 	return luop; | ||||
| } | ||||
|  | ||||
| /* FIXME: this doesn't seem to work at all */ | ||||
| struct lu_operation *lu_op_by_imsi(const char *imsi, | ||||
| 				   const struct llist_head *lst) | ||||
| { | ||||
| 	struct lu_operation *luop; | ||||
|  | ||||
| 	llist_for_each_entry(luop, lst, list) { | ||||
| 		if (!strcmp(imsi, luop->subscr.imsi)) | ||||
| 			return luop; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state) | ||||
| { | ||||
| 	enum lu_state old_state = luop->state; | ||||
|  | ||||
| 	DEBUGP(DMAIN, "LU OP state change: %s -> ", | ||||
| 		get_value_string(lu_state_names, old_state)); | ||||
| 	DEBUGPC(DMAIN, "%s\n", | ||||
| 		get_value_string(lu_state_names, new_state)); | ||||
|  | ||||
| 	luop->state = new_state; | ||||
| } | ||||
|  | ||||
| /*! Transmit UPD_LOC_ERROR and destroy lu_operation */ | ||||
| void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup; | ||||
|  | ||||
| 	DEBUGP(DMAIN, "%s: LU OP Tx Error (cause %s)\n", | ||||
| 	       luop->subscr.imsi, get_value_string(gsm48_gmm_cause_names, | ||||
| 						   cause)); | ||||
|  | ||||
| 	fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR); | ||||
| 	gsup.cause = cause; | ||||
|  | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
|  | ||||
| 	lu_op_free(luop); | ||||
| } | ||||
|  | ||||
| /*! Transmit UPD_LOC_RESULT and destroy lu_operation */ | ||||
| void lu_op_tx_ack(struct lu_operation *luop) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup; | ||||
|  | ||||
| 	fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT); | ||||
| 	//FIXME gsup.hlr_enc; | ||||
|  | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
|  | ||||
| 	lu_op_free(luop); | ||||
| } | ||||
|  | ||||
| /*! Send Cancel Location to old VLR/SGSN */ | ||||
| void lu_op_tx_cancel_old(struct lu_operation *luop) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup; | ||||
|  | ||||
| 	OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED); | ||||
|  | ||||
| 	fill_gsup_msg(&gsup, NULL, OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST); | ||||
| 	//gsup.cause = FIXME; | ||||
| 	//gsup.cancel_type = FIXME; | ||||
|  | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
|  | ||||
| 	lu_op_statechg(luop, LU_S_CANCEL_SENT); | ||||
| 	osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0); | ||||
| } | ||||
|  | ||||
| /*! Transmit Insert Subscriber Data to new VLR/SGSN */ | ||||
| void lu_op_tx_insert_subscr_data(struct lu_operation *luop) | ||||
| { | ||||
| 	struct hlr_subscriber *subscr = &luop->subscr; | ||||
| 	struct osmo_gsup_message gsup = { }; | ||||
| 	uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN]; | ||||
| 	uint8_t apn[APN_MAXLEN]; | ||||
| 	enum osmo_gsup_cn_domain cn_domain; | ||||
|  | ||||
| 	OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED || | ||||
| 		    luop->state == LU_S_CANCEL_ACK_RECEIVED); | ||||
|  | ||||
| 	if (luop->is_ps) | ||||
| 		cn_domain = OSMO_GSUP_CN_DOMAIN_PS; | ||||
| 	else | ||||
| 		cn_domain = OSMO_GSUP_CN_DOMAIN_CS; | ||||
|  | ||||
| 	if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc, | ||||
| 							sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, | ||||
| 		       "IMSI='%s': Cannot notify GSUP client; could not create gsup message " | ||||
| 		       "for %s\n", subscr->imsi, luop->peer); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Send ISD to new VLR/SGSN */ | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
|  | ||||
| 	lu_op_statechg(luop, LU_S_ISD_SENT); | ||||
| 	osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0); | ||||
| } | ||||
|  | ||||
| /*! Transmit Delete Subscriber Data to new VLR/SGSN. | ||||
|  * The luop is not freed. */ | ||||
| void lu_op_tx_del_subscr_data(struct lu_operation *luop) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup; | ||||
|  | ||||
| 	fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_DELETE_DATA_REQUEST); | ||||
|  | ||||
| 	gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_PS; | ||||
|  | ||||
| 	/* Send ISD to new VLR/SGSN */ | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
| } | ||||
							
								
								
									
										81
									
								
								src/luop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/luop.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| /* OsmoHLR TX/RX lu operations */ | ||||
|  | ||||
| /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| #define CANCEL_TIMEOUT_SECS	30 | ||||
| #define ISD_TIMEOUT_SECS	30 | ||||
|  | ||||
| enum lu_state { | ||||
| 	LU_S_NULL, | ||||
| 	LU_S_LU_RECEIVED, | ||||
| 	LU_S_CANCEL_SENT, | ||||
| 	LU_S_CANCEL_ACK_RECEIVED, | ||||
| 	LU_S_ISD_SENT, | ||||
| 	LU_S_ISD_ACK_RECEIVED, | ||||
| 	LU_S_COMPLETE, | ||||
| }; | ||||
|  | ||||
| extern const struct value_string lu_state_names[]; | ||||
|  | ||||
| struct lu_operation { | ||||
| 	/*! entry in global list of location update operations */ | ||||
| 	struct llist_head list; | ||||
| 	/*! to which gsup_server do we belong */ | ||||
| 	struct osmo_gsup_server *gsup_server; | ||||
| 	/*! state of the location update */ | ||||
| 	enum lu_state state; | ||||
| 	/*! CS (false) or PS (true) Location Update? */ | ||||
| 	bool is_ps; | ||||
| 	/*! currently running timer */ | ||||
| 	struct osmo_timer_list timer; | ||||
|  | ||||
| 	/*! subscriber related to this operation */ | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	/*! peer VLR/SGSN starting the request */ | ||||
| 	uint8_t *peer; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv); | ||||
| struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn); | ||||
| void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state); | ||||
| bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc, | ||||
| 		       const char *imsi); | ||||
| struct lu_operation *lu_op_by_imsi(const char *imsi, | ||||
| 				   const struct llist_head *lst); | ||||
|  | ||||
| void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause); | ||||
| void lu_op_tx_ack(struct lu_operation *luop); | ||||
| void lu_op_tx_cancel_old(struct lu_operation *luop); | ||||
| void lu_op_tx_insert_subscr_data(struct lu_operation *luop); | ||||
| void lu_op_tx_del_subscr_data(struct lu_operation *luop); | ||||
|  | ||||
| void lu_op_free(struct lu_operation *luop); | ||||
							
								
								
									
										239
									
								
								src/osmo-euse-demo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								src/osmo-euse-demo.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| /* osmo-demo-euse: An External USSD Entity (EUSE) for demo purpose */ | ||||
|  | ||||
| /* (C) 2018 by Harald Welte <laforge@gnumonks.org> | ||||
|  * | ||||
|  * 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/>. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * This program illustrates how to implement an external USSD application using | ||||
|  * the existing osmocom libraries, particularly libosmocore, libosmogsm and libosmo-gsup-client. | ||||
|  * | ||||
|  * It will receive any MS-originated USSD message that is routed to it via the HLR, and | ||||
|  * simply respond it quoted in the following string: 'You sent "foobar"' (assuming the original | ||||
|  * message was 'foobar'). | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <signal.h> | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include <osmocom/gsm/gsm0480.h> | ||||
| #include <osmocom/gsm/protocol/gsm_04_80.h> | ||||
|  | ||||
| #include <osmocom/gsupclient/gsup_client.h> | ||||
|  | ||||
| #include "logging.h" | ||||
|  | ||||
| static struct osmo_gsup_client *g_gc; | ||||
|  | ||||
| /*! send a SS/USSD response to a given imsi/session. | ||||
|  *  \param[in] gsupc GSUP client connection through which to send | ||||
|  *  \param[in] imsi IMSI of the subscriber | ||||
|  *  \param[in] session_id Unique identifier of SS session for which this response is | ||||
|  *  \param[in] gsup_msg_type GSUP message type (OSMO_GSUP_MSGT_PROC_SS_{REQUEST,RESULT,ERROR}) | ||||
|  *  \param[in] final Is this the final result (true=END) or an intermediate result (false=CONTINUE) | ||||
|  *  \param[in] msg Optional binary/BER encoded SS date (for FACILITY IE). Can be NULL. Freed in | ||||
|  *  		   this function call. | ||||
|  */ | ||||
| static int euse_tx_ss(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | ||||
| 		      enum osmo_gsup_message_type gsup_msg_type, bool final, struct msgb *ss_msg) | ||||
| { | ||||
| 	struct osmo_gsup_message resp = {0}; | ||||
| 	struct msgb *resp_msg; | ||||
|  | ||||
| 	switch (gsup_msg_type) { | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_REQUEST: | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_RESULT: | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_ERROR: | ||||
| 		break; | ||||
| 	default: | ||||
| 		msgb_free(ss_msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	resp.message_type = gsup_msg_type; | ||||
| 	OSMO_STRLCPY_ARRAY(resp.imsi, imsi); | ||||
| 	if (final) | ||||
| 		resp.session_state = OSMO_GSUP_SESSION_STATE_END; | ||||
| 	else | ||||
| 		resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE; | ||||
| 	resp.session_id = session_id; | ||||
| 	if (ss_msg) { | ||||
| 		resp.ss_info = msgb_data(ss_msg); | ||||
| 		resp.ss_info_len = msgb_length(ss_msg); | ||||
| 	} | ||||
|  | ||||
| 	resp_msg = gsm0480_msgb_alloc_name(__func__); | ||||
| 	OSMO_ASSERT(resp_msg); | ||||
| 	osmo_gsup_encode(resp_msg, &resp); | ||||
| 	msgb_free(ss_msg); | ||||
| 	return osmo_gsup_client_send(gsupc, resp_msg); | ||||
| } | ||||
|  | ||||
| /*! send a SS/USSD reject to a given IMSI/session. | ||||
|  * \param[in] gsupc		GSUP client connection through which to send | ||||
|  * \param[in] imsi		IMSI of the subscriber | ||||
|  * \param[in] session_id	Unique identifier of SS session for which this response is | ||||
|  * \param[in] invoke_id		InvokeID of the request | ||||
|  * \param[in] problem_tag	Problem code tag (table 3.13) | ||||
|  * \param[in] problem_code	Problem code (table 3.14-3.17) | ||||
|  */ | ||||
| static int euse_tx_ussd_reject(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | ||||
| 				int invoke_id, uint8_t problem_tag, uint8_t problem_code) | ||||
| { | ||||
| 	struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code); | ||||
| 	LOGP(DMAIN, LOGL_NOTICE, "Tx %s/0x%08x: Reject(%d, 0x%02x, 0x%02x)\n", imsi, session_id, | ||||
| 		invoke_id, problem_tag, problem_code); | ||||
| 	OSMO_ASSERT(msg); | ||||
| 	return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg); | ||||
| } | ||||
|  | ||||
| /*! send a SS/USSD response in 7-bit GSM default alphabet o a given imsi/session. | ||||
|  * \param[in] gsupc		GSUP client connection through which to send | ||||
|  * \param[in] imsi		IMSI of the subscriber | ||||
|  * \param[in] session_id	Unique identifier of SS session for which this response is | ||||
|  * \param[in] final		Is this the final result (true=END) or an intermediate result | ||||
|  * 				(false=CONTINUE) | ||||
|  * \param[in] invoke_id		InvokeID of the request | ||||
|  */ | ||||
| static int euse_tx_ussd_resp_7bit(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id, | ||||
| 				  bool final, uint8_t invoke_id, const char *text) | ||||
| { | ||||
| 	struct msgb *ss_msg; | ||||
|  | ||||
| 	/* encode response; remove L3 header */ | ||||
| 	ss_msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); | ||||
| 	LOGP(DMAIN, LOGL_DEBUG, "Tx %s/0x%08x: USSD Result(%d, %s, '%s')\n", imsi, session_id, | ||||
| 		invoke_id, final ? "END" : "CONTINUE", text); | ||||
| 	OSMO_ASSERT(ss_msg); | ||||
| 	return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, ss_msg); | ||||
| } | ||||
|  | ||||
| static int euse_rx_proc_ss_req(struct osmo_gsup_client *gsupc, const struct osmo_gsup_message *gsup) | ||||
| { | ||||
| 	char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; | ||||
| 	struct ss_request req = {0}; | ||||
|  | ||||
| 	if (gsup->ss_info && gsup->ss_info_len) { | ||||
| 		if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) { | ||||
| 			return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, -1, | ||||
| 						   GSM_0480_PROBLEM_CODE_TAG_GENERAL, | ||||
| 						   GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DMAIN, LOGL_INFO, "Rx %s/0x%08x: USSD SessionState=%s, OpCode=%s, '%s'\n", gsup->imsi, | ||||
| 		gsup->session_id, osmo_gsup_session_state_name(gsup->session_state), | ||||
| 		gsm0480_op_code_name(req.opcode), req.ussd_text); | ||||
|  | ||||
| 	/* we only handle single-request-response USSD in this demo */ | ||||
| 	if (gsup->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) { | ||||
| 		return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, req.invoke_id, | ||||
| 					   GSM_0480_PROBLEM_CODE_TAG_GENERAL, | ||||
| 					   GSM_0480_GEN_PROB_CODE_UNRECOGNISED); | ||||
| 	} | ||||
|  | ||||
| 	snprintf(buf, sizeof(buf), "You sent \"%s\"", req.ussd_text); | ||||
| 	return euse_tx_ussd_resp_7bit(gsupc, gsup->imsi, gsup->session_id, true, req.invoke_id, buf); | ||||
| } | ||||
|  | ||||
| static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup_msg = {0}; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "Error decoding GSUP: %s\n", msgb_hexdump(msg)); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	DEBUGP(DMAIN, "Rx GSUP %s: %s\n", osmo_gsup_message_type_name(gsup_msg.message_type), | ||||
| 		msgb_hexdump(msg)); | ||||
|  | ||||
| 	switch (gsup_msg.message_type) { | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_REQUEST: | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_RESULT: | ||||
| 		euse_rx_proc_ss_req(gsupc, &gsup_msg); | ||||
| 		break; | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_ERROR: | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n", | ||||
| 			osmo_gsup_message_type_name(gsup_msg.message_type)); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static struct log_info_cat default_categories[] = { | ||||
| 	[DMAIN] = { | ||||
| 		.name = "DMAIN", | ||||
| 		.description = "Main Program", | ||||
| 		.enabled = 1, .loglevel = LOGL_DEBUG, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static const struct log_info gsup_log_info = { | ||||
| 	.cat = default_categories, | ||||
| 	.num_cat = ARRAY_SIZE(default_categories), | ||||
| }; | ||||
|  | ||||
| static void print_usage(void) | ||||
| { | ||||
| 	printf("Usage: osmo-euse-demo [hlr-ip [hlr-gsup-port]]\n"); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	char *server_host = "127.0.0.1"; | ||||
| 	uint16_t server_port = OSMO_GSUP_PORT; | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "demo-euse"); | ||||
|  | ||||
| 	osmo_init_logging2(ctx, &gsup_log_info); | ||||
|  | ||||
| 	printf("argc=%d\n", argc); | ||||
|  | ||||
| 	if (argc > 1) { | ||||
| 		if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { | ||||
| 			print_usage(); | ||||
| 			exit(0); | ||||
| 		} else | ||||
| 			server_host = argv[1]; | ||||
| 	} | ||||
| 	if (argc > 2) | ||||
| 		server_port = atoi(argv[2]); | ||||
|  | ||||
| 	g_gc = osmo_gsup_client_create(ctx, "EUSE-foobar", server_host, server_port, gsupc_read_cb, NULL); | ||||
|  | ||||
| 	while (1) { | ||||
| 		osmo_select_main(0); | ||||
| 	} | ||||
|  | ||||
| 	exit(0); | ||||
| } | ||||
|  | ||||
							
								
								
									
										100
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| SUBDIRS = \ | ||||
| 	auc \ | ||||
| 	gsup_server \ | ||||
| 	db \ | ||||
| 	gsup \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # The `:;' works around a Bash 3.2 bug when the output is not writeable. | ||||
| $(srcdir)/package.m4: $(top_srcdir)/configure.ac | ||||
| 	:;{ \ | ||||
|                echo '# Signature of the current package.' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_NAME],' && \ | ||||
|                echo '  [$(PACKAGE_NAME)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_TARNAME],' && \ | ||||
|                echo '  [$(PACKAGE_TARNAME)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_VERSION],' && \ | ||||
|                echo '  [$(PACKAGE_VERSION)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_STRING],' && \ | ||||
|                echo '  [$(PACKAGE_STRING)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ | ||||
|                echo '  [$(PACKAGE_BUGREPORT)])'; \ | ||||
|                echo 'm4_define([AT_PACKAGE_URL],' && \ | ||||
|                echo '  [$(PACKAGE_URL)])'; \ | ||||
|              } >'$(srcdir)/package.m4' | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	testsuite.at \ | ||||
| 	$(srcdir)/package.m4 \ | ||||
| 	$(TESTSUITE) \ | ||||
| 	test_nodes.vty \ | ||||
| 	test_subscriber.vty \ | ||||
| 	test_subscriber.sql \ | ||||
| 	test_subscriber.ctrl \ | ||||
| 	$(NULL) | ||||
|  | ||||
| TESTSUITE = $(srcdir)/testsuite | ||||
|  | ||||
| DISTCLEANFILES = \ | ||||
| 	atconfig \ | ||||
| 	$(NULL) | ||||
|  | ||||
| if ENABLE_EXT_TESTS | ||||
| python-tests: | ||||
| # don't run vty and ctrl tests concurrently so that the ports don't conflict | ||||
| 	$(MAKE) vty-test | ||||
| 	$(MAKE) ctrl-test | ||||
| else | ||||
| python-tests: | ||||
| 	echo "Not running python-based external tests (determined at configure-time)" | ||||
| endif | ||||
|  | ||||
| VTY_TEST_DB = hlr_vty_test.db | ||||
|  | ||||
| # To update the VTY script from current application behavior, | ||||
| # pass -u to vty_script_runner.py by doing: | ||||
| #   make vty-test U=-u | ||||
| vty-test: | ||||
| 	-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 \ | ||||
| 		-n OsmoHLR -p 4258 \ | ||||
| 		-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(VTY_TEST_DB)" \ | ||||
| 		$(U) $(srcdir)/*.vty | ||||
| 	-rm -f $(VTY_TEST_DB) | ||||
| 	-rm $(VTY_TEST_DB)-* | ||||
|  | ||||
| CTRL_TEST_DB = hlr_ctrl_test.db | ||||
|  | ||||
| # To update the CTRL script from current application behavior, | ||||
| # pass -u to ctrl_script_runner.py by doing: | ||||
| #   make ctrl-test U=-u | ||||
| ctrl-test: | ||||
| 	-rm -f $(CTRL_TEST_DB) | ||||
| 	sqlite3 $(CTRL_TEST_DB) < $(top_srcdir)/sql/hlr.sql | ||||
| 	sqlite3 $(CTRL_TEST_DB) < $(srcdir)/test_subscriber.sql | ||||
| 	osmo_verify_transcript_ctrl.py -v \ | ||||
| 		-p 4259 \ | ||||
| 		-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \ | ||||
| 		$(U) $(srcdir)/*.ctrl | ||||
| 	-rm -f $(CTRL_TEST_DB) | ||||
| 	-rm $(CTRL_TEST_DB)-* | ||||
|  | ||||
| check-local: atconfig $(TESTSUITE) | ||||
| 	$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) | ||||
| 	$(MAKE) $(AM_MAKEFLAGS) python-tests | ||||
|  | ||||
| installcheck-local: atconfig $(TESTSUITE) | ||||
| 	$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ | ||||
| 		$(TESTSUITEFLAGS) | ||||
|  | ||||
| clean-local: | ||||
| 	test ! -f '$(TESTSUITE)' || \ | ||||
| 		$(SHELL) '$(TESTSUITE)' --clean | ||||
|  | ||||
| AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te | ||||
| AUTOTEST = $(AUTOM4TE) --language=autotest | ||||
| $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 | ||||
| 	$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at | ||||
| 	mv $@.tmp $@ | ||||
							
								
								
									
										58
									
								
								tests/auc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/auc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| SUBDIRS = gen_ts_55_205_test_sets | ||||
|  | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/src \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	auc_test.ok \ | ||||
| 	auc_test.err \ | ||||
| 	auc_ts_55_205_test_sets.ok \ | ||||
| 	auc_ts_55_205_test_sets.err \ | ||||
| 	$(NULL) | ||||
|  | ||||
| check_PROGRAMS = auc_ts_55_205_test_sets | ||||
|  | ||||
| noinst_PROGRAMS = auc_test | ||||
|  | ||||
| auc_test_SOURCES = \ | ||||
| 	auc_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| auc_test_LDADD = \ | ||||
| 	$(top_srcdir)/src/auc.c \ | ||||
| 	$(top_srcdir)/src/logging.c \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| auc_ts_55_205_test_sets_SOURCES = \ | ||||
| 	$(builddir)/auc_ts_55_205_test_sets.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| auc_ts_55_205_test_sets_LDADD = \ | ||||
| 	$(top_srcdir)/src/auc.c \ | ||||
| 	$(top_srcdir)/src/logging.c \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| auc_ts_55_205_test_sets.c: $(top_srcdir)/tests/auc/gen_ts_55_205_test_sets/* | ||||
| 	$(top_srcdir)/tests/auc/gen_ts_55_205_test_sets/pdftxt_2_c.py > $@ | ||||
|  | ||||
| .PHONY: update_exp | ||||
| update_exp: | ||||
| 	$(builddir)/auc_test >"$(srcdir)/auc_test.ok" 2>"$(srcdir)/auc_test.err" | ||||
| 	$(builddir)/auc_ts_55_205_test_sets >"$(srcdir)/auc_ts_55_205_test_sets.ok" 2>"$(srcdir)/auc_ts_55_205_test_sets.err" | ||||
							
								
								
									
										629
									
								
								tests/auc/auc_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										629
									
								
								tests/auc/auc_test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,629 @@ | ||||
| /* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| #include <getopt.h> | ||||
|  | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #include <osmocom/crypt/auth.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "auc.h" | ||||
|  | ||||
| #define comment_start() fprintf(stderr, "\n===== %s\n", __func__); | ||||
| #define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__); | ||||
|  | ||||
| #define VERBOSE_ASSERT(val, expect_op, fmt) \ | ||||
| 	do { \ | ||||
| 		fprintf(stderr, #val " == " fmt "\n", (val)); \ | ||||
| 		OSMO_ASSERT((val) expect_op); \ | ||||
| 	} while (0); | ||||
|  | ||||
| char *vec_str(const struct osmo_auth_vector *vec) | ||||
| { | ||||
| 	static char buf[1024]; | ||||
| 	char *pos = buf; | ||||
| 	char *end = buf + sizeof(buf); | ||||
|  | ||||
| #define append(what) \ | ||||
| 	if (pos >= end) \ | ||||
| 		return buf; \ | ||||
| 	pos += snprintf(pos, sizeof(buf) - (pos - buf), \ | ||||
|                         "  " #what ": %s\n", \ | ||||
| 			osmo_hexdump_nospc((void*)&vec->what, sizeof(vec->what))) | ||||
|  | ||||
| 	append(rand); | ||||
| 	append(autn); | ||||
| 	append(ck); | ||||
| 	append(ik); | ||||
| 	append(res); | ||||
| 	append(res_len); | ||||
| 	append(kc); | ||||
| 	append(sres); | ||||
| 	append(auth_types); | ||||
| #undef append | ||||
|  | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| #define VEC_IS(vec, expect) do { \ | ||||
| 		char *_is = vec_str(vec); \ | ||||
| 	        if (strcmp(_is, expect)) { \ | ||||
| 			fprintf(stderr, "MISMATCH! expected ==\n%s\n", \ | ||||
| 				expect); \ | ||||
| 			char *a = _is; \ | ||||
| 			char *b = expect; \ | ||||
| 			for (; *a && *b; a++, b++) { \ | ||||
| 				if (*a != *b) { \ | ||||
| 					fprintf(stderr, "mismatch at %d:\n", \ | ||||
| 						(int)(a - _is)); \ | ||||
| 					while (a > _is && *(a-1) != '\n') { \ | ||||
| 						fprintf(stderr, " "); \ | ||||
| 						a--; \ | ||||
| 					} \ | ||||
| 					fprintf(stderr, "v\n%s", a); \ | ||||
| 					break; \ | ||||
| 				} \ | ||||
| 			} \ | ||||
| 			OSMO_ASSERT(false); \ | ||||
| 		} else \ | ||||
| 			fprintf(stderr, "vector matches expectations\n"); \ | ||||
| 	} while (0) | ||||
|  | ||||
| uint8_t fake_rand[16] = { 0 }; | ||||
| bool fake_rand_fixed = true; | ||||
|  | ||||
| void next_rand(const char *hexstr, bool fixed) | ||||
| { | ||||
| 	osmo_hexparse(hexstr, fake_rand, sizeof(fake_rand)); | ||||
| 	fake_rand_fixed = fixed; | ||||
| } | ||||
|  | ||||
| int rand_get(uint8_t *rand, unsigned int len) | ||||
| { | ||||
| 	int i; | ||||
| 	OSMO_ASSERT(len <= sizeof(fake_rand)); | ||||
| 	memcpy(rand, fake_rand, len); | ||||
| 	if (!fake_rand_fixed) { | ||||
| 		for (i = 0; i < len; i++) | ||||
| 			fake_rand[i] += 0x11; | ||||
| 	} | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| static void test_gen_vectors_2g_only(void) | ||||
| { | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	struct osmo_auth_vector vec; | ||||
| 	int rc; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	aud2g = (struct osmo_sub_auth_data){ | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = OSMO_AUTH_ALG_COMP128v1, | ||||
| 	}; | ||||
|  | ||||
| 	osmo_hexparse("EB215756028D60E3275E613320AEC880", | ||||
| 		      aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); | ||||
|  | ||||
| 	aud3g = (struct osmo_sub_auth_data){ 0 }; | ||||
|  | ||||
| 	next_rand("39fa2f4e3d523d8619a73b4f65c3e14d", true); | ||||
|  | ||||
| 	vec = (struct osmo_auth_vector){ {0} }; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 00000000000000000000000000000000\n" | ||||
| 	       "  ck: 00000000000000000000000000000000\n" | ||||
| 	       "  ik: 00000000000000000000000000000000\n" | ||||
| 	       "  res: 00000000000000000000000000000000\n" | ||||
| 	       "  res_len: 00\n" | ||||
| 	       "  kc: 241a5b16aeb8e400\n" | ||||
| 	       "  sres: 429d5b27\n" | ||||
| 	       "  auth_types: 01000000\n" | ||||
| 	      ); | ||||
|  | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); | ||||
|  | ||||
| 	/* even though vec is not zero-initialized, it should produce the same | ||||
| 	 * result (regardless of the umts sequence nr) */ | ||||
| 	aud3g.u.umts.sqn = 123; | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 00000000000000000000000000000000\n" | ||||
| 	       "  ck: 00000000000000000000000000000000\n" | ||||
| 	       "  ik: 00000000000000000000000000000000\n" | ||||
| 	       "  res: 00000000000000000000000000000000\n" | ||||
| 	       "  res_len: 00\n" | ||||
| 	       "  kc: 241a5b16aeb8e400\n" | ||||
| 	       "  sres: 429d5b27\n" | ||||
| 	       "  auth_types: 01000000\n" | ||||
| 	      ); | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| static void test_gen_vectors_2g_plus_3g(void) | ||||
| { | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	struct osmo_auth_vector vec; | ||||
| 	int rc; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	aud2g = (struct osmo_sub_auth_data){ | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = OSMO_AUTH_ALG_COMP128v1, | ||||
| 	}; | ||||
|  | ||||
| 	osmo_hexparse("EB215756028D60E3275E613320AEC880", | ||||
| 		      aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); | ||||
|  | ||||
| 	aud3g = (struct osmo_sub_auth_data){ | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = OSMO_AUTH_ALG_MILENAGE, | ||||
| 		.u.umts.sqn = 31, | ||||
| 	}; | ||||
|  | ||||
| 	osmo_hexparse("EB215756028D60E3275E613320AEC880", | ||||
| 		      aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); | ||||
| 	osmo_hexparse("FB2A3D1B360F599ABAB99DB8669F8308", | ||||
| 		      aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); | ||||
| 	next_rand("39fa2f4e3d523d8619a73b4f65c3e14d", true); | ||||
|  | ||||
| 	vec = (struct osmo_auth_vector){ {0} }; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 8704f5ba55d30000541dde77ea5b1d8c\n" | ||||
| 	       "  ck: f64735036e5871319c679f4742a75ea1\n" | ||||
| 	       "  ik: 27497388b6cb044648f396aa155b95ef\n" | ||||
| 	       "  res: e229c19e791f2e410000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 241a5b16aeb8e400\n" | ||||
| 	       "  sres: 429d5b27\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
|  | ||||
| 	/* even though vec is not zero-initialized, it should produce the same | ||||
| 	 * result with the same sequence nr */ | ||||
| 	aud3g.u.umts.sqn = 31; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 8704f5ba55d30000541dde77ea5b1d8c\n" | ||||
| 	       "  ck: f64735036e5871319c679f4742a75ea1\n" | ||||
| 	       "  ik: 27497388b6cb044648f396aa155b95ef\n" | ||||
| 	       "  res: e229c19e791f2e410000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 241a5b16aeb8e400\n" | ||||
| 	       "  sres: 429d5b27\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| void _test_gen_vectors_3g_only__expect_vecs(struct osmo_auth_vector vecs[3]) | ||||
| { | ||||
| 	fprintf(stderr, "[0]: "); | ||||
| 	VEC_IS(&vecs[0], | ||||
| 	       "  rand: 897210a0f7de278f0b8213098e098a3f\n" | ||||
| 	       "  autn: c6b9790dad4b00000cf322869ea6a481\n" | ||||
| 	       "  ck: e9922bd036718ed9e40bd1d02c3b81a5\n" | ||||
| 	       "  ik: f19c20ca863137f8892326d959ec5e01\n" | ||||
| 	       "  res: 9af5a557902d2db80000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 7526fc13c5976685\n" | ||||
| 	       "  sres: 0ad888ef\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
| 	fprintf(stderr, "[1]: "); | ||||
| 	VEC_IS(&vecs[1], | ||||
| 	       "  rand: 9a8321b108ef38a01c93241a9f1a9b50\n" | ||||
| 	       "  autn: 79a5113eb0910000be6020540503ffc5\n" | ||||
| 	       "  ck: 3686f05df057d1899c66ae4eb18cf941\n" | ||||
| 	       "  ik: 79f21ed53bcb47787de57d136ff803a5\n" | ||||
| 	       "  res: 43023475cb29292c0000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: aef73dd515e86c15\n" | ||||
| 	       "  sres: 882b1d59\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
| 	fprintf(stderr, "[2]: "); | ||||
| 	VEC_IS(&vecs[2], | ||||
| 	       "  rand: ab9432c2190049b12da4352bb02bac61\n" | ||||
| 	       "  autn: 24b018d46c3b00009c7e1b47f3a19b2b\n" | ||||
| 	       "  ck: d86c3191a36fc0602e48202ef2080964\n" | ||||
| 	       "  ik: 648dab72016181406243420649e63dc9\n" | ||||
| 	       "  res: 010cab11cc63a6e40000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: f0eaf8cb19e0758d\n" | ||||
| 	       "  sres: cd6f0df5\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
| } | ||||
|  | ||||
| static void test_gen_vectors_3g_only(void) | ||||
| { | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	struct osmo_auth_vector vec; | ||||
| 	struct osmo_auth_vector vecs[3]; | ||||
| 	uint8_t auts[14]; | ||||
| 	uint8_t rand_auts[16]; | ||||
| 	int rc; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	aud2g = (struct osmo_sub_auth_data){ 0 }; | ||||
|  | ||||
| 	aud3g = (struct osmo_sub_auth_data){ | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = OSMO_AUTH_ALG_MILENAGE, | ||||
| 		.u.umts.sqn = 31, | ||||
| 	}; | ||||
|  | ||||
| 	osmo_hexparse("EB215756028D60E3275E613320AEC880", | ||||
| 		      aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); | ||||
| 	osmo_hexparse("FB2A3D1B360F599ABAB99DB8669F8308", | ||||
| 		      aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); | ||||
| 	next_rand("39fa2f4e3d523d8619a73b4f65c3e14d", true); | ||||
|  | ||||
| 	vec = (struct osmo_auth_vector){ {0} }; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 8704f5ba55d30000541dde77ea5b1d8c\n" | ||||
| 	       "  ck: f64735036e5871319c679f4742a75ea1\n" | ||||
| 	       "  ik: 27497388b6cb044648f396aa155b95ef\n" | ||||
| 	       "  res: e229c19e791f2e410000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 059a4f668f6fbe39\n" | ||||
| 	       "  sres: 9b36efdf\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
|  | ||||
| 	/* Note: 3GPP TS 33.102 6.8.1.2: c3 function to get GSM auth is | ||||
| 	 * KC[0..7] == CK[0..7] ^ CK[8..15] ^ IK[0..7] ^ IK[8..15] | ||||
| 	 * In [16]: hex(  0xf64735036e587131 | ||||
| 	 *              ^ 0x9c679f4742a75ea1 | ||||
| 	 *              ^ 0x27497388b6cb0446 | ||||
| 	 *              ^ 0x48f396aa155b95ef) | ||||
| 	 * Out[16]: '0x59a4f668f6fbe39L' | ||||
| 	 * hence expecting kc: 059a4f668f6fbe39 | ||||
| 	 */ | ||||
|  | ||||
| 	/* even though vec is not zero-initialized, it should produce the same | ||||
| 	 * result with the same sequence nr */ | ||||
| 	aud3g.u.umts.sqn = 31; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" | ||||
| 	       "  autn: 8704f5ba55d30000541dde77ea5b1d8c\n" | ||||
| 	       "  ck: f64735036e5871319c679f4742a75ea1\n" | ||||
| 	       "  ik: 27497388b6cb044648f396aa155b95ef\n" | ||||
| 	       "  res: e229c19e791f2e410000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 059a4f668f6fbe39\n" | ||||
| 	       "  sres: 9b36efdf\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
|  | ||||
|  | ||||
| 	fprintf(stderr, "- test AUTS resync\n"); | ||||
| 	vec = (struct osmo_auth_vector){}; | ||||
| 	aud3g.u.umts.sqn = 31; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
|  | ||||
| 	/* The AUTN sent was 8704f5ba55f30000d2ee44b22c8ea919 | ||||
| 	 * with the first 6 bytes being SQN ^ AK. | ||||
| 	 * K = EB215756028D60E3275E613320AEC880 | ||||
| 	 * OPC = FB2A3D1B360F599ABAB99DB8669F8308 | ||||
| 	 * RAND = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| 	 * --milenage-f5--> | ||||
| 	 * AK = 8704f5ba55f3 | ||||
| 	 * | ||||
| 	 * The first six bytes are 8704f5ba55f3, | ||||
| 	 * and 8704f5ba55f3 ^ AK = 0. | ||||
| 	 * --> SQN = 0. | ||||
| 	 * | ||||
| 	 * Say the USIM doesn't like that, let's say it is at SQN 23. | ||||
| 	 * SQN_MS = 000000000017 | ||||
| 	 * | ||||
| 	 * AUTS = Conc(SQN_MS) || MAC-S | ||||
| 	 * Conc(SQN_MS) = SQN_MS ⊕ f5*[K](RAND) | ||||
| 	 * MAC-S = f1*[K] (SQN MS || RAND || AMF) | ||||
| 	 * | ||||
| 	 * f5*--> Conc(SQN_MS) = 000000000017 ^ 979498b1f73a | ||||
| 	 *                     = 979498b1f72d | ||||
| 	 * AMF = 0000 (TS 33.102 v7.0.0, 6.3.3) | ||||
| 	 * | ||||
| 	 * MAC-S = f1*[K] (000000000017 || 39fa2f4e3d523d8619a73b4f65c3e14d || 0000) | ||||
| 	 *       = 3e28c59fa2e72f9c | ||||
| 	 * | ||||
| 	 * AUTS = 979498b1f72d || 3e28c59fa2e72f9c | ||||
| 	 * | ||||
| 	 * verify valid AUTS resulting in SQN 23 with: | ||||
| 	 * osmo-auc-gen -3 -a milenage -k EB215756028D60E3275E613320AEC880 \ | ||||
| 	 *              -o FB2A3D1B360F599ABAB99DB8669F8308 \ | ||||
| 	 *              -r 39fa2f4e3d523d8619a73b4f65c3e14d \ | ||||
| 	 *              -A 979498b1f72d3e28c59fa2e72f9c | ||||
| 	 */ | ||||
|  | ||||
| 	/* AUTS response by USIM */ | ||||
| 	osmo_hexparse("979498b1f72d3e28c59fa2e72f9c", | ||||
| 		      auts, sizeof(auts)); | ||||
| 	/* RAND sent to USIM, which AUTS was generated from */ | ||||
| 	osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", | ||||
| 		      rand_auts, sizeof(rand_auts)); | ||||
| 	/* new RAND token for the next key */ | ||||
| 	next_rand("897210a0f7de278f0b8213098e098a3f", true); | ||||
| 	rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, rand_auts, auts); | ||||
| 	VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	/* The USIM's last sqn was 23, the calculated vector was 24 */ | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 24, "%"PRIu64); | ||||
|  | ||||
| 	VEC_IS(&vec, | ||||
| 	       "  rand: 897210a0f7de278f0b8213098e098a3f\n" | ||||
| 	       "  autn: c6b9790dad4b00000cf322869ea6a481\n" | ||||
| 	       "  ck: e9922bd036718ed9e40bd1d02c3b81a5\n" | ||||
| 	       "  ik: f19c20ca863137f8892326d959ec5e01\n" | ||||
| 	       "  res: 9af5a557902d2db80000000000000000\n" | ||||
| 	       "  res_len: 08\n" | ||||
| 	       "  kc: 7526fc13c5976685\n" | ||||
| 	       "  sres: 0ad888ef\n" | ||||
| 	       "  auth_types: 03000000\n" | ||||
| 	      ); | ||||
|  | ||||
|  | ||||
| 	fprintf(stderr, "- verify N vectors with AUTS resync" | ||||
| 		" == N vectors without AUTS\n" | ||||
| 		"First just set rand and sqn = 23, and compute 3 vectors\n"); | ||||
| 	next_rand("897210a0f7de278f0b8213098e098a3f", false); | ||||
| 	aud3g.u.umts.sqn = 23; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 23, "%"PRIu64); | ||||
|  | ||||
| 	memset(vecs, 0, sizeof(vecs)); | ||||
| 	rc = auc_compute_vectors(vecs, 3, &aud2g, &aud3g, NULL, NULL); | ||||
| 	VERBOSE_ASSERT(rc, == 3, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 26, "%"PRIu64); | ||||
|  | ||||
| 	_test_gen_vectors_3g_only__expect_vecs(vecs); | ||||
|  | ||||
| 	fprintf(stderr, "Now reach sqn = 23 with AUTS and expect the same\n"); | ||||
| 	/* AUTS response by USIM */ | ||||
| 	osmo_hexparse("979498b1f72d3e28c59fa2e72f9c", | ||||
| 		      auts, sizeof(auts)); | ||||
| 	/* RAND sent to USIM, which AUTS was generated from */ | ||||
| 	osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", | ||||
| 		      rand_auts, sizeof(rand_auts)); | ||||
| 	next_rand("897210a0f7de278f0b8213098e098a3f", false); | ||||
| 	rc = auc_compute_vectors(vecs, 3, &aud2g, &aud3g, rand_auts, auts); | ||||
|  | ||||
| 	_test_gen_vectors_3g_only__expect_vecs(vecs); | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| void test_gen_vectors_bad_args() | ||||
| { | ||||
| 	struct osmo_auth_vector vec; | ||||
| 	uint8_t auts[14]; | ||||
| 	uint8_t rand_auts[16]; | ||||
| 	int rc; | ||||
| 	int i; | ||||
|  | ||||
| 	struct osmo_sub_auth_data aud2g = { | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = OSMO_AUTH_ALG_COMP128v1, | ||||
| 	}; | ||||
|  | ||||
| 	struct osmo_sub_auth_data aud3g = { | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = OSMO_AUTH_ALG_MILENAGE, | ||||
| 	}; | ||||
|  | ||||
| 	struct osmo_sub_auth_data aud2g_noalg = { | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = OSMO_AUTH_ALG_NONE, | ||||
| 	}; | ||||
|  | ||||
| 	struct osmo_sub_auth_data aud3g_noalg = { | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = OSMO_AUTH_ALG_NONE, | ||||
| 	}; | ||||
|  | ||||
| 	struct osmo_sub_auth_data aud_notype = { | ||||
| 		.type = OSMO_AUTH_TYPE_NONE, | ||||
| 		.algo = OSMO_AUTH_ALG_MILENAGE, | ||||
| 	}; | ||||
|  | ||||
| 	struct osmo_sub_auth_data no_aud = { | ||||
| 		.type = OSMO_AUTH_TYPE_NONE, | ||||
| 		.algo = OSMO_AUTH_ALG_NONE, | ||||
| 	}; | ||||
|  | ||||
| 	struct { | ||||
| 		struct osmo_sub_auth_data *aud2g; | ||||
| 		struct osmo_sub_auth_data *aud3g; | ||||
| 		uint8_t *rand_auts; | ||||
| 		uint8_t *auts; | ||||
| 		const char *label; | ||||
| 	} tests[] = { | ||||
| 		{         NULL,         NULL,       NULL,  NULL, "no auth data (a)"}, | ||||
| 		{         NULL, &aud3g_noalg,       NULL,  NULL, "no auth data (b)"}, | ||||
| 		{         NULL,  &aud_notype,       NULL,  NULL, "no auth data (c)"}, | ||||
| 		{         NULL,      &no_aud,       NULL,  NULL, "no auth data (d)"}, | ||||
| 		{ &aud2g_noalg,         NULL,       NULL,  NULL, "no auth data (e)"}, | ||||
| 		{ &aud2g_noalg, &aud3g_noalg,       NULL,  NULL, "no auth data (f)"}, | ||||
| 		{ &aud2g_noalg,  &aud_notype,       NULL,  NULL, "no auth data (g)"}, | ||||
| 		{ &aud2g_noalg,      &no_aud,       NULL,  NULL, "no auth data (h)"}, | ||||
| 		{  &aud_notype,         NULL,       NULL,  NULL, "no auth data (i)"}, | ||||
| 		{  &aud_notype, &aud3g_noalg,       NULL,  NULL, "no auth data (j)"}, | ||||
| 		{  &aud_notype,  &aud_notype,       NULL,  NULL, "no auth data (k)"}, | ||||
| 		{  &aud_notype,      &no_aud,       NULL,  NULL, "no auth data (l)"}, | ||||
| 		{      &no_aud,         NULL,       NULL,  NULL, "no auth data (m)"}, | ||||
| 		{      &no_aud, &aud3g_noalg,       NULL,  NULL, "no auth data (n)"}, | ||||
| 		{      &no_aud,  &aud_notype,       NULL,  NULL, "no auth data (o)"}, | ||||
| 		{      &no_aud,      &no_aud,       NULL,  NULL, "no auth data (p)"}, | ||||
| 		{       &aud3g,         NULL,       NULL,  NULL, "wrong auth data type (a)"}, | ||||
| 		{       &aud3g, &aud3g_noalg,       NULL,  NULL, "wrong auth data type (b)"}, | ||||
| 		{       &aud3g,  &aud_notype,       NULL,  NULL, "wrong auth data type (c)"}, | ||||
| 		{       &aud3g,      &no_aud,       NULL,  NULL, "wrong auth data type (d)"}, | ||||
| 		{         NULL,       &aud2g,       NULL,  NULL, "wrong auth data type (e)"}, | ||||
| 		{ &aud3g_noalg,       &aud2g,       NULL,  NULL, "wrong auth data type (f)"}, | ||||
| 		{  &aud_notype,       &aud2g,       NULL,  NULL, "wrong auth data type (g)"}, | ||||
| 		{      &no_aud,       &aud2g,       NULL,  NULL, "wrong auth data type (h)"}, | ||||
| 		{       &aud3g,       &aud2g,       NULL,  NULL, "wrong auth data type (i)"}, | ||||
| 		{       &aud3g,       &aud3g,       NULL,  NULL, "wrong auth data type (j)"}, | ||||
| 		{       &aud2g,       &aud2g,       NULL,  NULL, "wrong auth data type (k)"}, | ||||
| 		{       &aud2g,         NULL,  rand_auts,  auts, "AUTS for 2G-only (a)"}, | ||||
| 		{       &aud2g, &aud3g_noalg,  rand_auts,  auts, "AUTS for 2G-only (b)"}, | ||||
| 		{       &aud2g,  &aud_notype,  rand_auts,  auts, "AUTS for 2G-only (c)"}, | ||||
| 		{       &aud2g,      &no_aud,  rand_auts,  auts, "AUTS for 2G-only (d)"}, | ||||
| 		{         NULL,       &aud3g,       NULL,  auts, "incomplete AUTS (a)"}, | ||||
| 		{         NULL,       &aud3g,  rand_auts,  NULL, "incomplete AUTS (b)"}, | ||||
| 		{       &aud2g,       &aud3g,       NULL,  auts, "incomplete AUTS (c)"}, | ||||
| 		{       &aud2g,       &aud3g,  rand_auts,  NULL, "incomplete AUTS (d)"}, | ||||
| 	}; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(tests); i++) { | ||||
| 		fprintf(stderr, "\n- %s\n", tests[i].label); | ||||
| 		rc = auc_compute_vectors(&vec, 1, | ||||
| 					 tests[i].aud2g, | ||||
| 					 tests[i].aud3g, | ||||
| 					 tests[i].rand_auts, | ||||
| 					 tests[i].auts); | ||||
| 		VERBOSE_ASSERT(rc, < 0, "%d"); | ||||
| 	} | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| static struct { | ||||
| 	bool verbose; | ||||
| } cmdline_opts = { | ||||
| 	.verbose = false, | ||||
| }; | ||||
|  | ||||
| static void print_help(const char *program) | ||||
| { | ||||
| 	printf("Usage:\n" | ||||
| 	       "  %s [-v] [N [N...]]\n" | ||||
| 	       "Options:\n" | ||||
| 	       "  -h --help      show this text.\n" | ||||
| 	       "  -v --verbose   print source file and line numbers\n", | ||||
| 	       program | ||||
| 	       ); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv) | ||||
| { | ||||
| 	while (1) { | ||||
| 		int option_index = 0, c; | ||||
| 		static struct option long_options[] = { | ||||
| 			{"help", 0, 0, 'h'}, | ||||
| 			{"verbose", 1, 0, 'v'}, | ||||
| 			{0, 0, 0, 0} | ||||
| 		}; | ||||
|  | ||||
| 		c = getopt_long(argc, argv, "hv", | ||||
| 				long_options, &option_index); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
|  | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			print_help(argv[0]); | ||||
| 			exit(0); | ||||
| 		case 'v': | ||||
| 			cmdline_opts.verbose = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* catch unknown options *as well as* missing arguments. */ | ||||
| 			fprintf(stderr, "Error in command line options. Exiting.\n"); | ||||
| 			exit(-1); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (optind < argc) { | ||||
| 		fprintf(stderr, "too many args\n"); | ||||
| 		exit(-1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	printf("auc_3g_test.c\n"); | ||||
|  | ||||
| 	handle_options(argc, argv); | ||||
|  | ||||
| 	void *tall_ctx = talloc_named_const(NULL, 1, "auc_test"); | ||||
|  | ||||
| 	osmo_init_logging2(tall_ctx, &hlr_log_info); | ||||
| 	log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose); | ||||
| 	log_set_print_timestamp(osmo_stderr_target, 0); | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_category(osmo_stderr_target, 1); | ||||
| 	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); | ||||
|  | ||||
| 	test_gen_vectors_2g_only(); | ||||
| 	test_gen_vectors_2g_plus_3g(); | ||||
| 	test_gen_vectors_3g_only(); | ||||
| 	test_gen_vectors_bad_args(); | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										362
									
								
								tests/auc/auc_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								tests/auc/auc_test.err
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,362 @@ | ||||
|  | ||||
| ===== test_gen_vectors_2g_only | ||||
| aud3g.u.umts.sqn == 0 | ||||
| DAUC Computing 1 auth vector: 2G only | ||||
| DAUC 2G: ki = eb215756028d60e3275e613320aec880 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: kc = 241a5b16aeb8e400 | ||||
| DAUC vector [0]: sres = 429d5b27 | ||||
| DAUC vector [0]: auth_types = 0x1 | ||||
| rc == 1 | ||||
| vector matches expectations | ||||
| aud3g.u.umts.sqn == 0 | ||||
| DAUC Computing 1 auth vector: 2G only | ||||
| DAUC 2G: ki = eb215756028d60e3275e613320aec880 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: kc = 241a5b16aeb8e400 | ||||
| DAUC vector [0]: sres = 429d5b27 | ||||
| DAUC vector [0]: auth_types = 0x1 | ||||
| rc == 1 | ||||
| vector matches expectations | ||||
| ===== test_gen_vectors_2g_only: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_gen_vectors_2g_plus_3g | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G + separate 2G | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC 2G: ki = eb215756028d60e3275e613320aec880 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 8704f5ba55d30000541dde77ea5b1d8c | ||||
| DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1 | ||||
| DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef | ||||
| DAUC vector [0]: res = e229c19e791f2e410000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: calculating 2G separately | ||||
| DAUC vector [0]: kc = 241a5b16aeb8e400 | ||||
| DAUC vector [0]: sres = 429d5b27 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G + separate 2G | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC 2G: ki = eb215756028d60e3275e613320aec880 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 8704f5ba55d30000541dde77ea5b1d8c | ||||
| DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1 | ||||
| DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef | ||||
| DAUC vector [0]: res = e229c19e791f2e410000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: calculating 2G separately | ||||
| DAUC vector [0]: kc = 241a5b16aeb8e400 | ||||
| DAUC vector [0]: sres = 429d5b27 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_gen_vectors_2g_plus_3g: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_gen_vectors_3g_only | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 8704f5ba55d30000541dde77ea5b1d8c | ||||
| DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1 | ||||
| DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef | ||||
| DAUC vector [0]: res = e229c19e791f2e410000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 059a4f668f6fbe39 | ||||
| DAUC vector [0]: sres = 9b36efdf | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 8704f5ba55d30000541dde77ea5b1d8c | ||||
| DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1 | ||||
| DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef | ||||
| DAUC vector [0]: res = e229c19e791f2e410000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 059a4f668f6fbe39 | ||||
| DAUC vector [0]: sres = 9b36efdf | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| - test AUTS resync | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys), with AUTS resync | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 897210a0f7de278f0b8213098e098a3f | ||||
| DAUC vector [0]: resync: auts = 979498b1f72d3e28c59fa2e72f9c | ||||
| DAUC vector [0]: resync: rand_auts = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 24 | ||||
| DAUC vector [0]: autn = c6b9790dad4b00000cf322869ea6a481 | ||||
| DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5 | ||||
| DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01 | ||||
| DAUC vector [0]: res = 9af5a557902d2db80000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 7526fc13c5976685 | ||||
| DAUC vector [0]: sres = 0ad888ef | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 24 | ||||
| vector matches expectations | ||||
| - verify N vectors with AUTS resync == N vectors without AUTS | ||||
| First just set rand and sqn = 23, and compute 3 vectors | ||||
| aud3g.u.umts.sqn == 23 | ||||
| DAUC Computing 3 auth vectors: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 23 | ||||
| DAUC vector [0]: rand = 897210a0f7de278f0b8213098e098a3f | ||||
| DAUC vector [0]: sqn = 24 | ||||
| DAUC vector [0]: autn = c6b9790dad4b00000cf322869ea6a481 | ||||
| DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5 | ||||
| DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01 | ||||
| DAUC vector [0]: res = 9af5a557902d2db80000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 7526fc13c5976685 | ||||
| DAUC vector [0]: sres = 0ad888ef | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| DAUC vector [1]: rand = 9a8321b108ef38a01c93241a9f1a9b50 | ||||
| DAUC vector [1]: sqn = 25 | ||||
| DAUC vector [1]: autn = 79a5113eb0910000be6020540503ffc5 | ||||
| DAUC vector [1]: ck = 3686f05df057d1899c66ae4eb18cf941 | ||||
| DAUC vector [1]: ik = 79f21ed53bcb47787de57d136ff803a5 | ||||
| DAUC vector [1]: res = 43023475cb29292c0000000000000000 | ||||
| DAUC vector [1]: res_len = 8 | ||||
| DAUC vector [1]: deriving 2G from 3G | ||||
| DAUC vector [1]: kc = aef73dd515e86c15 | ||||
| DAUC vector [1]: sres = 882b1d59 | ||||
| DAUC vector [1]: auth_types = 0x3 | ||||
| DAUC vector [2]: rand = ab9432c2190049b12da4352bb02bac61 | ||||
| DAUC vector [2]: sqn = 26 | ||||
| DAUC vector [2]: autn = 24b018d46c3b00009c7e1b47f3a19b2b | ||||
| DAUC vector [2]: ck = d86c3191a36fc0602e48202ef2080964 | ||||
| DAUC vector [2]: ik = 648dab72016181406243420649e63dc9 | ||||
| DAUC vector [2]: res = 010cab11cc63a6e40000000000000000 | ||||
| DAUC vector [2]: res_len = 8 | ||||
| DAUC vector [2]: deriving 2G from 3G | ||||
| DAUC vector [2]: kc = f0eaf8cb19e0758d | ||||
| DAUC vector [2]: sres = cd6f0df5 | ||||
| DAUC vector [2]: auth_types = 0x3 | ||||
| rc == 3 | ||||
| aud3g.u.umts.sqn == 26 | ||||
| [0]: vector matches expectations | ||||
| [1]: vector matches expectations | ||||
| [2]: vector matches expectations | ||||
| Now reach sqn = 23 with AUTS and expect the same | ||||
| DAUC Computing 3 auth vectors: 3G only (2G derived from 3G keys), with AUTS resync | ||||
| DAUC 3G: k = eb215756028d60e3275e613320aec880 | ||||
| DAUC 3G: opc = fb2a3d1b360f599abab99db8669f8308 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 26 | ||||
| DAUC vector [0]: rand = 897210a0f7de278f0b8213098e098a3f | ||||
| DAUC vector [0]: resync: auts = 979498b1f72d3e28c59fa2e72f9c | ||||
| DAUC vector [0]: resync: rand_auts = 39fa2f4e3d523d8619a73b4f65c3e14d | ||||
| DAUC vector [0]: sqn = 24 | ||||
| DAUC vector [0]: autn = c6b9790dad4b00000cf322869ea6a481 | ||||
| DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5 | ||||
| DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01 | ||||
| DAUC vector [0]: res = 9af5a557902d2db80000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 7526fc13c5976685 | ||||
| DAUC vector [0]: sres = 0ad888ef | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| DAUC vector [1]: rand = 9a8321b108ef38a01c93241a9f1a9b50 | ||||
| DAUC vector [1]: sqn = 25 | ||||
| DAUC vector [1]: autn = 79a5113eb0910000be6020540503ffc5 | ||||
| DAUC vector [1]: ck = 3686f05df057d1899c66ae4eb18cf941 | ||||
| DAUC vector [1]: ik = 79f21ed53bcb47787de57d136ff803a5 | ||||
| DAUC vector [1]: res = 43023475cb29292c0000000000000000 | ||||
| DAUC vector [1]: res_len = 8 | ||||
| DAUC vector [1]: deriving 2G from 3G | ||||
| DAUC vector [1]: kc = aef73dd515e86c15 | ||||
| DAUC vector [1]: sres = 882b1d59 | ||||
| DAUC vector [1]: auth_types = 0x3 | ||||
| DAUC vector [2]: rand = ab9432c2190049b12da4352bb02bac61 | ||||
| DAUC vector [2]: sqn = 26 | ||||
| DAUC vector [2]: autn = 24b018d46c3b00009c7e1b47f3a19b2b | ||||
| DAUC vector [2]: ck = d86c3191a36fc0602e48202ef2080964 | ||||
| DAUC vector [2]: ik = 648dab72016181406243420649e63dc9 | ||||
| DAUC vector [2]: res = 010cab11cc63a6e40000000000000000 | ||||
| DAUC vector [2]: res_len = 8 | ||||
| DAUC vector [2]: deriving 2G from 3G | ||||
| DAUC vector [2]: kc = f0eaf8cb19e0758d | ||||
| DAUC vector [2]: sres = cd6f0df5 | ||||
| DAUC vector [2]: auth_types = 0x3 | ||||
| [0]: vector matches expectations | ||||
| [1]: vector matches expectations | ||||
| [2]: vector matches expectations | ||||
| ===== test_gen_vectors_3g_only: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_gen_vectors_bad_args | ||||
|  | ||||
| - no auth data (a) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (b) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (c) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (d) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (e) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (f) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (g) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (h) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (i) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (j) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (k) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (l) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (m) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (n) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (o) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - no auth data (p) | ||||
| DAUC auc_compute_vectors() called with neither 2G nor 3G auth data available | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (a) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (b) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (c) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (d) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (e) | ||||
| DAUC auc_compute_vectors() called with non-3G auth data passed for aud3g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (f) | ||||
| DAUC auc_compute_vectors() called with non-3G auth data passed for aud3g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (g) | ||||
| DAUC auc_compute_vectors() called with non-3G auth data passed for aud3g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (h) | ||||
| DAUC auc_compute_vectors() called with non-3G auth data passed for aud3g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (i) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (j) | ||||
| DAUC auc_compute_vectors() called with non-2G auth data passed for aud2g arg | ||||
| rc == -1 | ||||
|  | ||||
| - wrong auth data type (k) | ||||
| DAUC auc_compute_vectors() called with non-3G auth data passed for aud3g arg | ||||
| rc == -1 | ||||
|  | ||||
| - AUTS for 2G-only (a) | ||||
| DAUC auc_compute_vectors() with AUTS called but no 3G auth data passed | ||||
| rc == -1 | ||||
|  | ||||
| - AUTS for 2G-only (b) | ||||
| DAUC auc_compute_vectors() with AUTS called but no 3G auth data passed | ||||
| rc == -1 | ||||
|  | ||||
| - AUTS for 2G-only (c) | ||||
| DAUC auc_compute_vectors() with AUTS called but no 3G auth data passed | ||||
| rc == -1 | ||||
|  | ||||
| - AUTS for 2G-only (d) | ||||
| DAUC auc_compute_vectors() with AUTS called but no 3G auth data passed | ||||
| rc == -1 | ||||
|  | ||||
| - incomplete AUTS (a) | ||||
| DAUC auc_compute_vectors() with only one of AUTS and AUTS_RAND given, need both or neither | ||||
| rc == -1 | ||||
|  | ||||
| - incomplete AUTS (b) | ||||
| DAUC auc_compute_vectors() with only one of AUTS and AUTS_RAND given, need both or neither | ||||
| rc == -1 | ||||
|  | ||||
| - incomplete AUTS (c) | ||||
| DAUC auc_compute_vectors() with only one of AUTS and AUTS_RAND given, need both or neither | ||||
| rc == -1 | ||||
|  | ||||
| - incomplete AUTS (d) | ||||
| DAUC auc_compute_vectors() with only one of AUTS and AUTS_RAND given, need both or neither | ||||
| rc == -1 | ||||
| ===== test_gen_vectors_bad_args: SUCCESS | ||||
|  | ||||
							
								
								
									
										2
									
								
								tests/auc/auc_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/auc/auc_test.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| auc_3g_test.c | ||||
| Done | ||||
							
								
								
									
										437
									
								
								tests/auc/auc_ts_55_205_test_sets.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								tests/auc/auc_ts_55_205_test_sets.err
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,437 @@ | ||||
|  | ||||
| ===== test_set_1 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 465b5ce8b199b49faa5f0a2ee238a6bc | ||||
| DAUC 3G: opc = cd63cb71954a9f4e48a5994e37a02baf | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 23553cbe9637a89d218ae64dae47bf35 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = aa689c64835000002bb2bf2f1faba139 | ||||
| DAUC vector [0]: ck = b40ba9a3c58b2a05bbf0d987b21bf8cb | ||||
| DAUC vector [0]: ik = f769bcd751044604127672711c6d3441 | ||||
| DAUC vector [0]: res = a54211d5e3ba50bf0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = eae4be823af9a08b | ||||
| DAUC vector [0]: sres = 46f8416a | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_1: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_2 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = fec86ba6eb707ed08905757b1bb44b8f | ||||
| DAUC 3G: opc = 1006020f0a478bf6b699f15c062e42b3 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 9f7c8d021accf4db213ccff0c7f71a6a | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 33484dc2134b000091ec125f4840ed64 | ||||
| DAUC vector [0]: ck = 5dbdbb2954e8f3cde665b046179a5098 | ||||
| DAUC vector [0]: ik = 59a92d3b476a0443487055cf88b2307b | ||||
| DAUC vector [0]: res = 8011c48c0c214ed20000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = aa01739b8caa976d | ||||
| DAUC vector [0]: sres = 8c308a5e | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_2: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_3 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 9e5944aea94b81165c82fbf9f32db751 | ||||
| DAUC 3G: opc = a64a507ae1a2a98bb88eb4210135dc87 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = ce83dbc54ac0274a157c17f80d017bd6 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = f0b9c08ad00e00005da4ccbbdfa29310 | ||||
| DAUC vector [0]: ck = e203edb3971574f5a94b0d61b816345d | ||||
| DAUC vector [0]: ik = 0c4524adeac041c4dd830d20854fc46b | ||||
| DAUC vector [0]: res = f365cd683cd92e960000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 9a8ec95f408cc507 | ||||
| DAUC vector [0]: sres = cfbce3fe | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_3: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_4 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 4ab1deb05ca6ceb051fc98e77d026a84 | ||||
| DAUC 3G: opc = dcf07cbd51855290b92a07a9891e523e | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 74b0cd6031a1c8339b2b6ce2b8c4a186 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 31e11a60913800006a7003718d5d82e5 | ||||
| DAUC vector [0]: ck = 7657766b373d1c2138f307e3de9242f9 | ||||
| DAUC vector [0]: ik = 1c42e960d89b8fa99f2744e0708ccb53 | ||||
| DAUC vector [0]: res = 5860fc1bce351e7e0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = cdc1dc0841b81a22 | ||||
| DAUC vector [0]: sres = 9655e265 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_4: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_5 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 6c38a116ac280c454f59332ee35c8c4f | ||||
| DAUC 3G: opc = 3803ef5363b947c6aaa225e58fae3934 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = ee6466bc96202c5a557abbeff8babf63 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 45b0f69ab04c000053f2a822f2b3e824 | ||||
| DAUC vector [0]: ck = 3f8c7587fe8e4b233af676aede30ba3b | ||||
| DAUC vector [0]: ik = a7466cc1e6b2a1337d49d3b66e95d7b4 | ||||
| DAUC vector [0]: res = 16c8233f05a0ac280000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = df75bc5ea899879f | ||||
| DAUC vector [0]: sres = 13688f17 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_5: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_6 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 2d609d4db0ac5bf0d2c0de267014de0d | ||||
| DAUC 3G: opc = c35a0ab0bcbfc9252caff15f24efbde0 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 194aa756013896b74b4a2a3b0af4539e | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 7e6455f34cd300004a2a9f2f3a529b8c | ||||
| DAUC vector [0]: ck = 4cd0846020f8fa0731dd47cbdc6be411 | ||||
| DAUC vector [0]: ik = 88ab80a415f15c73711254a1d388f696 | ||||
| DAUC vector [0]: res = 8c25a16cd918a1df0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 84b417ae3aeab4f3 | ||||
| DAUC vector [0]: sres = 553d00b3 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_6: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_7 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = a530a7fe428fad1082c45eddfce13884 | ||||
| DAUC 3G: opc = 27953e49bc8af6dcc6e730eb80286be3 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 3a4c2b3245c50eb5c71d08639395764d | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 88196c47984f00000a50c5f4056ccb68 | ||||
| DAUC vector [0]: ck = 10f05bab75a99a5fbb98a9c287679c3b | ||||
| DAUC vector [0]: ik = f9ec0865eb32f22369cade40c59c3a44 | ||||
| DAUC vector [0]: res = a63241e1ffc3e5ab0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 3b4e244cdc60ce03 | ||||
| DAUC vector [0]: sres = 59f1a44a | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_7: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_8 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = d9151cf04896e25830bf2e08267b8360 | ||||
| DAUC 3G: opc = c4c93effe8a08138c203d4c27ce4e3d9 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = f761e5e93d603feb730e27556cb8a2ca | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 82a0f5287a5100006d6c0ff132426479 | ||||
| DAUC vector [0]: ck = 71236b7129f9b22ab77ea7a54c96da22 | ||||
| DAUC vector [0]: ik = 90527ebaa5588968db41727325a04d9e | ||||
| DAUC vector [0]: res = 4a90b2171ac83a760000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 8d4ec01de597acfe | ||||
| DAUC vector [0]: sres = 50588861 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_8: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_9 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = a0e2971b6822e8d354a18cc235624ecb | ||||
| DAUC 3G: opc = 82a26f22bba9e9488f949a10d98e9cc4 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 08eff828b13fdb562722c65c7f30a9b2 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = a2f858aa9e7d00001c14f5fcd445bc46 | ||||
| DAUC vector [0]: ck = 08cef6d004ec61471a3c3cda048137fa | ||||
| DAUC vector [0]: ik = ed0318ca5deb9206272f6e8fa64ba411 | ||||
| DAUC vector [0]: res = 4bc2212d8624910a0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = d8debc4ffbcd60aa | ||||
| DAUC vector [0]: sres = cde6b027 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_9: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_10 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 0da6f7ba86d5eac8a19cf563ac58642d | ||||
| DAUC 3G: opc = 0db1071f8767562ca43a0a64c41e8d08 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 679ac4dbacd7d233ff9d6806f4149ce3 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 4c539a26e1da000071cc0b769fd1aa96 | ||||
| DAUC vector [0]: ck = 69b1cae7c7429d975e245cacb05a517c | ||||
| DAUC vector [0]: ik = 74f24e8c26df58e1b38d7dcd4f1b7fbd | ||||
| DAUC vector [0]: res = 6fc30fee6d1235230000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = f0eaa50a1edcebb7 | ||||
| DAUC vector [0]: sres = 02d13acd | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_10: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_11 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 77b45843c88e58c10d202684515ed430 | ||||
| DAUC 3G: opc = d483afae562409a326b5bb0b20c4d762 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 4c47eb3076dc55fe5106cb2034b8cd78 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 30ff25cdadd60000e08a00f7ed54d6fe | ||||
| DAUC vector [0]: ck = 908c43f0569cb8f74bc971e706c36c5f | ||||
| DAUC vector [0]: ik = c251df0d888dd9329bcf46655b226e40 | ||||
| DAUC vector [0]: res = aefa357beac2a87a0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 82dbab7f83f063da | ||||
| DAUC vector [0]: sres = 44389d01 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_11: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_12 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 729b17729270dd87ccdf1bfe29b4e9bb | ||||
| DAUC 3G: opc = 228c2f2f06ac3268a9e616ee16db4ba1 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 311c4c929744d675b720f3b7e9b1cbd0 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 5380d158cfc30000f4e1436e9f67e4b2 | ||||
| DAUC vector [0]: ck = 44c0f23c5493cfd241e48f197e1d1012 | ||||
| DAUC vector [0]: ik = 0c9fb81613884c2535dd0eabf3b440d8 | ||||
| DAUC vector [0]: res = 98dbbd099b3b408d0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 3c66cb98cab2d33d | ||||
| DAUC vector [0]: sres = 03e0fd84 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_12: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_13 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = d32dd23e89dc662354ca12eb79dd32fa | ||||
| DAUC 3G: opc = d22a4b4180a5325708a5ff70d9f67ec7 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = cf7d0ab1d94306950bf12018fbd46887 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 217af492728d00003bd338249751de80 | ||||
| DAUC vector [0]: ck = 5af86b80edb70df5292cc1121cbad50c | ||||
| DAUC vector [0]: ik = 7f4d6ae7440e18789a8b75ad3f42f03a | ||||
| DAUC vector [0]: res = af4a411e1139f2c20000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 9612b5d88a4130bb | ||||
| DAUC vector [0]: sres = be73b3dc | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_13: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_14 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = af7c65e1927221de591187a2c5987a53 | ||||
| DAUC 3G: opc = a4cf5c8155c08a7eff418e5443b98e55 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 1f0f8578464fd59b64bed2d09436b57a | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 837fd7b744390000557a836fd534e542 | ||||
| DAUC vector [0]: ck = 3f8c3f3ccf7625bf77fc94bcfd22fd26 | ||||
| DAUC vector [0]: ik = abcbae8fd46115e9961a55d0da5f2078 | ||||
| DAUC vector [0]: res = 7bffa5c2f41fbc050000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 75a150df3c6aed08 | ||||
| DAUC vector [0]: sres = 8fe019c7 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_14: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_15 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 5bd7ecd3d3127a41d12539bed4e7cf71 | ||||
| DAUC 3G: opc = 76089d3c0ff3efdc6e36721d4fceb747 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 59b75f14251c75031d0bcbac1c2c04c7 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 5be11495527d0000298064f82a439924 | ||||
| DAUC vector [0]: ck = d42b2d615e49a03ac275a5aef97af892 | ||||
| DAUC vector [0]: ik = 0b3f8d024fe6bfafaa982b8f82e319c2 | ||||
| DAUC vector [0]: res = 7e3f44c7591f6f450000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = b7f92e426a36fec5 | ||||
| DAUC vector [0]: sres = 27202b82 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_15: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_16 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 6cd1c6ceb1e01e14f1b82316a90b7f3d | ||||
| DAUC 3G: opc = a219dc37f1dc7d66738b5843c799f206 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = f69b78f300a0568bce9f0cb93c4be4c9 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 1c408a858b1e0000e6e96310f83b5689 | ||||
| DAUC vector [0]: ck = 6edaf99e5bd9f85d5f36d91c1272fb4b | ||||
| DAUC vector [0]: ik = d61c853c280dd9c46f297baec386de17 | ||||
| DAUC vector [0]: res = 70f6bdb9ad21525f0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 88d9de10a22004c5 | ||||
| DAUC vector [0]: sres = ddd7efe6 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_16: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_17 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = b73a90cbcf3afb622dba83c58a8415df | ||||
| DAUC 3G: opc = df0c67868fa25f748b7044c6e7c245b8 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = b120f1c1a0102a2f507dd543de68281f | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = aefdaa5dddb90000c4741d698b7a7ed3 | ||||
| DAUC vector [0]: ck = 66195dbed0313274c5ca7766615fa25e | ||||
| DAUC vector [0]: ik = 66bec707eb2afc476d7408a8f2927b36 | ||||
| DAUC vector [0]: res = 479dd25c20792d630000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = a819e577a8d6175b | ||||
| DAUC vector [0]: sres = 67e4ff3f | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_17: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_18 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 5122250214c33e723a5dd523fc145fc0 | ||||
| DAUC 3G: opc = 981d464c7c52eb6e5036234984ad0bcf | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 81e92b6c0ee0e12ebceba8d92a99dfa5 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = ada15aeb7b980000a99729b59d5688b2 | ||||
| DAUC vector [0]: ck = 5349fbe098649f948f5d2e973a81c00f | ||||
| DAUC vector [0]: ik = 9744871ad32bf9bbd1dd5ce54e3e2e5a | ||||
| DAUC vector [0]: res = 28d7b0f2a2ec3de50000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = 9a8d0e883ff0887a | ||||
| DAUC vector [0]: sres = 8a3b8d17 | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_18: SUCCESS | ||||
|  | ||||
|  | ||||
| ===== test_set_19 | ||||
| aud3g.u.umts.sqn == 31 | ||||
| DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys) | ||||
| DAUC 3G: k = 90dca4eda45b53cf0f12d7c9c3bc6a89 | ||||
| DAUC 3G: opc = cb9cccc4b9258e6dca4760379fb82581 | ||||
| DAUC 3G: for sqn ind 0, previous sqn was 31 | ||||
| DAUC vector [0]: rand = 9fddc72092c6ad036b6e464789315b78 | ||||
| DAUC vector [0]: sqn = 32 | ||||
| DAUC vector [0]: autn = 83cfd54db9330000695685b2b9214472 | ||||
| DAUC vector [0]: ck = b5f2da03883b69f96bf52e029ed9ac45 | ||||
| DAUC vector [0]: ik = b4721368bc16ea67875c5598688bb0ef | ||||
| DAUC vector [0]: res = a95100e2760952cd0000000000000000 | ||||
| DAUC vector [0]: res_len = 8 | ||||
| DAUC vector [0]: deriving 2G from 3G | ||||
| DAUC vector [0]: kc = ed29b2f1c27f9f34 | ||||
| DAUC vector [0]: sres = df58522f | ||||
| DAUC vector [0]: auth_types = 0x3 | ||||
| rc == 1 | ||||
| aud3g.u.umts.sqn == 32 | ||||
| vector matches expectations | ||||
| ===== test_set_19: SUCCESS | ||||
|  | ||||
							
								
								
									
										2
									
								
								tests/auc/auc_ts_55_205_test_sets.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/auc/auc_ts_55_205_test_sets.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| 3GPP TS 55.205 Test Sets | ||||
| Done | ||||
							
								
								
									
										6
									
								
								tests/auc/gen_ts_55_205_test_sets/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/auc/gen_ts_55_205_test_sets/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| EXTRA_DIST = \ | ||||
| 	func_template.c \ | ||||
| 	main_template.c \ | ||||
| 	pdftxt_2_c.py \ | ||||
| 	ts55_205_test_sets.txt \ | ||||
| 	$(NULL) | ||||
							
								
								
									
										66
									
								
								tests/auc/gen_ts_55_205_test_sets/func_template.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								tests/auc/gen_ts_55_205_test_sets/func_template.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| /* gen_ts_55_205_test_sets/func_template.c: Template to generate test code | ||||
|  * from 3GPP TS 55.205 test sets */ | ||||
|  | ||||
| /* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| static void {func_name}(void) | ||||
| {{ | ||||
|         struct osmo_sub_auth_data aud2g; | ||||
|         struct osmo_sub_auth_data aud3g; | ||||
|         struct osmo_auth_vector vec; | ||||
|         int rc; | ||||
|  | ||||
|         comment_start(); | ||||
|  | ||||
|         aud2g = (struct osmo_sub_auth_data){{ 0 }}; | ||||
|  | ||||
|         aud3g = (struct osmo_sub_auth_data){{ | ||||
|                 .type = OSMO_AUTH_TYPE_UMTS, | ||||
|                 .algo = OSMO_AUTH_ALG_MILENAGE, | ||||
| 		.u.umts.sqn = 31, | ||||
|         }}; | ||||
|  | ||||
|         osmo_hexparse("{Ki}", | ||||
|                       aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); | ||||
|         osmo_hexparse("{OPc}", | ||||
|                       aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); | ||||
|  | ||||
|         osmo_hexparse("{RAND}", | ||||
|                       fake_rand, sizeof(fake_rand)); | ||||
|  | ||||
|         vec = (struct osmo_auth_vector){{ {{0}} }}; | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); | ||||
|         rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); | ||||
|         VERBOSE_ASSERT(rc, == 1, "%d"); | ||||
| 	VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); | ||||
|  | ||||
|         VEC_IS(&vec, | ||||
|                "  rand: {RAND}\n" | ||||
|                "  ck: {MIL3G-CK}\n" | ||||
|                "  ik: {MIL3G-IK}\n" | ||||
|                "  res: {MIL3G-RES}0000000000000000\n" | ||||
|                "  kc: {Kc}\n" | ||||
|                "  sres: {SRES#1}\n" | ||||
|               ); | ||||
|  | ||||
| 	comment_end(); | ||||
| }} | ||||
							
								
								
									
										119
									
								
								tests/auc/gen_ts_55_205_test_sets/main_template.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								tests/auc/gen_ts_55_205_test_sets/main_template.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| /* gen_ts_55_205_test_sets/main_template.c: Template to generate test code | ||||
|  * from 3GPP TS 55.205 test sets */ | ||||
|  | ||||
| /* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
|  | ||||
| #include <osmocom/crypt/auth.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "auc.h" | ||||
|  | ||||
| #define comment_start() fprintf(stderr, "\n===== %s\n", __func__); | ||||
| #define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__); | ||||
|  | ||||
| #define VERBOSE_ASSERT(val, expect_op, fmt) \ | ||||
| 	do { \ | ||||
| 		fprintf(stderr, #val " == " fmt "\n", (val)); \ | ||||
| 		OSMO_ASSERT((val) expect_op); \ | ||||
| 	} while (0); | ||||
|  | ||||
| char *vec_str(const struct osmo_auth_vector *vec) | ||||
| { | ||||
| 	static char buf[1024]; | ||||
| 	char *pos = buf; | ||||
| 	char *end = buf + sizeof(buf); | ||||
|  | ||||
| #define append(what) \ | ||||
| 	if (pos >= end) \ | ||||
| 		return buf; \ | ||||
| 	pos += snprintf(pos, sizeof(buf) - (pos - buf), \ | ||||
|                         "  " #what ": %s\n", \ | ||||
| 			osmo_hexdump_nospc((void*)&vec->what, sizeof(vec->what))) | ||||
|  | ||||
| 	append(rand); | ||||
| 	append(ck); | ||||
| 	append(ik); | ||||
| 	append(res); | ||||
| 	append(kc); | ||||
| 	append(sres); | ||||
| #undef append | ||||
|  | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| #define VEC_IS(vec, expect) do { \ | ||||
| 		char *_is = vec_str(vec); \ | ||||
| 	        if (strcmp(_is, expect)) { \ | ||||
| 			fprintf(stderr, "MISMATCH! expected ==\n%s\n", \ | ||||
| 				expect); \ | ||||
| 			char *a = _is; \ | ||||
| 			char *b = expect; \ | ||||
| 			for (; *a && *b; a++, b++) { \ | ||||
| 				if (*a != *b) { \ | ||||
| 					while (a > _is && *(a-1) != '\n') a--; \ | ||||
| 					fprintf(stderr, "mismatch at %d:\n" \ | ||||
| 						"%s", (int)(a - _is), a); \ | ||||
| 					break; \ | ||||
| 				} \ | ||||
| 			} \ | ||||
| 			OSMO_ASSERT(false); \ | ||||
| 		} else \ | ||||
| 			fprintf(stderr, "vector matches expectations\n"); \ | ||||
| 	} while (0) | ||||
|  | ||||
| uint8_t fake_rand[16] = { 0 }; | ||||
|  | ||||
| int rand_get(uint8_t *rand, unsigned int len) | ||||
| { | ||||
| 	OSMO_ASSERT(len <= sizeof(fake_rand)); | ||||
| 	memcpy(rand, fake_rand, len); | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| FUNCTIONS | ||||
|  | ||||
| int main() | ||||
| { | ||||
| 	printf("3GPP TS 55.205 Test Sets\n"); | ||||
| 	void *tall_ctx = talloc_named_const(NULL, 1, "test"); | ||||
| 	msgb_talloc_ctx_init(tall_ctx, 0); | ||||
| 	osmo_init_logging2(tall_ctx, &hlr_log_info); | ||||
| 	log_set_print_filename(osmo_stderr_target, 0); | ||||
| 	log_set_print_timestamp(osmo_stderr_target, 0); | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_category(osmo_stderr_target, 1); | ||||
| 	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); | ||||
|  | ||||
| FUNCTION_CALLS | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										99
									
								
								tests/auc/gen_ts_55_205_test_sets/pdftxt_2_c.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										99
									
								
								tests/auc/gen_ts_55_205_test_sets/pdftxt_2_c.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| #!/usr/bin/env python | ||||
| # FIXME: use python3 once buildslaves are updated. | ||||
| # Convert test sets pasted from 3GPP TS 55.205 to C code. | ||||
|   | ||||
| # (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
| # | ||||
| # All Rights Reserved | ||||
| # | ||||
| # Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU Affero General Public License as published by | ||||
| # the Free Software Foundation; either version 3 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU 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/>. | ||||
|  | ||||
| import sys, os | ||||
|  | ||||
| script_dir = sys.path[0] | ||||
|  | ||||
| fields = ( | ||||
|   'Ki', | ||||
|   'RAND', | ||||
|   'OP', | ||||
|   'OPc', | ||||
|   'MIL3G-RES', | ||||
|   'SRES#1', | ||||
|   'SRES#2', | ||||
|   'MIL3G-CK', | ||||
|   'MIL3G-IK', | ||||
|   'Kc', | ||||
| ) | ||||
|  | ||||
| test_sets_lines = [] | ||||
| test_set_lines = None | ||||
|  | ||||
| for line in [l.strip() for l in open(os.path.join(script_dir, 'ts55_205_test_sets.txt'), 'r')]: | ||||
|   if line.startswith('Test Set'): | ||||
|     if test_set_lines: | ||||
|       test_sets_lines.append(test_set_lines) | ||||
|     test_set_lines = [] | ||||
|   elif len(line) == 8: | ||||
|     try: | ||||
|       is_hex = int(line, 16) | ||||
|       test_set_lines.append(line) | ||||
|     except ValueError: | ||||
|       pass | ||||
|  | ||||
| if test_set_lines: | ||||
|   test_sets_lines.append(test_set_lines) | ||||
|  | ||||
| # Magic fixups for PDF-to-text uselessness | ||||
| idx = (( 0, 10, 15, 19), | ||||
|        ( 1, 11, 16, 20), | ||||
|        ( 2, 12, 17, 21), | ||||
|        ( 3, 13, 18, 22), | ||||
|        ( 4, 14), | ||||
|        ( 5, ), | ||||
|        ( 6, ), | ||||
|        ( 7, 23, 26, 28), | ||||
|        ( 8, 24, 27, 29), | ||||
|        ( 9, 25 ), | ||||
|       ) | ||||
|  | ||||
| test_sets = [] | ||||
| for l in test_sets_lines: | ||||
|   test_sets.append( [ ''.join([l[i] for i in li]) for li in idx ] ) | ||||
|  | ||||
| func_templ = open(os.path.join(script_dir, 'func_template.c'), 'r').read() | ||||
|  | ||||
| funcs = [] | ||||
| func_calls = [] | ||||
| nr = 0 | ||||
| for test_set in test_sets: | ||||
|   nr += 1 | ||||
|   func_name = 'test_set_%d' % nr | ||||
|   kwargs = dict(zip(fields, test_set)) | ||||
|   kwargs['func_name'] = func_name | ||||
|  | ||||
|   func_calls.append('\t%s();' % func_name) | ||||
|   funcs.append(func_templ.format(**kwargs)) | ||||
|  | ||||
| templ = open(os.path.join(script_dir, 'main_template.c')).read() | ||||
|  | ||||
| code = templ.replace('FUNCTIONS', '\n'.join(funcs)).replace('FUNCTION_CALLS', '\n'.join(func_calls)) | ||||
|  | ||||
| print(''' | ||||
| /***** DO NOT EDIT THIS FILE -- THIS CODE IS GENERATED ***** | ||||
|  ***** by gen_ts_55_205_test_sets/pdftxt_2_c.py        *****/ | ||||
| ''') | ||||
| print(code) | ||||
|  | ||||
							
								
								
									
										972
									
								
								tests/auc/gen_ts_55_205_test_sets/ts55_205_test_sets.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										972
									
								
								tests/auc/gen_ts_55_205_test_sets/ts55_205_test_sets.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,972 @@ | ||||
|  | ||||
| Test Set 1 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 465b5ce8 | ||||
| 23553cbe | ||||
| cdc202d5 | ||||
| cd63cb71 | ||||
| a54211d5 | ||||
| 46f8416a | ||||
| a54211d5 | ||||
| b40ba9a3 | ||||
| f769bcd7 | ||||
| eae4be82 | ||||
|  | ||||
| b199b49f | ||||
| 9637a89d | ||||
| 123e20f6 | ||||
| 954a9f4e | ||||
| e3ba50bf | ||||
|  | ||||
| aa5f0a2e | ||||
| 218ae64d | ||||
| 2b6d676a | ||||
| 48a5994e | ||||
|  | ||||
| e238a6bc | ||||
| ae47bf35 | ||||
| c72cb318 | ||||
| 37a02baf | ||||
|  | ||||
| c58b2a05 | ||||
| 51044604 | ||||
| 3af9a08b | ||||
|  | ||||
| bbf0d987 | ||||
| 12767271 | ||||
|  | ||||
| b21bf8cb | ||||
| 1c6d3441 | ||||
|  | ||||
| Test Set 2 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| fec86ba6 | ||||
| 9f7c8d02 | ||||
| dbc59adc | ||||
| 1006020f | ||||
| 8011c48c | ||||
| 8c308a5e | ||||
| 8011c48c | ||||
| 5dbdbb29 | ||||
| 59a92d3b | ||||
| aa01739b | ||||
|  | ||||
| eb707ed0 | ||||
| 1accf4db | ||||
| b6f9a0ef | ||||
| 0a478bf6 | ||||
| 0c214ed2 | ||||
|  | ||||
| 8905757b | ||||
| 213ccff0 | ||||
| 735477b7 | ||||
| b699f15c | ||||
|  | ||||
| 1bb44b8f | ||||
| c7f71a6a | ||||
| fadf8374 | ||||
| 062e42b3 | ||||
|  | ||||
| 54e8f3cd | ||||
| 476a0443 | ||||
| 8caa976d | ||||
|  | ||||
| e665b046 | ||||
| 487055cf | ||||
|  | ||||
| 179a5098 | ||||
| 88b2307b | ||||
|  | ||||
| ETSI | ||||
|  | ||||
| 3GPP TS 55.205 version 6.2.0 Release 6 | ||||
|  | ||||
| 10 | ||||
|  | ||||
| ETSI TS 155 205 V6.2.0 (2006-03) | ||||
|  | ||||
| Test Set 3 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 9e5944ae | ||||
| ce83dbc5 | ||||
| 223014c5 | ||||
| a64a507a | ||||
| f365cd68 | ||||
| cfbce3fe | ||||
| f365cd68 | ||||
| e203edb3 | ||||
| 0c4524ad | ||||
| 9a8ec95f | ||||
|  | ||||
| a94b8116 | ||||
| 4ac0274a | ||||
| 806694c0 | ||||
| e1a2a98b | ||||
| 3cd92e96 | ||||
|  | ||||
| 5c82fbf9 | ||||
| 157c17f8 | ||||
| 07ca1eee | ||||
| b88eb421 | ||||
|  | ||||
| f32db751 | ||||
| 0d017bd6 | ||||
| f57f004f | ||||
| 0135dc87 | ||||
|  | ||||
| 971574f5 | ||||
| eac041c4 | ||||
| 408cc507 | ||||
|  | ||||
| a94b0d61 | ||||
| dd830d20 | ||||
|  | ||||
| b816345d | ||||
| 854fc46b | ||||
|  | ||||
| Test Set 4 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 4ab1deb0 | ||||
| 74b0cd60 | ||||
| 2d16c5cd | ||||
| dcf07cbd | ||||
| 5860fc1b | ||||
| 9655e265 | ||||
| 5860fc1b | ||||
| 7657766b | ||||
| 1c42e960 | ||||
| cdc1dc08 | ||||
|  | ||||
| 5ca6ceb0 | ||||
| 31a1c833 | ||||
| 1fdf6b22 | ||||
| 51855290 | ||||
| ce351e7e | ||||
|  | ||||
| 51fc98e7 | ||||
| 9b2b6ce2 | ||||
| 383584e3 | ||||
| b92a07a9 | ||||
|  | ||||
| 7d026a84 | ||||
| b8c4a186 | ||||
| bef2a8d8 | ||||
| 891e523e | ||||
|  | ||||
| 373d1c21 | ||||
| d89b8fa9 | ||||
| 41b81a22 | ||||
|  | ||||
| 38f307e3 | ||||
| 9f2744e0 | ||||
|  | ||||
| de9242f9 | ||||
| 708ccb53 | ||||
|  | ||||
| Test Set 5 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 6c38a116 | ||||
| ee6466bc | ||||
| 1ba00a1a | ||||
| 3803ef53 | ||||
| 16c8233f | ||||
| 13688f17 | ||||
| 16c8233f | ||||
| 3f8c7587 | ||||
| a7466cc1 | ||||
| df75bc5e | ||||
|  | ||||
| ac280c45 | ||||
| 96202c5a | ||||
| 7c6700ac | ||||
| 63b947c6 | ||||
| 05a0ac28 | ||||
|  | ||||
| 4f59332e | ||||
| 557abbef | ||||
| 8c3ff3e9 | ||||
| aaa225e5 | ||||
|  | ||||
| e35c8c4f | ||||
| f8babf63 | ||||
| 6ad08725 | ||||
| 8fae3934 | ||||
|  | ||||
| fe8e4b23 | ||||
| e6b2a133 | ||||
| a899879f | ||||
|  | ||||
| 3af676ae | ||||
| 7d49d3b6 | ||||
|  | ||||
| de30ba3b | ||||
| 6e95d7b4 | ||||
|  | ||||
| Test Set 6 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 2d609d4d | ||||
| 194aa756 | ||||
| 460a4838 | ||||
| c35a0ab0 | ||||
| 8c25a16c | ||||
| 553d00b3 | ||||
| 8c25a16c | ||||
| 4cd08460 | ||||
| 88ab80a4 | ||||
| 84b417ae | ||||
|  | ||||
| b0ac5bf0 | ||||
| 013896b7 | ||||
| 5427aa39 | ||||
| bcbfc925 | ||||
| d918a1df | ||||
|  | ||||
| d2c0de26 | ||||
| 4b4a2a3b | ||||
| 264aac8e | ||||
| 2caff15f | ||||
|  | ||||
| 7014de0d | ||||
| 0af4539e | ||||
| fc9e73e8 | ||||
| 24efbde0 | ||||
|  | ||||
| 20f8fa07 | ||||
| 15f15c73 | ||||
| 3aeab4f3 | ||||
|  | ||||
| 31dd47cb | ||||
| 711254a1 | ||||
|  | ||||
| dc6be411 | ||||
| d388f696 | ||||
|  | ||||
| ETSI | ||||
|  | ||||
| 3GPP TS 55.205 version 6.2.0 Release 6 | ||||
|  | ||||
| 11 | ||||
|  | ||||
| ETSI TS 155 205 V6.2.0 (2006-03) | ||||
|  | ||||
| Test Set 7 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| a530a7fe | ||||
| 3a4c2b32 | ||||
| 511c6c4e | ||||
| 27953e49 | ||||
| a63241e1 | ||||
| 59f1a44a | ||||
| a63241e1 | ||||
| 10f05bab | ||||
| f9ec0865 | ||||
| 3b4e244c | ||||
|  | ||||
| 428fad10 | ||||
| 45c50eb5 | ||||
| 83e38c89 | ||||
| bc8af6dc | ||||
| ffc3e5ab | ||||
|  | ||||
| 82c45edd | ||||
| c71d0863 | ||||
| b1c5d8dd | ||||
| c6e730eb | ||||
|  | ||||
| fce13884 | ||||
| 9395764d | ||||
| e62426fa | ||||
| 80286be3 | ||||
|  | ||||
| 75a99a5f | ||||
| eb32f223 | ||||
| dc60ce03 | ||||
|  | ||||
| bb98a9c2 | ||||
| 69cade40 | ||||
|  | ||||
| 87679c3b | ||||
| c59c3a44 | ||||
|  | ||||
| Test Set 8 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| d9151cf0 | ||||
| f761e5e9 | ||||
| 75fc2233 | ||||
| c4c93eff | ||||
| 4a90b217 | ||||
| 50588861 | ||||
| 4a90b217 | ||||
| 71236b71 | ||||
| 90527eba | ||||
| 8d4ec01d | ||||
|  | ||||
| 4896e258 | ||||
| 3d603feb | ||||
| a44294ee | ||||
| e8a08138 | ||||
| 1ac83a76 | ||||
|  | ||||
| 30bf2e08 | ||||
| 730e2755 | ||||
| 8e6de25c | ||||
| c203d4c2 | ||||
|  | ||||
| 267b8360 | ||||
| 6cb8a2ca | ||||
| 4353d26b | ||||
| 7ce4e3d9 | ||||
|  | ||||
| 29f9b22a | ||||
| a5588968 | ||||
| e597acfe | ||||
|  | ||||
| b77ea7a5 | ||||
| db417273 | ||||
|  | ||||
| 4c96da22 | ||||
| 25a04d9e | ||||
|  | ||||
| Test Set 9 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| a0e2971b | ||||
| 08eff828 | ||||
| 323792fa | ||||
| 82a26f22 | ||||
| 4bc2212d | ||||
| cde6b027 | ||||
| 4bc2212d | ||||
| 08cef6d0 | ||||
| ed0318ca | ||||
| d8debc4f | ||||
|  | ||||
| 6822e8d3 | ||||
| b13fdb56 | ||||
| ca21fb4d | ||||
| bba9e948 | ||||
| 8624910a | ||||
|  | ||||
| 54a18cc2 | ||||
| 2722c65c | ||||
| 5d6f13c1 | ||||
| 8f949a10 | ||||
|  | ||||
| 35624ecb | ||||
| 7f30a9b2 | ||||
| 45a9d2c1 | ||||
| d98e9cc4 | ||||
|  | ||||
| 04ec6147 | ||||
| 5deb9206 | ||||
| fbcd60aa | ||||
|  | ||||
| 1a3c3cda | ||||
| 272f6e8f | ||||
|  | ||||
| 048137fa | ||||
| a64ba411 | ||||
|  | ||||
| Test Set 10 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 0da6f7ba | ||||
| 679ac4db | ||||
| 4b9a26fa | ||||
| 0db1071f | ||||
| 6fc30fee | ||||
| 02d13acd | ||||
| 6fc30fee | ||||
| 69b1cae7 | ||||
| 74f24e8c | ||||
| f0eaa50a | ||||
|  | ||||
| 86d5eac8 | ||||
| acd7d233 | ||||
| 459e3acb | ||||
| 8767562c | ||||
| 6d123523 | ||||
|  | ||||
| a19cf563 | ||||
| ff9d6806 | ||||
| ff36f401 | ||||
| a43a0a64 | ||||
|  | ||||
| ac58642d | ||||
| f4149ce3 | ||||
| 5de3bdc1 | ||||
| c41e8d08 | ||||
|  | ||||
| c7429d97 | ||||
| 26df58e1 | ||||
| 1edcebb7 | ||||
|  | ||||
| 5e245cac | ||||
| b38d7dcd | ||||
|  | ||||
| b05a517c | ||||
| 4f1b7fbd | ||||
|  | ||||
| ETSI | ||||
|  | ||||
| 3GPP TS 55.205 version 6.2.0 Release 6 | ||||
|  | ||||
| 12 | ||||
|  | ||||
| ETSI TS 155 205 V6.2.0 (2006-03) | ||||
|  | ||||
| Test Set 11 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 77b45843 | ||||
| 4c47eb30 | ||||
| bf3286c7 | ||||
| d483afae | ||||
| aefa357b | ||||
| 44389d01 | ||||
| aefa357b | ||||
| 908c43f0 | ||||
| c251df0d | ||||
| 82dbab7f | ||||
|  | ||||
| c88e58c1 | ||||
| 76dc55fe | ||||
| a51409ce | ||||
| 562409a3 | ||||
| eac2a87a | ||||
|  | ||||
| 0d202684 | ||||
| 5106cb20 | ||||
| 95724d50 | ||||
| 26b5bb0b | ||||
|  | ||||
| 515ed430 | ||||
| 34b8cd78 | ||||
| 3bfe6e70 | ||||
| 20c4d762 | ||||
|  | ||||
| 569cb8f7 | ||||
| 888dd932 | ||||
| 83f063da | ||||
|  | ||||
| 4bc971e7 | ||||
| 9bcf4665 | ||||
|  | ||||
| 06c36c5f | ||||
| 5b226e40 | ||||
|  | ||||
| Test Set 12 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 729b1772 | ||||
| 311c4c92 | ||||
| d04c9c35 | ||||
| 228c2f2f | ||||
| 98dbbd09 | ||||
| 03e0fd84 | ||||
| 98dbbd09 | ||||
| 44c0f23c | ||||
| 0c9fb816 | ||||
| 3c66cb98 | ||||
|  | ||||
| 9270dd87 | ||||
| 9744d675 | ||||
| bd2262fa | ||||
| 06ac3268 | ||||
| 9b3b408d | ||||
|  | ||||
| ccdf1bfe | ||||
| b720f3b7 | ||||
| 810d2924 | ||||
| a9e616ee | ||||
|  | ||||
| 29b4e9bb | ||||
| e9b1cbd0 | ||||
| d036fd13 | ||||
| 16db4ba1 | ||||
|  | ||||
| 5493cfd2 | ||||
| 13884c25 | ||||
| cab2d33d | ||||
|  | ||||
| 41e48f19 | ||||
| 35dd0eab | ||||
|  | ||||
| 7e1d1012 | ||||
| f3b440d8 | ||||
|  | ||||
| Test Set 13 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| d32dd23e | ||||
| cf7d0ab1 | ||||
| fe75905b | ||||
| d22a4b41 | ||||
| af4a411e | ||||
| be73b3dc | ||||
| af4a411e | ||||
| 5af86b80 | ||||
| 7f4d6ae7 | ||||
| 9612b5d8 | ||||
|  | ||||
| 89dc6623 | ||||
| d9430695 | ||||
| 9da47d35 | ||||
| 80a53257 | ||||
| 1139f2c2 | ||||
|  | ||||
| 54ca12eb | ||||
| 0bf12018 | ||||
| 6236d031 | ||||
| 08a5ff70 | ||||
|  | ||||
| 79dd32fa | ||||
| fbd46887 | ||||
| 4e09c32e | ||||
| d9f67ec7 | ||||
|  | ||||
| edb70df5 | ||||
| 440e1878 | ||||
| 8a4130bb | ||||
|  | ||||
| 292cc112 | ||||
| 9a8b75ad | ||||
|  | ||||
| 1cbad50c | ||||
| 3f42f03a | ||||
|  | ||||
| Test Set 14 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| af7c65e1 | ||||
| 1f0f8578 | ||||
| 0c7acb8d | ||||
| a4cf5c81 | ||||
| 7bffa5c2 | ||||
| 8fe019c7 | ||||
| 7bffa5c2 | ||||
| 3f8c3f3c | ||||
| abcbae8f | ||||
| 75a150df | ||||
|  | ||||
| 927221de | ||||
| 464fd59b | ||||
| 95b7d4a3 | ||||
| 55c08a7e | ||||
| f41fbc05 | ||||
|  | ||||
| 591187a2 | ||||
| 64bed2d0 | ||||
| 1c5aca6d | ||||
| ff418e54 | ||||
|  | ||||
| c5987a53 | ||||
| 9436b57a | ||||
| 26345a88 | ||||
| 43b98e55 | ||||
|  | ||||
| cf7625bf | ||||
| d46115e9 | ||||
| 3c6aed08 | ||||
|  | ||||
| 77fc94bc | ||||
| 961a55d0 | ||||
|  | ||||
| fd22fd26 | ||||
| da5f2078 | ||||
|  | ||||
| ETSI | ||||
|  | ||||
| 3GPP TS 55.205 version 6.2.0 Release 6 | ||||
|  | ||||
| 13 | ||||
|  | ||||
| ETSI TS 155 205 V6.2.0 (2006-03) | ||||
|  | ||||
| Test Set 15 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 5bd7ecd3 | ||||
| 59b75f14 | ||||
| f967f760 | ||||
| 76089d3c | ||||
| 7e3f44c7 | ||||
| 27202b82 | ||||
| 7e3f44c7 | ||||
| d42b2d61 | ||||
| 0b3f8d02 | ||||
| b7f92e42 | ||||
|  | ||||
| d3127a41 | ||||
| 251c7503 | ||||
| 38b920a9 | ||||
| 0ff3efdc | ||||
| 591f6f45 | ||||
|  | ||||
| d12539be | ||||
| 1d0bcbac | ||||
| cd25e10c | ||||
| 6e36721d | ||||
|  | ||||
| d4e7cf71 | ||||
| 1c2c04c7 | ||||
| 08b49924 | ||||
| 4fceb747 | ||||
|  | ||||
| 5e49a03a | ||||
| 4fe6bfaf | ||||
| 6a36fec5 | ||||
|  | ||||
| c275a5ae | ||||
| aa982b8f | ||||
|  | ||||
| f97af892 | ||||
| 82e319c2 | ||||
|  | ||||
| Test Set 16 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 6cd1c6ce | ||||
| f69b78f3 | ||||
| 078bfca9 | ||||
| a219dc37 | ||||
| 70f6bdb9 | ||||
| ddd7efe6 | ||||
| 70f6bdb9 | ||||
| 6edaf99e | ||||
| d61c853c | ||||
| 88d9de10 | ||||
|  | ||||
| b1e01e14 | ||||
| 00a0568b | ||||
| 564659ec | ||||
| f1dc7d66 | ||||
| ad21525f | ||||
|  | ||||
| f1b82316 | ||||
| ce9f0cb9 | ||||
| d8851e84 | ||||
| 738b5843 | ||||
|  | ||||
| a90b7f3d | ||||
| 3c4be4c9 | ||||
| e6c59b48 | ||||
| c799f206 | ||||
|  | ||||
| 5bd9f85d | ||||
| 280dd9c4 | ||||
| a22004c5 | ||||
|  | ||||
| 5f36d91c | ||||
| 6f297bae | ||||
|  | ||||
| 1272fb4b | ||||
| c386de17 | ||||
|  | ||||
| Test Set 17 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| b73a90cb | ||||
| b120f1c1 | ||||
| b672047e | ||||
| df0c6786 | ||||
| 479dd25c | ||||
| 67e4ff3f | ||||
| 479dd25c | ||||
| 66195dbe | ||||
| 66bec707 | ||||
| a819e577 | ||||
|  | ||||
| cf3afb62 | ||||
| a0102a2f | ||||
| 003bb952 | ||||
| 8fa25f74 | ||||
| 20792d63 | ||||
|  | ||||
| 2dba83c5 | ||||
| 507dd543 | ||||
| dca6cb8a | ||||
| 8b7044c6 | ||||
|  | ||||
| 8a8415df | ||||
| de68281f | ||||
| f0e5b779 | ||||
| e7c245b8 | ||||
|  | ||||
| d0313274 | ||||
| eb2afc47 | ||||
| a8d6175b | ||||
|  | ||||
| c5ca7766 | ||||
| 6d7408a8 | ||||
|  | ||||
| 615fa25e | ||||
| f2927b36 | ||||
|  | ||||
| Test Set 18 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 51222502 | ||||
| 81e92b6c | ||||
| c9e87632 | ||||
| 981d464c | ||||
| 28d7b0f2 | ||||
| 8a3b8d17 | ||||
| 28d7b0f2 | ||||
| 5349fbe0 | ||||
| 9744871a | ||||
| 9a8d0e88 | ||||
|  | ||||
| 14c33e72 | ||||
| 0ee0e12e | ||||
| 86b5b9ff | ||||
| 7c52eb6e | ||||
| a2ec3de5 | ||||
|  | ||||
| 3a5dd523 | ||||
| bceba8d9 | ||||
| bdf56e12 | ||||
| 50362349 | ||||
|  | ||||
| fc145fc0 | ||||
| 2a99dfa5 | ||||
| 97d0887b | ||||
| 84ad0bcf | ||||
|  | ||||
| 98649f94 | ||||
| d32bf9bb | ||||
| 3ff0887a | ||||
|  | ||||
| 8f5d2e97 | ||||
| d1dd5ce5 | ||||
|  | ||||
| 3a81c00f | ||||
| 4e3e2e5a | ||||
|  | ||||
| ETSI | ||||
|  | ||||
| 3GPP TS 55.205 version 6.2.0 Release 6 | ||||
|  | ||||
| Test Set 19 | ||||
| Ki | ||||
| RAND | ||||
| OP | ||||
| OPc | ||||
| MIL3G-RES | ||||
| SRES#1 | ||||
| SRES#2 | ||||
| MIL3G-CK | ||||
| MIL3G-IK | ||||
| Kc | ||||
|  | ||||
| 90dca4ed | ||||
| 9fddc720 | ||||
| 3ffcfe5b | ||||
| cb9cccc4 | ||||
| a95100e2 | ||||
| df58522f | ||||
| a95100e2 | ||||
| b5f2da03 | ||||
| b4721368 | ||||
| ed29b2f1 | ||||
|  | ||||
| 14 | ||||
|  | ||||
| ETSI TS 155 205 V6.2.0 (2006-03) | ||||
|  | ||||
| a45b53cf | ||||
| 92c6ad03 | ||||
| 7b111158 | ||||
| b9258e6d | ||||
| 760952cd | ||||
|  | ||||
| 0f12d7c9 | ||||
| 6b6e4647 | ||||
| 9920d352 | ||||
| ca476037 | ||||
|  | ||||
| c3bc6a89 | ||||
| 89315b78 | ||||
| 8e84e655 | ||||
| 9fb82581 | ||||
|  | ||||
| 883b69f9 | ||||
| bc16ea67 | ||||
| c27f9f34 | ||||
|  | ||||
| 6bf52e02 | ||||
| 875c5598 | ||||
|  | ||||
| 9ed9ac45 | ||||
| 688bb0ef | ||||
|  | ||||
							
								
								
									
										58
									
								
								tests/db/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/db/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| AM_CFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/src \ | ||||
| 	-I$(top_builddir)/src \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOABIS_CFLAGS) \ | ||||
| 	$(SQLITE3_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	db_test.ok \ | ||||
| 	db_test.err \ | ||||
| 	$(NULL) | ||||
|  | ||||
| check_PROGRAMS = db_test | ||||
|  | ||||
| db_test_SOURCES = \ | ||||
| 	db_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| db_test_LDADD = \ | ||||
| 	$(top_builddir)/src/logging.o \ | ||||
| 	$(top_builddir)/src/db_auc.o \ | ||||
| 	$(top_builddir)/src/db_hlr.o \ | ||||
| 	$(top_builddir)/src/db.o \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOABIS_LIBS) \ | ||||
| 	$(SQLITE3_LIBS) \ | ||||
| 	$(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 | ||||
| db_test.db: | ||||
| 	rm -f db_test.db | ||||
| 	sqlite3 $(builddir)/db_test.db < $(top_srcdir)/sql/hlr.sql | ||||
|  | ||||
| update_exp: db_test.db | ||||
| 	cd $(builddir); ./db_test >"$(srcdir)/db_test.ok" 2>"$(srcdir)/db_test.err" | ||||
|  | ||||
| manual: db_test.db | ||||
| 	cd $(builddir); ./db_test -v | ||||
|  | ||||
| manual-nonverbose: db_test.db | ||||
| 	cd $(builddir); ./db_test | ||||
|  | ||||
| manual-gdb: db_test.db | ||||
| 	cd $(builddir); gdb -ex run --args ./db_test -v | ||||
							
								
								
									
										992
									
								
								tests/db/db_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										992
									
								
								tests/db/db_test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,992 @@ | ||||
| /* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <getopt.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "logging.h" | ||||
|  | ||||
| #define comment_start() fprintf(stderr, "\n===== %s\n", __func__); | ||||
| #define comment(fmt, args...) fprintf(stderr, "\n--- " fmt "\n\n", ## args); | ||||
| #define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__); | ||||
|  | ||||
| #define fill_invalid(x) _fill_invalid(&x, sizeof(x)) | ||||
| static void _fill_invalid(void *dest, size_t size) | ||||
| { | ||||
| 	uint8_t *pos = dest; | ||||
| 	size_t remain = size; | ||||
| 	int wrote = 0; | ||||
| 	do { | ||||
| 		remain -= wrote; | ||||
| 		pos += wrote; | ||||
| 		wrote = snprintf((void*)pos, remain, "-invalid-data"); | ||||
| 	} while (wrote < remain); | ||||
| } | ||||
|  | ||||
| /* Perform a function call and verbosely assert that its return value is as expected. | ||||
|  * The return code is then available in g_rc. */ | ||||
| #define ASSERT_RC(call, expect_rc) \ | ||||
| 	do { \ | ||||
| 		fprintf(stderr, #call " --> " #expect_rc "\n"); \ | ||||
| 		g_rc = call; \ | ||||
| 		if (g_rc != (expect_rc)) \ | ||||
| 			fprintf(stderr, " MISMATCH: got rc = %d, expected: " \ | ||||
|                                         #expect_rc " = %d\n", g_rc, expect_rc); \ | ||||
| 		OSMO_ASSERT(g_rc == (expect_rc)); \ | ||||
| 		fprintf(stderr, "\n"); \ | ||||
| 	} while (0) | ||||
|  | ||||
| /* Do db_subscr_get_by_xxxx and verbosely assert that its return value is as expected. | ||||
|  * Print the subscriber struct to stderr to be validated by db_test.err. | ||||
|  * The result is then available in g_subscr. */ | ||||
| #define ASSERT_SEL(by, val, expect_rc) \ | ||||
| 	do { \ | ||||
| 		int rc; \ | ||||
| 		fill_invalid(g_subscr); \ | ||||
| 		fprintf(stderr, "db_subscr_get_by_" #by "(dbc, " #val ", &g_subscr) --> " \ | ||||
|                                 #expect_rc "\n"); \ | ||||
| 		rc = db_subscr_get_by_##by(dbc, val, &g_subscr); \ | ||||
| 		if (rc != (expect_rc)) \ | ||||
| 			fprintf(stderr, " MISMATCH: got rc = %d, expected: " \ | ||||
|                                         #expect_rc " = %d\n", rc, expect_rc); \ | ||||
| 		OSMO_ASSERT(rc == (expect_rc)); \ | ||||
| 		if (!rc) \ | ||||
| 			dump_subscr(&g_subscr); \ | ||||
| 		fprintf(stderr, "\n"); \ | ||||
| 	} while (0) | ||||
|  | ||||
| /* Do db_get_auth_data() and verbosely assert that its return value is as expected. | ||||
|  * Print the subscriber struct to stderr to be validated by db_test.err. | ||||
|  * The results are then available in g_aud2g and g_aud3g. */ | ||||
| #define ASSERT_SEL_AUD(imsi, expect_rc, expect_id) \ | ||||
| 	do { \ | ||||
| 		fill_invalid(g_aud2g); \ | ||||
| 		fill_invalid(g_aud3g); \ | ||||
| 		g_id = 0; \ | ||||
| 		ASSERT_RC(db_get_auth_data(dbc, imsi, &g_aud2g, &g_aud3g, &g_id), expect_rc); \ | ||||
| 		if (!g_rc) { \ | ||||
| 			dump_aud("2G", &g_aud2g); \ | ||||
| 			dump_aud("3G", &g_aud3g); \ | ||||
| 		}\ | ||||
| 		if (g_id != expect_id) {\ | ||||
| 			fprintf(stderr, "MISMATCH: got subscriber id %"PRId64 \ | ||||
| 				", expected %"PRId64"\n", g_id, (int64_t)(expect_id)); \ | ||||
| 			OSMO_ASSERT(g_id == expect_id); \ | ||||
| 		} \ | ||||
| 		fprintf(stderr, "\n"); \ | ||||
| 	} while (0) | ||||
|  | ||||
| #define N_VECTORS 3 | ||||
|  | ||||
| #define ASSERT_DB_GET_AUC(imsi, expect_rc) \ | ||||
| 	do { \ | ||||
| 		struct osmo_auth_vector vec[N_VECTORS]; \ | ||||
| 		ASSERT_RC(db_get_auc(dbc, imsi, 3, vec, N_VECTORS, NULL, NULL), expect_rc); \ | ||||
| 	} while (0) | ||||
|  | ||||
| /* Not linking the real auc_compute_vectors(), just returning num_vec. | ||||
|  * This gets called by db_get_auc(), but we're only interested in its rc. */ | ||||
| int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, | ||||
| 			struct osmo_sub_auth_data *aud2g, | ||||
| 			struct osmo_sub_auth_data *aud3g, | ||||
| 			const uint8_t *rand_auts, const uint8_t *auts) | ||||
| { return num_vec; } | ||||
|  | ||||
| static struct db_context *dbc = NULL; | ||||
| static void *ctx = NULL; | ||||
| static struct hlr_subscriber g_subscr; | ||||
| static struct osmo_sub_auth_data g_aud2g; | ||||
| static struct osmo_sub_auth_data g_aud3g; | ||||
| static int g_rc; | ||||
| static int64_t g_id; | ||||
|  | ||||
| #define Pfv(name, fmt, val) \ | ||||
| 	fprintf(stderr, "  ." #name " = " fmt ",\n", val) | ||||
| #define Pfo(name, fmt, obj) \ | ||||
| 	Pfv(name, fmt, obj->name) | ||||
|  | ||||
| /* Print a subscriber struct to stderr to be validated by db_test.err. */ | ||||
| void dump_subscr(struct hlr_subscriber *subscr) | ||||
| { | ||||
| #define Ps(name) \ | ||||
| 	if (*subscr->name) \ | ||||
| 		Pfo(name, "'%s'", subscr) | ||||
| #define Pd(name) \ | ||||
| 	Pfv(name, "%"PRId64, (int64_t)subscr->name) | ||||
| #define Pd_nonzero(name) \ | ||||
| 	if (subscr->name) \ | ||||
| 		Pd(name) | ||||
| #define Pb(if_val, name) \ | ||||
| 	if (subscr->name == (if_val)) \ | ||||
| 		Pfv(name, "%s", subscr->name ? "true" : "false") | ||||
|  | ||||
| 	fprintf(stderr, "struct hlr_subscriber {\n"); | ||||
| 	Pd(id); | ||||
| 	Ps(imsi); | ||||
| 	Ps(msisdn); | ||||
| 	Ps(imei); | ||||
| 	Ps(vlr_number); | ||||
| 	Ps(sgsn_number); | ||||
| 	Ps(sgsn_address); | ||||
| 	Pd_nonzero(periodic_lu_timer); | ||||
| 	Pd_nonzero(periodic_rau_tau_timer); | ||||
| 	Pb(false, nam_cs); | ||||
| 	Pb(false, nam_ps); | ||||
| 	if (subscr->lmsi) | ||||
| 		Pfo(lmsi, "0x%x", subscr); | ||||
| 	Pb(true, ms_purged_cs); | ||||
| 	Pb(true, ms_purged_ps); | ||||
| 	fprintf(stderr, "}\n"); | ||||
| #undef Ps | ||||
| #undef Pd | ||||
| #undef Pd_nonzero | ||||
| #undef Pb | ||||
| } | ||||
|  | ||||
| void dump_aud(const char *label, struct osmo_sub_auth_data *aud) | ||||
| { | ||||
| 	if (aud->type == OSMO_AUTH_TYPE_NONE) { | ||||
| 		fprintf(stderr, "%s: none\n", label); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	fprintf(stderr, "%s: struct osmo_sub_auth_data {\n", label); | ||||
| #define Pf(name, fmt) \ | ||||
| 	Pfo(name, fmt, aud) | ||||
| #define Phex(name) \ | ||||
| 	Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, sizeof(aud->name))) | ||||
|  | ||||
| 	Pfv(type, "%s", osmo_sub_auth_type_name(aud->type)); | ||||
| 	Pfv(algo, "%s", osmo_auth_alg_name(aud->algo)); | ||||
| 	switch (aud->type) { | ||||
| 	case OSMO_AUTH_TYPE_GSM: | ||||
| 		Phex(u.gsm.ki); | ||||
| 		break; | ||||
| 	case OSMO_AUTH_TYPE_UMTS: | ||||
| 		Phex(u.umts.opc); | ||||
| 		Pf(u.umts.opc_is_op, "%u"); | ||||
| 		Phex(u.umts.k); | ||||
| 		Phex(u.umts.amf); | ||||
| 		if (aud->u.umts.sqn) { | ||||
| 			Pf(u.umts.sqn, "%"PRIu64); | ||||
| 			Pf(u.umts.sqn, "0x%"PRIx64); | ||||
| 		} | ||||
| 		if (aud->u.umts.ind_bitlen) | ||||
| 			Pf(u.umts.ind_bitlen, "%u"); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	fprintf(stderr, "}\n"); | ||||
|  | ||||
| #undef Pf | ||||
| #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 *imsi1 = "123456789000001"; | ||||
| static const char *imsi2 = "123456789000002"; | ||||
| static const char *short_imsi = "123456"; | ||||
| static const char *unknown_imsi = "999999999"; | ||||
|  | ||||
| static void test_subscr_create_update_sel_delete() | ||||
| { | ||||
| 	int64_t id0, id1, id2, id_short; | ||||
| 	comment_start(); | ||||
|  | ||||
| 	comment("Create with valid / invalid IMSI"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	id0 = g_subscr.id; | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0); | ||||
| 	ASSERT_SEL(imsi, imsi1, 0); | ||||
| 	id1 = g_subscr.id; | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0); | ||||
| 	ASSERT_SEL(imsi, imsi2, 0); | ||||
| 	id2 = g_subscr.id; | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO); | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO); | ||||
| 	ASSERT_SEL(imsi, imsi1, 0); | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO); | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO); | ||||
| 	ASSERT_SEL(imsi, imsi2, 0); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, "123456789000003", -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, "123456789000002123456", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), | ||||
| 		  -EINVAL); | ||||
| 	ASSERT_SEL(imsi, "123456789000002123456", -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, "foobar123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, "foobar123", -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, "123", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, "123", -ENOENT); | ||||
|  | ||||
| 	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); | ||||
| 	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"); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, "54321"), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "54321", 0); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "54321012345678912345678"), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "54321", 0); | ||||
| 	ASSERT_SEL(msisdn, "54321012345678912345678", -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "543 21"), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "543 21", -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "foobar123"), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "foobar123", -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "5"), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "5", 0); | ||||
| 	ASSERT_SEL(msisdn, "54321", -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "543210123456789"), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_SEL(msisdn, "543210123456789", 0); | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, imsi0, | ||||
| 					  "5432101234567891"), -EINVAL); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	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"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, unknown_imsi, "99"), -ENOENT); | ||||
| 	ASSERT_SEL(msisdn, "99", -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_msisdn_by_imsi(dbc, "foobar", "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"); | ||||
|  | ||||
| 	/*                                nam_val, is_ps */ | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
|  | ||||
| 	comment("Set / unset nam_cs and nam_ps *again*"); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
|  | ||||
| 	comment("Set nam_cs and nam_ps on non-existent / invalid IMSI"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, true), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, unknown_imsi, false, false), -ENOENT); | ||||
| 	ASSERT_SEL(imsi, unknown_imsi, -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, "foobar", false, true), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_nam(dbc, "foobar", false, false), -ENOENT); | ||||
|  | ||||
| 	comment("Record LU for PS and CS (SGSN and VLR names)"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Record LU for PS and CS (SGSN and VLR names) *again*"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Unset LU info for PS and CS (SGSN and VLR names)"); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Record LU for non-existent ID"); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT); | ||||
| 	ASSERT_SEL(id, 99999, -ENOENT); | ||||
|  | ||||
| 	comment("Purge and un-purge PS and CS"); | ||||
|  | ||||
| 	/*                               purge_val, is_ps */ | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
|  | ||||
| 	comment("Purge PS and CS *again*"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, true), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, true, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, imsi0, false, false), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
|  | ||||
| 	comment("Purge on non-existent / invalid IMSI"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, unknown_imsi, true, true), -ENOENT); | ||||
| 	ASSERT_SEL(imsi, unknown_imsi, -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_purge(dbc, unknown_imsi, true, false), -ENOENT); | ||||
| 	ASSERT_SEL(imsi, unknown_imsi, -ENOENT); | ||||
|  | ||||
| 	comment("Delete non-existent / invalid IDs"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, 999), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, -10), -ENOENT); | ||||
|  | ||||
| 	comment("Delete subscribers"); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id0), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id0), -ENOENT); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi1, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id1), 0); | ||||
| 	ASSERT_SEL(imsi, imsi1, -ENOENT); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi2, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id2), 0); | ||||
| 	ASSERT_SEL(imsi, imsi2, -ENOENT); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, short_imsi, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id_short), 0); | ||||
| 	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(); | ||||
| } | ||||
|  | ||||
| static const struct sub_auth_data_str *mk_aud_2g(enum osmo_auth_algo algo, | ||||
| 						 const char *ki) | ||||
| { | ||||
| 	static struct sub_auth_data_str aud; | ||||
| 	aud = (struct sub_auth_data_str){ | ||||
| 		.type = OSMO_AUTH_TYPE_GSM, | ||||
| 		.algo = algo, | ||||
| 		.u.gsm.ki = ki, | ||||
| 	}; | ||||
| 	return &aud; | ||||
| } | ||||
|  | ||||
| static const struct sub_auth_data_str *mk_aud_3g(enum osmo_auth_algo algo, | ||||
| 						 const char *opc, bool opc_is_op, | ||||
| 						 const char *k, unsigned int ind_bitlen) | ||||
| { | ||||
| 	static struct sub_auth_data_str aud; | ||||
| 	aud = (struct sub_auth_data_str){ | ||||
| 		.type = OSMO_AUTH_TYPE_UMTS, | ||||
| 		.algo = algo, | ||||
| 		.u.umts.k = k, | ||||
| 		.u.umts.opc = opc, | ||||
| 		.u.umts.opc_is_op = opc_is_op ? 1 : 0, | ||||
| 		.u.umts.ind_bitlen = ind_bitlen, | ||||
| 	}; | ||||
| 	return &aud; | ||||
| } | ||||
|  | ||||
| static void test_subscr_aud() | ||||
| { | ||||
| 	int64_t id; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	comment("Get auth data for non-existent subscriber"); | ||||
| 	ASSERT_SEL_AUD(unknown_imsi, -ENOENT, 0); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOENT); | ||||
|  | ||||
| 	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; | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
|  | ||||
| 	comment("Set auth data, 2G only"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, N_VECTORS); | ||||
|  | ||||
| 	/* same again */ | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_COMP128v2, "BeadedBeeAced1EbbedDefacedFacade")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "DeafBeddedBabeAcceededFadedDecaf")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, 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); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
| 	/* Removing nothing results in -ENOENT */ | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)), | ||||
| 		-ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
|  | ||||
| 	comment("Set auth data, 3G only"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", true, | ||||
| 			  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, N_VECTORS); | ||||
|  | ||||
| 	/* same again */ | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", true, | ||||
| 			  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "Deaf0ff1ceD0d0DabbedD1ced1ceF00d", true, | ||||
| 			  "F1bbed0afD0eF0bD0ffed0ddF1fe0b0e", 0)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", false, | ||||
| 			  "DeafBeddedBabeAcceededFadedDecaf", | ||||
| 			  OSMO_MILENAGE_IND_BITLEN_MAX)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "CededEffacedAceFacedBadFadedBeef", false, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	comment("Remove 3G auth data"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
| 	/* Removing nothing results in -ENOENT */ | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)), | ||||
| 		-ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "CededEffacedAceFacedBadFadedBeef", false, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, N_VECTORS); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_NONE, | ||||
| 			  "asdfasdfasd", false, | ||||
| 			  "asdfasdfasdf", 99999)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
|  | ||||
| 	comment("Set auth data, 2G and 3G"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "CededEffacedAceFacedBadFadedBeef")), | ||||
| 		0); | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", false, | ||||
| 			  "DeafBeddedBabeAcceededFadedDecaf", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, N_VECTORS); | ||||
|  | ||||
|  | ||||
| 	comment("Set invalid auth data"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(99999, "f000000000000f00000000000f000000")), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_2g(OSMO_AUTH_ALG_MILENAGE, "0123456789abcdef0123456789abcdef")), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "0f000000000000f00000000000f000000", false, | ||||
| 			  "f000000000000f00000000000f000000", 5)), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "f000000000000f00000000000f000000", false, | ||||
| 			  "000000000000f00000000000f000000", 5)), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "f000000000000f00000000000f000000", false, | ||||
| 			  "f000000000000f00000000000f000000", | ||||
| 			  OSMO_MILENAGE_IND_BITLEN_MAX + 1)), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "X000000000000f00000000000f000000", false, | ||||
| 			  "f000000000000f00000000000f000000", 5)), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "f000000000000f00000000000f000000", false, | ||||
| 			  "f000000000000 f00000000000 f000000", 5)), | ||||
| 		-EINVAL); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	comment("Delete subscriber"); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, -ENOENT); | ||||
|  | ||||
| 	comment("Re-add subscriber and verify auth data didn't come back"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
|  | ||||
| 	/* For this test to work, we want to get the same subscriber ID back, | ||||
| 	 * and make sure there are no auth data leftovers for this ID. */ | ||||
| 	OSMO_ASSERT(id == g_subscr.id); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOKEY); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, -ENOENT); | ||||
| 	ASSERT_DB_GET_AUC(imsi0, -ENOENT); | ||||
|  | ||||
| 	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() | ||||
| { | ||||
| 	int64_t id; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	comment("Set SQN for unknown subscriber"); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, 99, 999), -ENOENT); | ||||
| 	ASSERT_SEL(id, 99, -ENOENT); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, 9999, 99), -ENOENT); | ||||
| 	ASSERT_SEL(id, 9999, -ENOENT); | ||||
|  | ||||
| 	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; | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
|  | ||||
| 	comment("Set SQN, but no 3G auth data present"); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 123), -ENOENT); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 543), -ENOENT); | ||||
| 	ASSERT_SEL_AUD(imsi0, -ENOKEY, id); | ||||
|  | ||||
| 	comment("Set auth 3G data"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, | ||||
| 		mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, | ||||
| 			  "BeefedCafeFaceAcedAddedDecadeFee", true, | ||||
| 			  "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)), | ||||
| 		0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	comment("Set SQN"); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 23315), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 23315), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 423), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	comment("Set SQN: thru uint64_t range, using the int64_t SQLite bind"); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, 0), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, INT64_MAX), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, INT64_MIN), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	ASSERT_RC(db_update_sqn(dbc, id, UINT64_MAX), 0); | ||||
| 	ASSERT_SEL_AUD(imsi0, 0, id); | ||||
|  | ||||
| 	comment("Delete subscriber"); | ||||
|  | ||||
| 	ASSERT_SEL(imsi, imsi0, 0); | ||||
| 	ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0); | ||||
| 	ASSERT_SEL(imsi, imsi0, -ENOENT); | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| static struct { | ||||
| 	bool verbose; | ||||
| } cmdline_opts = { | ||||
| 	.verbose = false, | ||||
| }; | ||||
|  | ||||
| static void print_help(const char *program) | ||||
| { | ||||
| 	printf("Usage:\n" | ||||
| 	       "  %s [-v] [N [N...]]\n" | ||||
| 	       "Options:\n" | ||||
| 	       "  -h --help      show this text.\n" | ||||
| 	       "  -v --verbose   print source file and line numbers\n", | ||||
| 	       program | ||||
| 	       ); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv) | ||||
| { | ||||
| 	while (1) { | ||||
| 		int option_index = 0, c; | ||||
| 		static struct option long_options[] = { | ||||
| 			{"help", 0, 0, 'h'}, | ||||
| 			{"verbose", 1, 0, 'v'}, | ||||
| 			{0, 0, 0, 0} | ||||
| 		}; | ||||
|  | ||||
| 		c = getopt_long(argc, argv, "hv", | ||||
| 				long_options, &option_index); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
|  | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			print_help(argv[0]); | ||||
| 			exit(0); | ||||
| 		case 'v': | ||||
| 			cmdline_opts.verbose = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* catch unknown options *as well as* missing arguments. */ | ||||
| 			fprintf(stderr, "Error in command line options. Exiting.\n"); | ||||
| 			exit(-1); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (optind < argc) { | ||||
| 		fprintf(stderr, "too many args\n"); | ||||
| 		exit(-1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	printf("db_test.c\n"); | ||||
|  | ||||
| 	ctx = talloc_named_const(NULL, 1, "db_test"); | ||||
|  | ||||
| 	handle_options(argc, argv); | ||||
|  | ||||
| 	osmo_init_logging2(ctx, &hlr_log_info); | ||||
| 	log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose); | ||||
| 	log_set_print_timestamp(osmo_stderr_target, 0); | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_category(osmo_stderr_target, 1); | ||||
| 	log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); | ||||
|  | ||||
| 	/* omit the SQLite version and compilation flags from test output */ | ||||
| 	log_set_log_level(osmo_stderr_target, LOGL_ERROR); | ||||
| 	/* Disable SQLite logging so that we're not vulnerable on SQLite error messages changing across | ||||
| 	 * library versions. */ | ||||
| 	dbc = db_open(ctx, "db_test.db", false, false); | ||||
| 	log_set_log_level(osmo_stderr_target, 0); | ||||
| 	OSMO_ASSERT(dbc); | ||||
|  | ||||
| 	test_subscr_create_update_sel_delete(); | ||||
| 	test_subscr_aud(); | ||||
| 	test_subscr_aud_invalid_len(); | ||||
| 	test_subscr_sqn(); | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| 	db_close(dbc); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* stubs */ | ||||
| void *lu_op_alloc_conn(void *conn) | ||||
| { OSMO_ASSERT(false); return NULL; } | ||||
| void lu_op_tx_del_subscr_data(void *luop) | ||||
| { OSMO_ASSERT(false); } | ||||
| void lu_op_free(void *luop) | ||||
| { OSMO_ASSERT(false); } | ||||
							
								
								
									
										1641
									
								
								tests/db/db_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1641
									
								
								tests/db/db_test.err
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								tests/db/db_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/db/db_test.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| db_test.c | ||||
| Done | ||||
							
								
								
									
										42
									
								
								tests/gsup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/gsup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/src \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOABIS_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	gsup_test.ok \ | ||||
| 	gsup_test.err \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	gsup_test \ | ||||
| 	$(NULL) | ||||
|  | ||||
| gsup_test_SOURCES = \ | ||||
| 	gsup_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| gsup_test_LDADD = \ | ||||
| 	$(top_srcdir)/src/luop.c \ | ||||
| 	$(top_srcdir)/src/gsup_server.c \ | ||||
| 	$(top_srcdir)/src/gsup_router.c \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOABIS_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| .PHONY: update_exp | ||||
| update_exp: | ||||
| 	$(builddir)/gsup_test >"$(srcdir)/gsup_test.ok" 2>"$(srcdir)/gsup_test.err" | ||||
							
								
								
									
										91
									
								
								tests/gsup/gsup_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								tests/gsup/gsup_test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| /* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "luop.h" | ||||
|  | ||||
| struct osmo_gsup_server; | ||||
|  | ||||
| /* override osmo_gsup_addr_send() to not actually send anything. */ | ||||
| int osmo_gsup_addr_send(struct osmo_gsup_server *gs, | ||||
| 			const uint8_t *addr, size_t addrlen, | ||||
| 			struct msgb *msg) | ||||
| { | ||||
| 	LOGP(DMAIN, LOGL_DEBUG, "%s\n", msgb_hexdump(msg)); | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi, | ||||
| 			  struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Verify that the internally allocated msgb is large enough */ | ||||
| void test_gsup_tx_insert_subscr_data() | ||||
| { | ||||
| 	struct lu_operation luop = { | ||||
| 		.state = LU_S_LU_RECEIVED, | ||||
| 		.subscr = { | ||||
| 			.imsi = "123456789012345", | ||||
| 			.msisdn = "987654321098765", | ||||
| 			.nam_cs = true, | ||||
| 			.nam_ps = true, | ||||
| 		}, | ||||
| 		.is_ps = true, | ||||
| 	}; | ||||
|  | ||||
| 	lu_op_tx_insert_subscr_data(&luop); | ||||
| } | ||||
|  | ||||
| const struct log_info_cat default_categories[] = { | ||||
| 	[DMAIN] = { | ||||
| 		.name = "DMAIN", | ||||
| 		.description = "Main Program", | ||||
| 		.enabled = 1, .loglevel = LOGL_DEBUG, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static struct log_info info = { | ||||
| 	.cat = default_categories, | ||||
| 	.num_cat = ARRAY_SIZE(default_categories), | ||||
| }; | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "gsup_test"); | ||||
| 	osmo_init_logging2(ctx, &info); | ||||
| 	log_set_print_filename(osmo_stderr_target, 0); | ||||
| 	log_set_print_timestamp(osmo_stderr_target, 0); | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_category(osmo_stderr_target, 1); | ||||
|  | ||||
| 	test_gsup_tx_insert_subscr_data(); | ||||
|  | ||||
| 	printf("Done.\n"); | ||||
| 	return EXIT_SUCCESS; | ||||
| } | ||||
							
								
								
									
										2
									
								
								tests/gsup/gsup_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/gsup/gsup_test.err
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| DMAIN 10 01 08 21 43 65 87 09 21 43 f5 08 09 08 89 67 45 23 01 89 67 f5 05 07 10 01 01 12 02 01 2a 28 01 01  | ||||
| DMAIN LU OP state change: LU RECEIVED -> ISD SENT | ||||
							
								
								
									
										1
									
								
								tests/gsup/gsup_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/gsup/gsup_test.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Done. | ||||
							
								
								
									
										41
									
								
								tests/gsup_server/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/gsup_server/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/src \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOABIS_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	gsup_server_test.ok \ | ||||
| 	gsup_server_test.err \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	gsup_server_test \ | ||||
| 	$(NULL) | ||||
|  | ||||
| gsup_server_test_SOURCES = \ | ||||
| 	gsup_server_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| gsup_server_test_LDADD = \ | ||||
| 	$(top_srcdir)/src/gsup_server.c \ | ||||
| 	$(top_srcdir)/src/gsup_router.c \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOABIS_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| .PHONY: update_exp | ||||
| update_exp: | ||||
| 	$(builddir)/gsup_server_test >"$(srcdir)/gsup_server_test.ok" 2>"$(srcdir)/gsup_server_test.err" | ||||
							
								
								
									
										145
									
								
								tests/gsup_server/gsup_server_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								tests/gsup_server/gsup_server_test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| /* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| #define comment_start() printf("\n===== %s\n", __func__) | ||||
| #define comment_end() printf("===== %s: SUCCESS\n\n", __func__) | ||||
| #define btw(fmt, args...) printf("\n" fmt "\n", ## args) | ||||
|  | ||||
| #define VERBOSE_ASSERT(val, expect_op, fmt) \ | ||||
| 	do { \ | ||||
| 		printf(#val " == " fmt "\n", (val)); \ | ||||
| 		OSMO_ASSERT((val) expect_op); \ | ||||
| 	} while (0) | ||||
|  | ||||
| void osmo_gsup_server_add_conn(struct llist_head *clients, | ||||
| 			       struct osmo_gsup_conn *conn); | ||||
|  | ||||
| static void test_add_conn(void) | ||||
| { | ||||
| 	struct llist_head _list; | ||||
| 	struct llist_head *clients = &_list; | ||||
| 	struct osmo_gsup_conn conn_inst[23] = {}; | ||||
| 	struct osmo_gsup_conn *conn; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	comment_start(); | ||||
|  | ||||
| 	INIT_LLIST_HEAD(clients); | ||||
|  | ||||
| 	btw("Add 10 items"); | ||||
| 	for (i = 0; i < 10; i++) { | ||||
| 		osmo_gsup_server_add_conn(clients, &conn_inst[i]); | ||||
| 		printf("conn_inst[%u].auc_3g_ind == %u\n", i, conn_inst[i].auc_3g_ind); | ||||
| 		OSMO_ASSERT(clients->next == &conn_inst[0].list); | ||||
| 	} | ||||
|  | ||||
| 	btw("Expecting a list of 0..9"); | ||||
| 	i = 0; | ||||
| 	llist_for_each_entry(conn, clients, list) { | ||||
| 		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind); | ||||
| 		OSMO_ASSERT(conn->auc_3g_ind == i); | ||||
| 		OSMO_ASSERT(conn == &conn_inst[i]); | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	btw("Punch two holes in the sequence in arbitrary order," | ||||
| 	    " a larger one from 2..4 and a single one at 7."); | ||||
| 	llist_del(&conn_inst[4].list); | ||||
| 	llist_del(&conn_inst[2].list); | ||||
| 	llist_del(&conn_inst[3].list); | ||||
| 	llist_del(&conn_inst[7].list); | ||||
|  | ||||
| 	btw("Expecting a list of 0,1, 5,6, 8,9"); | ||||
| 	i = 0; | ||||
| 	llist_for_each_entry(conn, clients, list) { | ||||
| 		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind); | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	btw("Add conns, expecting them to take the open slots"); | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[12]); | ||||
| 	VERBOSE_ASSERT(conn_inst[12].auc_3g_ind, == 2, "%u"); | ||||
|  | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[13]); | ||||
| 	VERBOSE_ASSERT(conn_inst[13].auc_3g_ind, == 3, "%u"); | ||||
|  | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[14]); | ||||
| 	VERBOSE_ASSERT(conn_inst[14].auc_3g_ind, == 4, "%u"); | ||||
|  | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[17]); | ||||
| 	VERBOSE_ASSERT(conn_inst[17].auc_3g_ind, == 7, "%u"); | ||||
|  | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[18]); | ||||
| 	VERBOSE_ASSERT(conn_inst[18].auc_3g_ind, == 10, "%u"); | ||||
|  | ||||
| 	btw("Expecting a list of 0..10"); | ||||
| 	i = 0; | ||||
| 	llist_for_each_entry(conn, clients, list) { | ||||
| 		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind); | ||||
| 		OSMO_ASSERT(conn->auc_3g_ind == i); | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	btw("Does it also work for the first item?"); | ||||
| 	llist_del(&conn_inst[0].list); | ||||
|  | ||||
| 	btw("Expecting a list of 1..10"); | ||||
| 	i = 0; | ||||
| 	llist_for_each_entry(conn, clients, list) { | ||||
| 		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind); | ||||
| 		OSMO_ASSERT(conn->auc_3g_ind == i + 1); | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	btw("Add another conn, should take auc_3g_ind == 0"); | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[20]); | ||||
| 	VERBOSE_ASSERT(conn_inst[20].auc_3g_ind, == 0, "%u"); | ||||
|  | ||||
| 	btw("Expecting a list of 0..10"); | ||||
| 	i = 0; | ||||
| 	llist_for_each_entry(conn, clients, list) { | ||||
| 		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind); | ||||
| 		OSMO_ASSERT(conn->auc_3g_ind == i); | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	btw("If a client reconnects, it will (likely) get the same auc_3g_ind"); | ||||
| 	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u"); | ||||
| 	llist_del(&conn_inst[5].list); | ||||
| 	conn_inst[5].auc_3g_ind = 423; | ||||
| 	osmo_gsup_server_add_conn(clients, &conn_inst[5]); | ||||
| 	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u"); | ||||
|  | ||||
| 	comment_end(); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	printf("test_gsup_server.c\n"); | ||||
|  | ||||
| 	test_add_conn(); | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| 	return 0; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user