mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-10-31 12:13:46 +00:00 
			
		
		
		
	Compare commits
	
		
			42 Commits
		
	
	
		
			0.2.0
			...
			fixeria/sq
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8cdecdd54f | ||
|  | 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 | 
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,6 @@ | ||||
| *.o | ||||
| *.lo | ||||
| *.la | ||||
| *.db | ||||
| *.pyc | ||||
| .*.sw? | ||||
| @@ -22,15 +24,24 @@ 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/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 | ||||
|   | ||||
| @@ -3,6 +3,7 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 | ||||
| SUBDIRS = \ | ||||
| 	doc \ | ||||
| 	src \ | ||||
| 	include \ | ||||
| 	sql \ | ||||
| 	tests \ | ||||
| 	$(NULL) | ||||
| @@ -11,6 +12,9 @@ EXTRA_DIST = \ | ||||
| 	.version \ | ||||
| 	$(NULL) | ||||
|  | ||||
| pkgconfigdir = $(libdir)/pkgconfig | ||||
| pkgconfig_DATA = libosmo-gsup-client.pc | ||||
|  | ||||
| @RELMAKE@ | ||||
|  | ||||
| BUILT_SOURCES = $(top_srcdir)/.version | ||||
|   | ||||
| @@ -99,10 +99,14 @@ AC_OUTPUT( | ||||
| 	Makefile | ||||
| 	doc/Makefile | ||||
| 	src/Makefile | ||||
| 	src/gsupclient/Makefile | ||||
| 	include/Makefile | ||||
| 	libosmo-gsup-client.pc | ||||
| 	sql/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 | ||||
| 	) | ||||
|   | ||||
							
								
								
									
										9
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,12 @@ | ||||
| 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 ] | ||||
|   | ||||
							
								
								
									
										5
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/osmo-hlr.install
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,6 @@ | ||||
| /usr/bin/osmo-hlr | ||||
| /usr/bin/osmo-hlr-db-tool | ||||
| /usr/share/doc/osmo-hlr/hlr.sql | ||||
| /usr/share/doc/osmo-hlr/sql/hlr.sql | ||||
| /usr/share/doc/osmo-hlr/sql/hlr_data.sql | ||||
| /usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg | ||||
| /usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg /etc/osmocom/ | ||||
| /var/lib/osmocom | ||||
|   | ||||
| @@ -7,7 +7,11 @@ log stderr | ||||
|   logging print category 1 | ||||
|   logging timestamp 1 | ||||
|   logging print extended-timestamp 1 | ||||
|   logging level all debug | ||||
|   logging level all notice | ||||
|   logging level main notice | ||||
|   logging level db notice | ||||
|   logging level auc notice | ||||
|   logging level ss info | ||||
|   logging level linp error | ||||
| ! | ||||
| line vty | ||||
| @@ -17,3 +21,4 @@ ctrl | ||||
| hlr | ||||
|  gsup | ||||
|   bind ip 127.0.0.1 | ||||
|  ussd route prefix *#100# internal own-msisdn | ||||
|   | ||||
							
								
								
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| nobase_include_HEADERS = osmocom/gsupclient/gsup_client.h | ||||
|  | ||||
							
								
								
									
										65
									
								
								include/osmocom/gsupclient/gsup_client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								include/osmocom/gsupclient/gsup_client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /* 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> | ||||
|  | ||||
| /* 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; | ||||
|  | ||||
| 	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 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); | ||||
| 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 and OAP Client Library | ||||
| Description: C Utility Library | ||||
| Version: @VERSION@ | ||||
| Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore | ||||
| Cflags: -I${includedir}/ | ||||
|  | ||||
| @@ -3,5 +3,12 @@ EXTRA_DIST = \ | ||||
| 	hlr.sql \ | ||||
| 	$(NULL) | ||||
|  | ||||
| docsdir = $(datadir)/doc/osmo-hlr | ||||
| docs_DATA = $(srcdir)/hlr.sql | ||||
| 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 | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| SUBDIRS = gsupclient | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| @@ -8,6 +10,9 @@ AM_CFLAGS = \ | ||||
| 	$(SQLITE3_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CPPFLAGS = -I$(top_srcdir)/include \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	populate_hlr_db.pl \ | ||||
| 	db_bootstrap.sed \ | ||||
| @@ -30,12 +35,14 @@ noinst_HEADERS = \ | ||||
| 	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 = \ | ||||
| @@ -52,6 +59,8 @@ osmo_hlr_SOURCES = \ | ||||
| 	rand_urandom.c \ | ||||
| 	hlr_vty.c \ | ||||
| 	hlr_vty_subscr.c \ | ||||
| 	gsup_send.c \ | ||||
| 	hlr_ussd.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_hlr_LDADD = \ | ||||
| @@ -93,6 +102,16 @@ db_test_LDADD = \ | ||||
| 	$(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) | ||||
|  | ||||
| BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql | ||||
|  | ||||
| db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_bootstrap.sed | ||||
|   | ||||
							
								
								
									
										138
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								src/db.c
									
									
									
									
									
								
							| @@ -18,10 +18,12 @@ | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <sqlite3.h> | ||||
| #include <string.h> | ||||
| #include <malloc.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "db.h" | ||||
| @@ -72,6 +74,109 @@ static const char *stmt_sql[] = { | ||||
| 	[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id", | ||||
| }; | ||||
|  | ||||
| /* Dedicated talloc context for SQLite */ | ||||
| static void *db_sqlite_ctx = NULL; | ||||
| /* TEMP: used for memory footprint debugging */ | ||||
| static int ctr = 0; | ||||
|  | ||||
| /* talloc or malloc? */ | ||||
| // #define ENABLE_TALLOC | ||||
|  | ||||
| static void *tall_xMalloc(int size) | ||||
| { | ||||
| #ifdef ENABLE_TALLOC | ||||
| 	void *p = talloc_size(db_sqlite_ctx, size); | ||||
| 	int s = talloc_total_size(p); | ||||
| #else | ||||
| 	void *p = malloc(size); | ||||
| 	int s = malloc_usable_size(p); | ||||
| #endif | ||||
| 	/* TEMP: used for memory footprint debugging */ | ||||
| 	printf("[%d] %s size_req=%d vs size_act=%d ptr=%c\n", | ||||
| 		ctr++, __func__, size, s, p != NULL ? '+' : '!'); | ||||
| 	return p; | ||||
| } | ||||
|  | ||||
| static void tall_xFree(void *ptr) | ||||
| { | ||||
| #ifdef ENABLE_TALLOC | ||||
| 	int s = talloc_total_size(ptr); | ||||
| 	talloc_free(ptr); | ||||
| #else | ||||
| 	int s = malloc_usable_size(ptr); | ||||
| 	free(ptr); | ||||
| #endif | ||||
| 	/* TEMP: used for memory footprint debugging */ | ||||
| 	printf("[%d] %s size=%d\n", ctr++, __func__, s); | ||||
| } | ||||
|  | ||||
| static void *tall_xRealloc(void *ptr, int size) | ||||
| { | ||||
| #ifdef ENABLE_TALLOC | ||||
| 	void *p = talloc_realloc_fn(db_sqlite_ctx, ptr, size); | ||||
| 	int s = talloc_total_size(p); | ||||
| #else | ||||
| 	void *p = realloc(ptr, size); | ||||
| 	int s = malloc_usable_size(p); | ||||
| #endif | ||||
| 	/* TEMP: used for memory footprint debugging */ | ||||
| 	printf("[%d] %s size_req=%d vs size_act=%d ptr=%c\n", | ||||
| 		ctr++, __func__, size, s, p != NULL ? '+' : '!'); | ||||
| 	return p; | ||||
| } | ||||
|  | ||||
| static int tall_xSize(void *ptr) | ||||
| { | ||||
| #ifdef ENABLE_TALLOC | ||||
| 	int s = talloc_total_size(ptr); | ||||
| #else | ||||
| 	int s = malloc_usable_size(ptr); | ||||
| #endif | ||||
| 	/* TEMP: used for memory footprint debugging */ | ||||
| 	printf("[%d] %s size=%d\n", ctr++, __func__, s); | ||||
| 	return s; | ||||
| } | ||||
|  | ||||
| static int tall_xRoundup(int size) | ||||
| { | ||||
| 	/** | ||||
| 	 * DUMMY: return size 'as-is'... | ||||
| 	 * AFAIK talloc doesn't round up the allocation size. | ||||
| 	 */ | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| static int tall_xInit(void *data) | ||||
| { | ||||
| 	/* DUMMY: nothing to initialize */ | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void tall_xShutdown(void *data) | ||||
| { | ||||
| 	/* DUMMY: nothing to deinitialize */ | ||||
| } | ||||
|  | ||||
| /* 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, | ||||
| }; | ||||
|  | ||||
| static void sql3_error_log_cb(void *arg, int err_code, const char *msg) | ||||
| { | ||||
| 	LOGP(DDB, LOGL_ERROR, "(%d) %s\n", err_code, msg); | ||||
| @@ -173,12 +278,20 @@ bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr) | ||||
| 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); | ||||
| } | ||||
|  | ||||
| @@ -194,21 +307,22 @@ static int db_bootstrap(struct db_context *dbc) | ||||
| 		if (rc != SQLITE_OK) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", | ||||
| 			     stmt_bootstrap_sql[i]); | ||||
| 			return -1; | ||||
| 			return rc; | ||||
| 		} | ||||
|  | ||||
| 		/* execute the statement */ | ||||
| 		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 -1; | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| 	return SQLITE_OK; | ||||
| } | ||||
|  | ||||
| struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging) | ||||
| @@ -222,6 +336,15 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg | ||||
| 	LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION); | ||||
| 	LOGP(DDB, LOGL_INFO, "Running with SQLite3 lib version %s\n", sqlite3_libversion()); | ||||
|  | ||||
| 	db_sqlite_ctx = talloc_named_const(dbc, 0, "SQLite3"); | ||||
|  | ||||
| 	/* Configure SQLite3 to use talloc memory allocator */ | ||||
| 	rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &tall_sqlite_if); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to configure SQLite3 " | ||||
| 			"to use talloc, using default memory allocator\n"); | ||||
| 	} | ||||
|  | ||||
| 	dbc->fname = talloc_strdup(dbc, fname); | ||||
|  | ||||
| 	for (i = 0; i < 0xfffff; i++) { | ||||
| @@ -265,7 +388,12 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n", | ||||
| 			err_msg); | ||||
|  | ||||
| 	db_bootstrap(dbc); | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	/* prepare all SQL statements */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) { | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
|  | ||||
| #include "logging.h" | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| struct gsup_route { | ||||
| @@ -60,6 +61,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\n", addr); | ||||
|  | ||||
| 	gr->addr = talloc_memdup(gr, addr, addrlen); | ||||
| 	gr->conn = conn; | ||||
| 	llist_add_tail(&gr->list, &conn->server->routes); | ||||
| @@ -75,6 +78,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,3 +1,8 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs, | ||||
| 					const uint8_t *addr, size_t addrlen); | ||||
|  | ||||
| @@ -6,3 +11,7 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr | ||||
|  | ||||
| /* 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); | ||||
|   | ||||
							
								
								
									
										45
									
								
								src/gsup_send.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/gsup_send.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* (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 */ | ||||
| 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", addr); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
|  | ||||
| 	return osmo_gsup_conn_send(conn, msg); | ||||
| } | ||||
|  | ||||
| @@ -24,6 +24,7 @@ | ||||
| #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" | ||||
| @@ -291,7 +292,7 @@ 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, | ||||
| 			struct llist_head *lu_op_lst) | ||||
| 			struct llist_head *lu_op_lst, void *priv) | ||||
| { | ||||
| 	struct osmo_gsup_server *gsups; | ||||
| 	int rc; | ||||
| @@ -311,6 +312,7 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port, | ||||
| 		goto failed; | ||||
|  | ||||
| 	gsups->read_cb = read_cb; | ||||
| 	gsups->priv = priv; | ||||
|  | ||||
| 	rc = ipa_server_link_open(gsups->link); | ||||
| 	if (rc < 0) | ||||
| @@ -335,19 +337,78 @@ void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups) | ||||
| 	talloc_free(gsups); | ||||
| } | ||||
|  | ||||
| void osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup) | ||||
| /* 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; | ||||
| 	uint8_t apn[APN_MAXLEN]; | ||||
|  | ||||
| 	l = osmo_apn_from_str(apn, sizeof(apn), "*"); | ||||
| 	l = osmo_apn_from_str(apn_buf, apn_buf_size, "*"); | ||||
| 	if (l <= 0) | ||||
| 		return; | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	gsup->pdp_infos[0].apn_enc = apn; | ||||
| 	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; | ||||
| } | ||||
|   | ||||
| @@ -6,12 +6,19 @@ | ||||
| #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; | ||||
|  | ||||
| /* Expects message in msg->l2h */ | ||||
| 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; | ||||
|  | ||||
| @@ -49,8 +56,14 @@ 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, | ||||
| 						 struct llist_head *lu_op_lst); | ||||
| 						 struct llist_head *lu_op_lst, | ||||
| 						 void *priv); | ||||
|  | ||||
| void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups); | ||||
|  | ||||
| void osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup); | ||||
| 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 | ||||
							
								
								
									
										345
									
								
								src/gsupclient/gsup_client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								src/gsupclient/gsup_client.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,345 @@ | ||||
| /* 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 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/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; | ||||
| 	struct ipaccess_unit ipa_dev = { | ||||
| 		/* see gsup_client_create() on const vs non-const */ | ||||
| 		.unit_name = (char*)gsupc->unit_name, | ||||
| 	}; | ||||
|  | ||||
| 	OSMO_ASSERT(ipa_dev.unit_name); | ||||
|  | ||||
| 	msg->l2h = &hh->data[0]; | ||||
|  | ||||
| 	rc = ipaccess_bts_handle_ccm(link, &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); | ||||
| } | ||||
|  | ||||
| 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 osmo_gsup_client *gsupc; | ||||
| 	int rc; | ||||
|  | ||||
| 	gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client); | ||||
| 	OSMO_ASSERT(gsupc); | ||||
|  | ||||
| 	/* struct ipaccess_unit has a non-const unit_name, so let's copy to be | ||||
| 	 * able to have a non-const unit_name here as well. To not taint the | ||||
| 	 * public gsup_client API, let's store it in a const char* anyway. */ | ||||
| 	gsupc->unit_name = talloc_strdup(gsupc, unit_name); | ||||
| 	OSMO_ASSERT(gsupc->unit_name); | ||||
|  | ||||
| 	/* 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; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										153
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								src/hlr.c
									
									
									
									
									
								
							| @@ -26,12 +26,12 @@ | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include <osmocom/gsm/gsm48_ie.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| #include <osmocom/vty/ports.h> | ||||
| #include <osmocom/ctrl/control_vty.h> | ||||
| #include <osmocom/gsm/apn.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "hlr.h" | ||||
| @@ -42,68 +42,57 @@ | ||||
| #include "rand.h" | ||||
| #include "luop.h" | ||||
| #include "hlr_vty.h" | ||||
| #include "hlr_ussd.h" | ||||
|  | ||||
| static struct hlr *g_hlr; | ||||
| struct hlr *g_hlr; | ||||
| static int quit = 0; | ||||
|  | ||||
| /* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients. | ||||
|  * | ||||
|  * FIXME: In order to support large-scale networks this function should skip | ||||
|  * VLRs/SGSNs which do not currently serve the subscriber. | ||||
|  * | ||||
|  * \param[in] subscr  A subscriber we have new data to send for. | ||||
|  */ | ||||
| void | ||||
| osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	/* FIXME: the below code can only be re-enabled after we make sure that an ISD | ||||
| 	 * is only sent tot the currently serving VLR and/or SGSN (if there are any). | ||||
| 	 * We cannot blindly flood the entire PLMN with this, as it would create subscriber | ||||
| 	 * state in every VLR/SGSN out there, even those that have never seen the subscriber. | ||||
| 	 * See https://osmocom.org/issues/3154 for details. */ | ||||
| #if 0 | ||||
|         struct osmo_gsup_conn *co; | ||||
|  | ||||
| 	if (g_hlr->gs == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	llist_for_each_entry(co, &g_hlr->gs->clients, list) { | ||||
| 		struct osmo_gsup_message gsup = { | ||||
| 			.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST | ||||
| 		}; | ||||
| 		struct osmo_gsup_message gsup = { }; | ||||
| 		uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN]; | ||||
| 		uint8_t apn[APN_MAXLEN]; | ||||
| 		struct msgb *msg_out; | ||||
| 		uint8_t *peer; | ||||
| 		int peer_len; | ||||
| 		uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */ | ||||
| 		int len; | ||||
| 		struct msgb *msg_out; | ||||
| 		enum osmo_gsup_cn_domain cn_domain; | ||||
|  | ||||
| 		peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR); | ||||
| 		if (peer_len < 0) { | ||||
| 		if (co->supports_ps) | ||||
| 			cn_domain = OSMO_GSUP_CN_DOMAIN_PS; | ||||
| 		else if (co->supports_cs) | ||||
| 			cn_domain = OSMO_GSUP_CN_DOMAIN_CS; | ||||
| 		else { | ||||
| 			/* We have not yet received a location update from this subscriber .*/ | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		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, cannot get peer name " | ||||
| 			       "IMSI='%s': Cannot notify GSUP client; could not create gsup message " | ||||
| 			       "for %s:%u\n", subscr->imsi, | ||||
| 			       co && co->conn && co->conn->server? co->conn->server->addr : "unset", | ||||
| 			       co && co->conn && co->conn->server? co->conn->server->port : 0); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		osmo_strlcpy(gsup.imsi, subscr->imsi, GSM23003_IMSI_MAX_DIGITS + 1); | ||||
|  | ||||
| 		len = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0, subscr->msisdn); | ||||
| 		if (len < 1) { | ||||
| 			LOGP(DMAIN, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", | ||||
| 			     subscr->imsi, subscr->msisdn); | ||||
| 			continue; | ||||
| 		} | ||||
| 		gsup.msisdn_enc = msisdn_enc; | ||||
| 		gsup.msisdn_enc_len = len; | ||||
|  | ||||
| 		if (co->supports_ps) { | ||||
| 			gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_PS; | ||||
|  | ||||
| 			/* FIXME: PDP infos - use more fine-grained access control | ||||
| 			   instead of wildcard APN */ | ||||
| 			osmo_gsup_configure_wildcard_apn(&gsup); | ||||
| 		} else if (co->supports_cs) { | ||||
| 			gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_CS; | ||||
| 		} else { | ||||
| 			/* We have not yet received a location update from this subscriber .*/ | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Send ISD to MSC/SGSN */ | ||||
| 		msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE"); | ||||
| 		if (msg_out == NULL) { | ||||
| @@ -114,8 +103,17 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr) | ||||
| 			       co && co->conn && co->conn->server? co->conn->server->port : 0); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		osmo_gsup_encode(msg_out, &gsup); | ||||
|  | ||||
| 		peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR); | ||||
| 		if (peer_len < 0) { | ||||
| 			LOGP(DMAIN, LOGL_ERROR, | ||||
| 			       "IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi, | ||||
| 			       co && co->conn && co->conn->server? co->conn->server->addr : "unset", | ||||
| 			       co && co->conn && co->conn->server? co->conn->server->port : 0); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (osmo_gsup_addr_send(g_hlr->gs, peer, peer_len, msg_out) < 0) { | ||||
| 			LOGP(DMAIN, LOGL_ERROR, | ||||
| 			       "IMSI='%s': Cannot notify GSUP client; send operation failed " | ||||
| @@ -125,6 +123,7 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr) | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
| @@ -345,6 +344,29 @@ static int rx_purge_ms_req(struct osmo_gsup_conn *conn, | ||||
| 	return osmo_gsup_conn_send(conn, msg_out); | ||||
| } | ||||
|  | ||||
| static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi, | ||||
| 				enum osmo_gsup_message_type type_in, uint8_t err_cause) | ||||
| { | ||||
| 	int type_err = osmo_gsup_get_err_msg_type(type_in); | ||||
| 	struct osmo_gsup_message gsup_reply = {0}; | ||||
| 	struct msgb *msg_out; | ||||
|  | ||||
| 	if (type_err < 0) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "unable to determine error response for %s\n", | ||||
| 			osmo_gsup_message_type_name(type_in)); | ||||
| 		return type_err; | ||||
| 	} | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi); | ||||
| 	gsup_reply.message_type = type_err; | ||||
| 	gsup_reply.cause = err_cause; | ||||
| 	msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ERR response"); | ||||
| 	OSMO_ASSERT(msg_out); | ||||
| 	osmo_gsup_encode(msg_out, &gsup_reply); | ||||
| 	LOGP(DMAIN, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(type_err)); | ||||
| 	return osmo_gsup_conn_send(conn, msg_out); | ||||
| } | ||||
|  | ||||
| static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) | ||||
| { | ||||
| 	static struct osmo_gsup_message gsup; | ||||
| @@ -356,6 +378,11 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	/* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5 | ||||
| 	 * digits is impossible.  Even 5 digits is a highly theoretical case */ | ||||
| 	if (strlen(gsup.imsi) < 5) | ||||
| 		return gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO); | ||||
|  | ||||
| 	switch (gsup.message_type) { | ||||
| 	/* requests sent to us */ | ||||
| 	case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST: | ||||
| @@ -376,6 +403,13 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n", | ||||
| 		     gsup.imsi); | ||||
| 		break; | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_REQUEST: | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_RESULT: | ||||
| 		rx_proc_ss_req(conn, &gsup); | ||||
| 		break; | ||||
| 	case OSMO_GSUP_MSGT_PROC_SS_ERROR: | ||||
| 		rx_proc_ss_error(conn, &gsup); | ||||
| 		break; | ||||
| 	case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: | ||||
| 	case OSMO_GSUP_MSGT_INSERT_DATA_RESULT: | ||||
| 	case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: | ||||
| @@ -498,11 +532,7 @@ static void signal_hdlr(int signal) | ||||
| 	switch (signal) { | ||||
| 	case SIGINT: | ||||
| 		LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n"); | ||||
| 		osmo_gsup_server_destroy(g_hlr->gs); | ||||
| 		db_close(g_hlr->dbc); | ||||
| 		log_fini(); | ||||
| 		talloc_report_full(hlr_ctx, stderr); | ||||
| 		exit(0); | ||||
| 		quit++; | ||||
| 		break; | ||||
| 	case SIGUSR1: | ||||
| 		LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n"); | ||||
| @@ -529,11 +559,18 @@ int main(int argc, char **argv) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Track the use of talloc NULL memory contexts */ | ||||
| 	talloc_enable_null_tracking(); | ||||
|  | ||||
| 	hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR"); | ||||
| 	msgb_talloc_ctx_init(hlr_ctx, 0); | ||||
| 	vty_info.tall_ctx = hlr_ctx; | ||||
|  | ||||
| 	g_hlr = talloc_zero(hlr_ctx, struct hlr); | ||||
| 	INIT_LLIST_HEAD(&g_hlr->euse_list); | ||||
| 	INIT_LLIST_HEAD(&g_hlr->iuse_list); | ||||
| 	INIT_LLIST_HEAD(&g_hlr->ss_sessions); | ||||
| 	INIT_LLIST_HEAD(&g_hlr->ussd_routes); | ||||
|  | ||||
| 	rc = osmo_init_logging2(hlr_ctx, &hlr_log_info); | ||||
| 	if (rc < 0) { | ||||
| @@ -544,7 +581,7 @@ int main(int argc, char **argv) | ||||
| 	vty_init(&vty_info); | ||||
| 	ctrl_vty_init(hlr_ctx); | ||||
| 	handle_options(argc, argv); | ||||
| 	hlr_vty_init(g_hlr, &hlr_log_info); | ||||
| 	hlr_vty_init(&hlr_log_info); | ||||
|  | ||||
| 	rc = vty_read_config_file(cmdline_opts.config_file, NULL); | ||||
| 	if (rc < 0) { | ||||
| @@ -575,7 +612,7 @@ int main(int argc, char **argv) | ||||
| 	} | ||||
|  | ||||
| 	g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT, | ||||
| 					    read_cb, &g_lu_ops); | ||||
| 					    read_cb, &g_lu_ops, g_hlr); | ||||
| 	if (!g_hlr->gs) { | ||||
| 		LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n"); | ||||
| 		exit(1); | ||||
| @@ -596,13 +633,29 @@ int main(int argc, char **argv) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	while (1) { | ||||
| 	while (!quit) | ||||
| 		osmo_select_main(0); | ||||
| 	} | ||||
|  | ||||
| 	osmo_gsup_server_destroy(g_hlr->gs); | ||||
| 	db_close(g_hlr->dbc); | ||||
|  | ||||
| 	log_fini(); | ||||
|  | ||||
| 	exit(0); | ||||
| 	/** | ||||
| 	 * Report the heap state of root context, then free, | ||||
| 	 * so both ASAN and Valgrind are happy... | ||||
| 	 */ | ||||
| 	talloc_report_full(hlr_ctx, stderr); | ||||
| 	talloc_free(hlr_ctx); | ||||
|  | ||||
| 	/* FIXME: VTY code still uses NULL-context */ | ||||
| 	talloc_free(tall_vty_ctx); | ||||
|  | ||||
| 	/** | ||||
| 	 * Report the heap state of NULL context, then free, | ||||
| 	 * so both ASAN and Valgrind are happy... | ||||
| 	 */ | ||||
| 	talloc_report_full(NULL, stderr); | ||||
| 	talloc_disable_null_tracking(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/hlr.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/hlr.h
									
									
									
									
									
								
							| @@ -23,6 +23,9 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <osmocom/core/linuxlist.h> | ||||
|  | ||||
| struct hlr_euse; | ||||
|  | ||||
| struct hlr { | ||||
| 	/* GSUP server pointer */ | ||||
| @@ -37,8 +40,18 @@ struct hlr { | ||||
|  | ||||
| 	/* Local bind addr */ | ||||
| 	char *gsup_bind_addr; | ||||
|  | ||||
| 	struct llist_head euse_list; | ||||
| 	struct hlr_euse *euse_default; | ||||
| 	struct llist_head iuse_list; | ||||
|  | ||||
| 	struct llist_head ussd_routes; | ||||
|  | ||||
| 	struct llist_head ss_sessions; | ||||
| }; | ||||
|  | ||||
| extern struct hlr *g_hlr; | ||||
|  | ||||
| struct hlr_subscriber; | ||||
|  | ||||
| void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr); | ||||
|   | ||||
							
								
								
									
										565
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,565 @@ | ||||
| /* 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" | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * 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 EUSE %s (prefix %s) for USSD Code '%s'\n", | ||||
| 				rt->u.euse->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[GSM23003_IMSI_MAX_DIGITS+2]; | ||||
| 	/* 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; | ||||
|  | ||||
| 	/* 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; | ||||
| 	osmo_timer_setup(&ss->timeout, ss_session_timeout, ss); | ||||
| 	/* NOTE: The timeout is currently global and not refreshed with subsequent messages | ||||
| 	 * within the SS/USSD session.  So 30s after the initial SS message, the session will | ||||
| 	 * timeout! */ | ||||
| 	osmo_timer_schedule(&ss->timeout, 30, 0); | ||||
|  | ||||
| 	llist_add_tail(&ss->list, &hlr->ss_sessions); | ||||
| 	return ss; | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * handling functions for encoding SS messages + wrapping them in GSUP | ||||
|  ***********************************************************************/ | ||||
|  | ||||
| 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 = gsm0480_msgb_alloc_name(__func__); | ||||
| 	OSMO_ASSERT(resp_msg); | ||||
| 	osmo_gsup_encode(resp_msg, &resp); | ||||
| 	msgb_free(ss_msg); | ||||
|  | ||||
| 	/* FIXME: resolve this based on the database vlr_addr */ | ||||
| 	return osmo_gsup_addr_send(g_hlr->gs, (uint8_t *)"MSC-00-00-00-00-00-00", 22, 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\r", subscr.msisdn); | ||||
| 		ss_tx_ussd_7bit(ss, true, req->invoke_id, buf); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		ss_tx_error(ss, true, 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!\n", 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 */ | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* 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); | ||||
| 		/* FIXME: resolve this based on the database vlr_addr */ | ||||
| 		osmo_gsup_addr_send(conn->server, (uint8_t *)"MSC-00-00-00-00-00-00", 22, 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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	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}; | ||||
|  | ||||
| 	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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
| 		} | ||||
| 		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; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			/* 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; | ||||
| 		} | ||||
| 		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; | ||||
| 		} | ||||
| 		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; | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| struct osmo_gsup_conn; | ||||
|  | ||||
| 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); | ||||
| }; | ||||
							
								
								
									
										225
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							| @@ -1,9 +1,14 @@ | ||||
| /* 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 | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> | ||||
|  * (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 | ||||
| @@ -25,11 +30,12 @@ | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/abis/ipa.h> | ||||
|  | ||||
| #include "hlr.h" | ||||
| #include "hlr_vty.h" | ||||
| #include "hlr_vty_subscr.h" | ||||
|  | ||||
| static struct hlr *g_hlr = NULL; | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| struct cmd_node hlr_node = { | ||||
| 	HLR_NODE, | ||||
| @@ -75,6 +81,33 @@ static int config_write_hlr_gsup(struct vty *vty) | ||||
| 	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", | ||||
| @@ -89,12 +122,181 @@ DEFUN(cfg_hlr_gsup_bind_ip, | ||||
| 	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_find(g_hlr, argv[0]); | ||||
|  | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*********************************************************************** | ||||
|  * 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: | ||||
| @@ -122,13 +324,13 @@ int hlr_vty_is_config_node(struct vty *vty, int node) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void hlr_vty_init(struct hlr *hlr, const struct log_info *cat) | ||||
| void hlr_vty_init(const struct log_info *cat) | ||||
| { | ||||
| 	g_hlr = hlr; | ||||
|  | ||||
| 	logging_vty_add_cmds(cat); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
|  | ||||
| 	install_element_ve(&show_gsup_conn_cmd); | ||||
|  | ||||
| 	install_element(CONFIG_NODE, &cfg_hlr_cmd); | ||||
| 	install_node(&hlr_node, config_write_hlr); | ||||
|  | ||||
| @@ -137,5 +339,14 @@ void hlr_vty_init(struct hlr *hlr, const struct log_info *cat) | ||||
|  | ||||
| 	install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd); | ||||
|  | ||||
| 	hlr_vty_subscriber_init(hlr); | ||||
| 	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); | ||||
|  | ||||
| 	hlr_vty_subscriber_init(); | ||||
| } | ||||
|   | ||||
| @@ -30,8 +30,9 @@ | ||||
| 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(struct hlr *hlr, const struct log_info *cat); | ||||
| void hlr_vty_init(const struct log_info *cat); | ||||
|   | ||||
| @@ -33,8 +33,6 @@ struct vty; | ||||
|  | ||||
| #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) | ||||
|  | ||||
| static struct hlr *g_hlr = NULL; | ||||
|  | ||||
| static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	int rc; | ||||
| @@ -165,8 +163,9 @@ DEFUN(subscriber_create, | ||||
|       subscriber_create_cmd, | ||||
|       SUBSCR_CMD "imsi IDENT create", | ||||
|       SUBSCR_CMD_HELP | ||||
|       "Create subscriber by IMSI\n" | ||||
|       "IMSI/MSISDN/ID of the subscriber\n") | ||||
|       "Identify subscriber by IMSI\n" | ||||
|       "IMSI/MSISDN/ID of the subscriber\n" | ||||
|       "Create subscriber by IMSI\n") | ||||
| { | ||||
| 	int rc; | ||||
| 	struct hlr_subscriber subscr; | ||||
| @@ -476,10 +475,8 @@ DEFUN(subscriber_aud3g, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| void hlr_vty_subscriber_init(struct hlr *hlr) | ||||
| void hlr_vty_subscriber_init(void) | ||||
| { | ||||
| 	g_hlr = hlr; | ||||
|  | ||||
| 	install_element_ve(&subscriber_show_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_create_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_delete_cmd); | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| #pragma once | ||||
|  | ||||
| void hlr_vty_subscriber_init(struct hlr *hlr); | ||||
| void hlr_vty_subscriber_init(void); | ||||
|   | ||||
| @@ -19,6 +19,13 @@ const struct log_info_cat hlr_log_info_cat[] = { | ||||
| 		.color = "\033[1;33m", | ||||
| 		.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; | ||||
|   | ||||
							
								
								
									
										53
									
								
								src/luop.c
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								src/luop.c
									
									
									
									
									
								
							| @@ -25,8 +25,8 @@ | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/gsm/gsm48_ie.h> | ||||
| #include <osmocom/gsm/gsup.h> | ||||
| #include <osmocom/gsm/apn.h> | ||||
|  | ||||
| #include "gsup_server.h" | ||||
| #include "gsup_router.h" | ||||
| @@ -51,6 +51,7 @@ static void _luop_tx_gsup(struct lu_operation *luop, | ||||
| 	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, | ||||
| @@ -164,23 +165,6 @@ void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state) | ||||
| 	luop->state = new_state; | ||||
| } | ||||
|  | ||||
| /* Send a msgb to a given address using routing */ | ||||
| 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(DMAIN, "Cannot find route for addr %s\n", addr); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
|  | ||||
| 	return osmo_gsup_conn_send(conn, msg); | ||||
| } | ||||
|  | ||||
| /*! Transmit UPD_LOC_ERROR and destroy lu_operation */ | ||||
| void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause) | ||||
| { | ||||
| @@ -231,34 +215,27 @@ void lu_op_tx_cancel_old(struct lu_operation *luop) | ||||
| /*! Transmit Insert Subscriber Data to new VLR/SGSN */ | ||||
| void lu_op_tx_insert_subscr_data(struct lu_operation *luop) | ||||
| { | ||||
| 	struct osmo_gsup_message gsup; | ||||
| 	uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */ | ||||
| 	int l; | ||||
| 	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); | ||||
|  | ||||
| 	fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_INSERT_DATA_REQUEST); | ||||
| 	if (luop->is_ps) | ||||
| 		cn_domain = OSMO_GSUP_CN_DOMAIN_PS; | ||||
| 	else | ||||
| 		cn_domain = OSMO_GSUP_CN_DOMAIN_CS; | ||||
|  | ||||
| 	l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0, | ||||
| 				    luop->subscr.msisdn); | ||||
| 	if (l < 1) { | ||||
| 	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, | ||||
| 		     "%s: Error: cannot encode MSISDN '%s'\n", | ||||
| 		     luop->subscr.imsi, luop->subscr.msisdn); | ||||
| 		lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC); | ||||
| 		       "IMSI='%s': Cannot notify GSUP client; could not create gsup message " | ||||
| 		       "for %s\n", subscr->imsi, luop->peer); | ||||
| 		return; | ||||
| 	} | ||||
| 	gsup.msisdn_enc = msisdn_enc; | ||||
| 	gsup.msisdn_enc_len = l; | ||||
|  | ||||
| 	#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc" | ||||
|  | ||||
| 	if (luop->is_ps) { | ||||
| 		/* FIXME: PDP infos - use more fine-grained access control | ||||
| 		   instead of wildcard APN */ | ||||
| 		osmo_gsup_configure_wildcard_apn(&gsup); | ||||
| 	} | ||||
|  | ||||
| 	/* Send ISD to new VLR/SGSN */ | ||||
| 	_luop_tx_gsup(luop, &gsup); | ||||
|   | ||||
| @@ -28,6 +28,7 @@ | ||||
| #include <osmocom/gsm/gsup.h> | ||||
|  | ||||
| #include "db.h" | ||||
| #include "gsup_server.h" | ||||
|  | ||||
| #define CANCEL_TIMEOUT_SECS	30 | ||||
| #define ISD_TIMEOUT_SECS	30 | ||||
| @@ -62,9 +63,6 @@ struct lu_operation { | ||||
| 	uint8_t *peer; | ||||
| }; | ||||
|  | ||||
| int osmo_gsup_addr_send(struct osmo_gsup_server *gs, | ||||
| 			const uint8_t *addr, size_t addrlen, | ||||
| 			struct msgb *msg); | ||||
|  | ||||
| struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv); | ||||
| struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn); | ||||
|   | ||||
							
								
								
									
										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); | ||||
| } | ||||
|  | ||||
| @@ -2,6 +2,7 @@ SUBDIRS = \ | ||||
| 	auc \ | ||||
| 	gsup_server \ | ||||
| 	db \ | ||||
| 	gsup \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # The `:;' works around a Bash 3.2 bug when the output is not writeable. | ||||
| @@ -56,6 +57,7 @@ vty-test: | ||||
| 		-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 | ||||
|  | ||||
| @@ -71,6 +73,7 @@ ctrl-test: | ||||
| 		-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)-* | ||||
|  | ||||
| else | ||||
| python-tests: | ||||
|   | ||||
							
								
								
									
										41
									
								
								tests/gsup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/gsup/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 = \ | ||||
| 	$(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. | ||||
| @@ -16,6 +16,7 @@ OsmoHLR> list | ||||
|   show talloc-context (application|all) (full|brief|DEPTH) | ||||
|   show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS | ||||
|   show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP | ||||
|   show gsup-connections | ||||
|   subscriber (imsi|msisdn|id) IDENT show | ||||
|  | ||||
| OsmoHLR> enable | ||||
| @@ -69,6 +70,13 @@ OsmoHLR(config-hlr)# list | ||||
|   exit | ||||
|   end | ||||
|   gsup | ||||
|   euse NAME | ||||
|   no euse NAME | ||||
|   ussd route prefix PREFIX internal (own-msisdn|own-imsi) | ||||
|   ussd route prefix PREFIX external EUSE | ||||
|   no ussd route prefix PREFIX | ||||
|   ussd default-route external EUSE | ||||
|   no ussd default-route | ||||
|  | ||||
| OsmoHLR(config-hlr)# gsup | ||||
| OsmoHLR(config-hlr-gsup)# list | ||||
| @@ -104,10 +112,11 @@ log stderr | ||||
|   logging print category 1 | ||||
|   logging print extended-timestamp 1 | ||||
|   logging print file 1 | ||||
|   logging level all debug | ||||
|   logging level all notice | ||||
|   logging level main notice | ||||
|   logging level db notice | ||||
|   logging level auc notice | ||||
|   logging level ss info | ||||
| ... | ||||
| ! | ||||
| line vty | ||||
| @@ -118,4 +127,5 @@ ctrl | ||||
| hlr | ||||
|  gsup | ||||
|   bind ip 127.0.0.1 | ||||
|  ussd route prefix *#100# internal own-msisdn | ||||
| end | ||||
|   | ||||
| @@ -15,6 +15,13 @@ cat $abs_srcdir/auc/auc_ts_55_205_test_sets.err > experr | ||||
| AT_CHECK([$abs_top_builddir/tests/auc/auc_ts_55_205_test_sets], [], [expout], [experr]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([gsup]) | ||||
| AT_KEYWORDS([gsup]) | ||||
| cat $abs_srcdir/gsup/gsup_test.ok > expout | ||||
| cat $abs_srcdir/gsup/gsup_test.err > experr | ||||
| AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [], [expout], [experr]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([gsup_server]) | ||||
| AT_KEYWORDS([gsup_server]) | ||||
| cat $abs_srcdir/gsup_server/gsup_server_test.ok > expout | ||||
|   | ||||
		Reference in New Issue
	
	Block a user