mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-10-31 04:03:53 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			osmith/aut
			...
			laforge/cc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a5e9ca9045 | ||
|  | 94c38a9e03 | ||
|  | 05e888ed7c | ||
|  | 9eb10efb23 | ||
|  | 63450d990b | ||
|  | d2d9f35835 | ||
|  | 80b7fe759f | ||
|  | b984a6c995 | ||
|  | 3896888468 | ||
|  | e9dd9c6282 | ||
|  | 277b2d642a | ||
|  | f1949f7b99 | ||
|  | f302de93dd | 
| @@ -5,7 +5,7 @@ Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db | ||||
| ExecStart=/usr/bin/osmo-hlr -U -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
|   | ||||
| @@ -24,3 +24,8 @@ hlr | ||||
|   bind ip 127.0.0.1 | ||||
|  ussd route prefix *#100# internal own-msisdn | ||||
|  ussd route prefix *#101# internal own-imsi | ||||
|  ussd route prefix *#102# internal get-ran | ||||
|  ussd route prefix *#200# internal gsm-off | ||||
|  ussd route prefix *#201# internal gsm-on | ||||
|  ussd route prefix *#300# internal umts-off | ||||
|  ussd route prefix *#301# internal umts-on | ||||
|   | ||||
							
								
								
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							| @@ -42,7 +42,12 @@ CREATE TABLE subscriber ( | ||||
|  | ||||
| 	-- Timestamp of last location update seen from subscriber | ||||
| 	-- The value is a string which encodes a UTC timestamp in granularity of seconds. | ||||
| 	last_lu_seen TIMESTAMP default NULL | ||||
| 	last_lu_seen TIMESTAMP default NULL, | ||||
|  | ||||
| 	-- Last Radio Access Type list as sent during Location Updating Request. | ||||
| 	-- This is usually just one RAT name, but can be a comma separated list of strings | ||||
| 	-- of all the RAT types sent during Location Updating Request. | ||||
| 	last_lu_rat	TEXT default NULL | ||||
| ); | ||||
|  | ||||
| CREATE TABLE subscriber_apn ( | ||||
| @@ -72,8 +77,17 @@ CREATE TABLE auc_3g ( | ||||
| 	ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end | ||||
| ); | ||||
|  | ||||
| -- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...). | ||||
| -- If a subscriber has no entry, that means that all RATs are allowed (backwards compat). | ||||
| CREATE TABLE subscriber_rat ( | ||||
| 	subscriber_id	INTEGER,		-- subscriber.id | ||||
| 	rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL,	-- Radio Access Technology, see enum ran_type | ||||
| 	allowed		BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0 | ||||
| ); | ||||
|  | ||||
| CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi); | ||||
| CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat); | ||||
|  | ||||
| -- Set HLR database schema version number | ||||
| -- Note: This constant is currently duplicated in src/db.c and must be kept in sync! | ||||
| PRAGMA user_version = 2; | ||||
| PRAGMA user_version = 4; | ||||
|   | ||||
							
								
								
									
										8
									
								
								sql/upgrade_v2_to_v3.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								sql/upgrade_v2_to_v3.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
|  | ||||
| CREATE TABLE subscriber_rat ( | ||||
| 	subscriber_id	INTEGER,		-- subscriber.id | ||||
| 	rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL,	-- Radio Access Technology, see enum ran_type | ||||
| 	allowed		BOOLEAN NOT NULL DEFAULT 0, | ||||
| ); | ||||
|  | ||||
| PRAGMA user_version = 3; | ||||
							
								
								
									
										121
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								src/db.c
									
									
									
									
									
								
							| @@ -28,7 +28,7 @@ | ||||
| #include "db_bootstrap.h" | ||||
|  | ||||
| /* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */ | ||||
| #define CURRENT_SCHEMA_VERSION	2 | ||||
| #define CURRENT_SCHEMA_VERSION	4 | ||||
|  | ||||
| #define SEL_COLUMNS \ | ||||
| 	"id," \ | ||||
| @@ -45,7 +45,8 @@ | ||||
| 	"lmsi," \ | ||||
| 	"ms_purged_cs," \ | ||||
| 	"ms_purged_ps," \ | ||||
| 	"last_lu_seen" | ||||
| 	"last_lu_seen," \ | ||||
| 	"last_lu_rat" | ||||
|  | ||||
| static const char *stmt_sql[] = { | ||||
| 	[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?", | ||||
| @@ -78,9 +79,16 @@ static const char *stmt_sql[] = { | ||||
| 		"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)" | ||||
| 		" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)", | ||||
| 	[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id", | ||||
| 	[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id", | ||||
| 	[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'), last_lu_rat = $rat" | ||||
| 				     " WHERE id = $subscriber_id", | ||||
| 	[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi", | ||||
| 	[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn", | ||||
| 	[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)" | ||||
| 		" VALUES ($subscriber_id, $rat, $allowed)", | ||||
| 	[DB_STMT_RAT_BY_ID] = | ||||
| 		"SELECT rat, allowed" | ||||
| 		" FROM subscriber_rat" | ||||
| 		" WHERE subscriber_id = $subscriber_id", | ||||
| }; | ||||
|  | ||||
| static void sql3_error_log_cb(void *arg, int err_code, const char *msg) | ||||
| @@ -329,6 +337,85 @@ static int db_upgrade_v2(struct db_context *dbc) | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int db_upgrade_v3(struct db_context *dbc) | ||||
| { | ||||
| 	int i; | ||||
| 	const char *stmts[] = { | ||||
| 		"CREATE TABLE subscriber_rat", | ||||
| 		"CREATE UNIQUE INDEX idx_subscr_rat_flag", | ||||
| 		NULL | ||||
| 	}; | ||||
| 	sqlite3_stmt *stmt = NULL; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(stmts); i++) { | ||||
| 		int rc; | ||||
| 		int j; | ||||
| 		const char *stmt_sql = NULL; | ||||
|  | ||||
| 		if (stmts[i] != NULL) { | ||||
| 			for (j = 0; j < ARRAY_SIZE(stmt_bootstrap_sql); j++) { | ||||
| 				if (strstr(stmt_bootstrap_sql[j], stmts[i])) { | ||||
| 					/* make sure we have a unique match, hence also not break; here */ | ||||
| 					OSMO_ASSERT(!stmt_sql); | ||||
| 					stmt_sql = stmt_bootstrap_sql[j]; | ||||
| 				} | ||||
| 			} | ||||
| 		} else | ||||
| 			stmt_sql = "PRAGMA user_version = 3"; | ||||
| 		OSMO_ASSERT(stmt_sql); | ||||
|  | ||||
| 		rc = sqlite3_prepare_v2(dbc->db, stmt_sql, -1, &stmt, NULL); | ||||
| 		if (rc != SQLITE_OK) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_sql); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		rc = sqlite3_step(stmt); | ||||
| 		db_remove_reset(stmt); | ||||
| 		sqlite3_finalize(stmt); | ||||
| 		if (rc != SQLITE_DONE) { | ||||
| 			LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 2: '%s'\n", | ||||
| 			     stmt_sql); | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return SQLITE_DONE; | ||||
| } | ||||
|  | ||||
| static int db_upgrade_v4(struct db_context *dbc) | ||||
| { | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	int rc; | ||||
| 	const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN last_lu_rat TEXT default NULL"; | ||||
| 	const char *set_schema_version_sql = "PRAGMA user_version = 4"; | ||||
|  | ||||
| 	rc = sqlite3_prepare_v2(dbc->db, update_stmt_sql, -1, &stmt, NULL); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", update_stmt_sql); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	db_remove_reset(stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	if (rc != SQLITE_DONE) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1); | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	rc = sqlite3_prepare_v2(dbc->db, set_schema_version_sql, -1, &stmt, NULL); | ||||
| 	if (rc != SQLITE_OK) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", set_schema_version_sql); | ||||
| 		return rc; | ||||
| 	} | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc != SQLITE_DONE) | ||||
| 		LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 4\n"); | ||||
|  | ||||
| 	db_remove_reset(stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int db_get_user_version(struct db_context *dbc) | ||||
| { | ||||
| 	const char *user_version_sql = "PRAGMA user_version"; | ||||
| @@ -440,6 +527,8 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg | ||||
| 	LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version); | ||||
|  | ||||
| 	if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) { | ||||
| 		int orig_version = version; | ||||
|  | ||||
| 		switch (version) { | ||||
| 		case 0: | ||||
| 			rc = db_upgrade_v1(dbc); | ||||
| @@ -459,21 +548,39 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg | ||||
| 			} | ||||
| 			version = 2; | ||||
| 			/* fall through */ | ||||
| 		case 2: | ||||
| 			rc = db_upgrade_v3(dbc); | ||||
| 			if (rc != SQLITE_DONE) { | ||||
| 				LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 2: (rc=%d) %s\n", | ||||
| 				     rc, sqlite3_errmsg(dbc->db)); | ||||
| 				goto out_free; | ||||
| 			} | ||||
| 			version = 3; | ||||
| 			/* fall through */ | ||||
| 		case 3: | ||||
| 			rc = db_upgrade_v4(dbc); | ||||
| 			if (rc != SQLITE_DONE) { | ||||
| 				LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 4: (rc=%d) %s\n", | ||||
| 				     rc, sqlite3_errmsg(dbc->db)); | ||||
| 				goto out_free; | ||||
| 			} | ||||
| 			version = 4; | ||||
| 			/* fall through */ | ||||
| 		/* case N: ... */ | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n", | ||||
| 		     dbc->fname, version); | ||||
| 		LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded from HLR DB schema version %d to %d\n", | ||||
| 		     dbc->fname, orig_version, version); | ||||
| 	} | ||||
|  | ||||
| 	if (version != CURRENT_SCHEMA_VERSION) { | ||||
| 		if (version < CURRENT_SCHEMA_VERSION) { | ||||
| 			LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version); | ||||
| 			if (!allow_upgrade) { | ||||
| 				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; " | ||||
| 				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; " | ||||
| 				     "use the --db-upgrade option to allow HLR database upgrades\n", | ||||
| 				     CURRENT_SCHEMA_VERSION); | ||||
| 				     version, CURRENT_SCHEMA_VERSION); | ||||
| 			} | ||||
| 		} else | ||||
| 			LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version); | ||||
|   | ||||
							
								
								
									
										19
									
								
								src/db.h
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/db.h
									
									
									
									
									
								
							| @@ -3,6 +3,8 @@ | ||||
| #include <stdbool.h> | ||||
| #include <sqlite3.h> | ||||
|  | ||||
| #include <osmocom/gsm/gsm_utils.h> | ||||
|  | ||||
| struct hlr; | ||||
|  | ||||
| enum stmt_idx { | ||||
| @@ -30,6 +32,8 @@ enum stmt_idx { | ||||
| 	DB_STMT_SET_LAST_LU_SEEN, | ||||
| 	DB_STMT_EXISTS_BY_IMSI, | ||||
| 	DB_STMT_EXISTS_BY_MSISDN, | ||||
| 	DB_STMT_UPD_RAT_FLAG, | ||||
| 	DB_STMT_RAT_BY_ID, | ||||
| 	_NUM_DB_STMT | ||||
| }; | ||||
|  | ||||
| @@ -65,7 +69,7 @@ int db_update_sqn(struct db_context *dbc, int64_t id, | ||||
| int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| 	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec, | ||||
| 	       unsigned int num_vec, const uint8_t *rand_auts, | ||||
| 	       const uint8_t *auts); | ||||
| 	       const uint8_t *auts, bool separation_bit); | ||||
|  | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/gsm/protocol/gsm_23_003.h> | ||||
| @@ -95,8 +99,14 @@ struct hlr_subscriber { | ||||
| 	bool		ms_purged_cs; | ||||
| 	bool		ms_purged_ps; | ||||
| 	time_t		last_lu_seen; | ||||
| 	char		last_lu_rat[128]; | ||||
| 	bool		rat_types[OSMO_RAT_COUNT]; | ||||
| }; | ||||
|  | ||||
| static const struct hlr_subscriber hlr_subscriber_empty = { | ||||
| 		.rat_types = { true, true, true }, | ||||
| 	}; | ||||
|  | ||||
| /* A format string for use with strptime(3). This format string is | ||||
|  * used to parse the last_lu_seen column stored in the HLR database. | ||||
|  * See https://sqlite.org/lang_datefunc.html, function datetime(). */ | ||||
| @@ -149,13 +159,18 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id, | ||||
| int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr); | ||||
| int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps); | ||||
| int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, | ||||
| 		 const char *vlr_or_sgsn_number, bool is_ps); | ||||
| 		 const char *vlr_or_sgsn_number, bool is_ps, | ||||
| 		 const enum osmo_rat_type rat_types[], size_t rat_types_len); | ||||
|  | ||||
| int db_subscr_purge(struct db_context *dbc, const char *by_imsi, | ||||
| 		    bool purge_val, bool is_ps); | ||||
|  | ||||
| int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps); | ||||
|  | ||||
| int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed); | ||||
| int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr); | ||||
| int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed); | ||||
|  | ||||
| /*! Call sqlite3_column_text() and copy result to a char[]. | ||||
|  * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target. | ||||
|  * \param[in] stmt  An sqlite3_stmt*. | ||||
|   | ||||
| @@ -189,7 +189,7 @@ out: | ||||
| int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| 	       unsigned int auc_3g_ind, struct osmo_auth_vector *vec, | ||||
| 	       unsigned int num_vec, const uint8_t *rand_auts, | ||||
| 	       const uint8_t *auts) | ||||
| 	       const uint8_t *auts, bool separation_bit) | ||||
| { | ||||
| 	struct osmo_sub_auth_data aud2g, aud3g; | ||||
| 	int64_t subscr_id; | ||||
| @@ -209,6 +209,12 @@ int db_get_auc(struct db_context *dbc, const char *imsi, | ||||
| 		       aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind); | ||||
| 		aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1; | ||||
| 	} | ||||
| 	/* the first bit (bit0) cannot be used as AMF anymore, but has been | ||||
| 	 * re-appropriated as the separation bit.  See 3GPP TS 33.102 Annex H | ||||
| 	 * together with 3GPP TS 33.401 / 33.402 / 33.501 */ | ||||
| 	aud3g.u.umts.amf[0] = aud3g.u.umts.amf[0] & 0x7f; | ||||
| 	if (separation_bit) | ||||
| 		aud3g.u.umts.amf[0] |= 0x80; | ||||
|  | ||||
| 	LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec); | ||||
| 	rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts); | ||||
|   | ||||
							
								
								
									
										142
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/db_hlr.c
									
									
									
									
									
								
							| @@ -461,7 +461,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri | ||||
| 	if (!subscr) | ||||
| 		goto out; | ||||
|  | ||||
| 	*subscr = (struct hlr_subscriber){}; | ||||
| 	*subscr = hlr_subscriber_empty; | ||||
|  | ||||
| 	/* obtain the various columns */ | ||||
| 	subscr->id = sqlite3_column_int64(stmt, 0); | ||||
| @@ -493,10 +493,14 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	copy_sqlite3_text_to_buf(subscr->last_lu_rat, stmt, 15); | ||||
|  | ||||
| out: | ||||
| 	db_remove_reset(stmt); | ||||
|  | ||||
| 	if (ret == 0) | ||||
| 		db_subscr_get_rat_types(dbc, subscr); | ||||
|  | ||||
| 	switch (ret) { | ||||
| 	case 0: | ||||
| 		*err = NULL; | ||||
| @@ -722,11 +726,14 @@ out: | ||||
|  *         -EIO on database errors. | ||||
|  */ | ||||
| int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, | ||||
| 		 const char *vlr_or_sgsn_number, bool is_ps) | ||||
| 		 const char *vlr_or_sgsn_number, bool is_ps, | ||||
| 		 const enum osmo_rat_type rat_types[], size_t rat_types_len) | ||||
| { | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	int rc, ret = 0; | ||||
| 	struct timespec localtime; | ||||
| 	char rat_types_str[128] = ""; | ||||
| 	int i; | ||||
|  | ||||
| 	stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID | ||||
| 			       : DB_STMT_UPD_VLR_BY_ID]; | ||||
| @@ -780,6 +787,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < rat_types_len; i++) { | ||||
| 		char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str)); | ||||
| 		int len = sizeof(rat_types_str) - (pos - rat_types_str); | ||||
| 		rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i])); | ||||
| 		if (rc > len) { | ||||
| 			osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!db_bind_text(stmt, "$rat", rat_types_str)) { | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc != SQLITE_DONE) { | ||||
| 		LOGP(DAUC, LOGL_ERROR, | ||||
| @@ -909,3 +931,119 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed) | ||||
| { | ||||
| 	int rc; | ||||
| 	int ret = 0; | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG]; | ||||
|  | ||||
| 	if (!db_bind_int64(stmt, "$subscriber_id", subscr_id)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT); | ||||
| 	if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat))) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	/* execute the statement */ | ||||
| 	rc = sqlite3_step(stmt); | ||||
| 	if (rc != SQLITE_DONE) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n", | ||||
| 		     allowed ? "enable" : "disable", osmo_rat_type_name(rat), | ||||
| 		     sqlite3_errmsg(dbc->db)); | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* verify execution result */ | ||||
| 	rc = sqlite3_changes(dbc->db); | ||||
| 	if (!rc) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n", | ||||
| 		     allowed ? "enable" : "disable", osmo_rat_type_name(rat), | ||||
| 		     subscr_id); | ||||
| 		ret = -ENOENT; | ||||
| 		goto out; | ||||
| 	} else if (rc != 1) { | ||||
| 		LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n", | ||||
| 		     allowed ? "enable" : "disable", osmo_rat_type_name(rat), | ||||
| 		     rc); | ||||
| 		ret = -EIO; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	db_remove_reset(stmt); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	int rc; | ||||
| 	int ret = 0; | ||||
| 	int i; | ||||
| 	sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID]; | ||||
|  | ||||
| 	if (!db_bind_int64(stmt, "$subscriber_id", subscr->id)) | ||||
| 		return -EIO; | ||||
|  | ||||
| 	for (i = 0; i < OSMO_RAT_COUNT; i++) | ||||
| 		subscr->rat_types[i] = true; | ||||
|  | ||||
| 	/* execute the statement */ | ||||
| 	while (1) { | ||||
| 		enum osmo_rat_type rat; | ||||
| 		bool allowed; | ||||
|  | ||||
| 		rc = sqlite3_step(stmt); | ||||
|  | ||||
| 		if (rc == SQLITE_DONE) | ||||
| 			break; | ||||
| 		if (rc != SQLITE_ROW) | ||||
| 			return -rc; | ||||
|  | ||||
| 		rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0)); | ||||
| 		if (rc == -EINVAL) { | ||||
| 			ret = -EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		if (rc <= 0 || rc >= OSMO_RAT_COUNT) { | ||||
| 			ret = -EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		rat = rc; | ||||
|  | ||||
| 		allowed = sqlite3_column_int(stmt, 1); | ||||
|  | ||||
| 		subscr->rat_types[rat] = allowed; | ||||
| 		LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n", | ||||
| 		     subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden"); | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	db_remove_reset(stmt); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed) | ||||
| { | ||||
| 	int rc; | ||||
| 	OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT); | ||||
|  | ||||
| 	db_subscr_get_rat_types(hlr->dbc, subscr); | ||||
|  | ||||
| 	if (subscr->rat_types[rat] == allowed) { | ||||
| 		LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n", | ||||
| 		       allowed ? "enable" : "disable", osmo_rat_type_name(rat)); | ||||
| 		return -ENOEXEC; | ||||
| 	} | ||||
|  | ||||
| 	rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed); | ||||
| 	if (rc) | ||||
| 		return rc > 0? -rc : rc; | ||||
|  | ||||
| 	/* FIXME: If we're disabling, send message to VLR to detach subscriber */ | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										44
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/hlr.c
									
									
									
									
									
								
							| @@ -233,6 +233,7 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn, | ||||
| { | ||||
| 	struct osmo_gsup_message gsup_out; | ||||
| 	struct msgb *msg_out; | ||||
| 	bool separation_bit = false; | ||||
| 	int rc; | ||||
|  | ||||
| 	subscr_create_on_demand(gsup->imsi); | ||||
| @@ -241,10 +242,13 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn, | ||||
| 	memset(&gsup_out, 0, sizeof(gsup_out)); | ||||
| 	memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi)); | ||||
|  | ||||
| 	if (gsup->rat_types_len >= 1 && gsup->rat_types[0] == OSMO_RAT_EUTRAN_SGS) | ||||
| 		separation_bit = true; | ||||
|  | ||||
| 	rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind, | ||||
| 			gsup_out.auth_vectors, | ||||
| 			ARRAY_SIZE(gsup_out.auth_vectors), | ||||
| 			gsup->rand, gsup->auts); | ||||
| 			gsup->rand, gsup->auts, separation_bit); | ||||
| 	if (rc <= 0) { | ||||
| 		gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR; | ||||
| 		switch (rc) { | ||||
| @@ -259,7 +263,7 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn, | ||||
| 			break; | ||||
| 		case -ENOENT: | ||||
| 			LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi); | ||||
| 			gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN; | ||||
| 			gsup_out.cause = GMM_CAUSE_ROAMING_NOTALLOWED; | ||||
| 			break; | ||||
| 		default: | ||||
| 			LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi); | ||||
| @@ -342,6 +346,9 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn, | ||||
| { | ||||
| 	struct hlr_subscriber *subscr; | ||||
| 	struct lu_operation *luop = lu_op_alloc_conn(conn); | ||||
| 	int i; | ||||
| 	bool allowed; | ||||
|  | ||||
| 	if (!luop) { | ||||
| 		LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n"); | ||||
| 		return -EINVAL; | ||||
| @@ -375,7 +382,7 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn, | ||||
| 	if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) { | ||||
| 		/* Send Error back: Subscriber Unknown in HLR */ | ||||
| 		osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi)); | ||||
| 		lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN); | ||||
| 		lu_op_tx_error(luop, GMM_CAUSE_ROAMING_NOTALLOWED); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -389,6 +396,34 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn, | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location | ||||
| 	 * update' for the "No Suitable cells in location area" error code. */ | ||||
| 	allowed = false; | ||||
| 	LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' on %s sent RAT types: %zu\n", subscr->imsi, | ||||
| 	     gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS ? "CS" : "PS", gsup->rat_types_len); | ||||
| 	for (i = 0; i < gsup->rat_types_len; i++) { | ||||
| 		enum osmo_rat_type rat = gsup->rat_types[i]; | ||||
| 		if (rat <= 0 || rat >= OSMO_RAT_COUNT) { | ||||
| 			lu_op_tx_error(luop, GMM_CAUSE_COND_IE_ERR); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if (luop->subscr.rat_types[rat]) { | ||||
| 			allowed = true; | ||||
| 			LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' allowed on %s\n", | ||||
| 			     subscr->imsi, osmo_rat_type_name(rat)); | ||||
| 		} else { | ||||
| 			LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' not allowed on %s\n", | ||||
| 			     subscr->imsi, osmo_rat_type_name(rat)); | ||||
| 		} | ||||
| 	} | ||||
| 	if (!allowed && gsup->rat_types_len > 0) { | ||||
| 		LOGP(DAUC, LOGL_DEBUG, "ISMI='%s' not allowed on %s%s\n", | ||||
| 		     subscr->imsi, osmo_rat_type_name(gsup->rat_types[0]), | ||||
| 		     gsup->rat_types_len > 1 ? " (nor on the other available RAT types)" : ""); | ||||
| 		lu_op_tx_error(luop, GMM_CAUSE_NO_SUIT_CELL_IN_LA); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* TODO: Set subscriber tracing = deactive in VLR/SGSN */ | ||||
|  | ||||
| #if 0 | ||||
| @@ -406,7 +441,8 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn, | ||||
| 	LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n", | ||||
| 	     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number", | ||||
| 	     osmo_quote_str((const char*)luop->peer, -1)); | ||||
| 	if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps)) | ||||
| 	if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps, | ||||
| 			 gsup->rat_types, gsup->rat_types_len)) | ||||
| 		LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n", | ||||
| 		     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number"); | ||||
|  | ||||
|   | ||||
							
								
								
									
										164
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							| @@ -355,6 +355,150 @@ static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session * | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_get_ran(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			       const struct osmo_gsup_message *gsup, | ||||
| 			       const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	char response[512]; | ||||
| 	int rc; | ||||
| 	const char *rat; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		if (!*subscr.last_lu_rat) | ||||
| 			rat = "nothing, you don't exist"; | ||||
| 		else if (!strcmp(subscr.last_lu_rat, "GERAN-A")) | ||||
| 			rat = "2G"; | ||||
| 		else if (!strcmp(subscr.last_lu_rat, "UTRAN-Iu")) | ||||
| 			rat = "3G"; | ||||
| 		else | ||||
| 			rat = subscr.last_lu_rat; | ||||
|  | ||||
| 		snprintf(response, sizeof(response), | ||||
| 			 "Now on %s. Available:%s%s.", | ||||
| 			 rat, | ||||
| 			 subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "", | ||||
| 			 subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : ""); | ||||
|  | ||||
| 		rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, response); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_gsm_on(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			      const struct osmo_gsup_message *gsup, | ||||
| 			      const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, true); | ||||
| 		rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, | ||||
| 			"Enabled GERAN-A (2G)"); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_gsm_off(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			       const struct osmo_gsup_message *gsup, | ||||
| 			       const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, false); | ||||
| 		rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, | ||||
| 			"Disabled GERAN-A (2G)"); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_umts_on(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 			       const struct osmo_gsup_message *gsup, | ||||
| 			       const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true); | ||||
| 		rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, | ||||
| 			"Enabled UTRAN-Iu (3G)"); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int handle_ussd_umts_off(struct osmo_gsup_conn *conn, struct ss_session *ss, | ||||
| 				const struct osmo_gsup_message *gsup, | ||||
| 				const struct ss_request *req) | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); | ||||
| 	switch (rc) { | ||||
| 	case 0: | ||||
| 		hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false); | ||||
| 		rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, | ||||
| 			"Disabled UTRAN-Iu (3G)"); | ||||
| 		break; | ||||
| 	case -ENOENT: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); | ||||
| 		break; | ||||
| 	case -EIO: | ||||
| 	default: | ||||
| 		rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static const struct hlr_iuse hlr_iuses[] = { | ||||
| 	{ | ||||
| @@ -365,6 +509,26 @@ static const struct hlr_iuse hlr_iuses[] = { | ||||
| 		.name = "own-imsi", | ||||
| 		.handle_ussd = handle_ussd_own_imsi, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "get-ran", | ||||
| 		.handle_ussd = handle_ussd_get_ran, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "gsm-on", | ||||
| 		.handle_ussd = handle_ussd_gsm_on, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "gsm-off", | ||||
| 		.handle_ussd = handle_ussd_gsm_off, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "umts-on", | ||||
| 		.handle_ussd = handle_ussd_umts_on, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "umts-off", | ||||
| 		.handle_ussd = handle_ussd_umts_off, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| const struct hlr_iuse *iuse_find(const char *name) | ||||
|   | ||||
| @@ -156,10 +156,13 @@ DEFUN(cfg_hlr_gsup_bind_ip, | ||||
| #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_CHOICE "(own-msisdn|own-imsi|get-ran|gsm-on|gsm-off|umts-on|umts-off)" | ||||
| #define INT_STR "Internal USSD Handler\n" \ | ||||
| 		"Respond with subscribers' own MSISDN\n" \ | ||||
| 		"Respond with subscribers' own IMSI\n" | ||||
| 		"Respond with subscribers' own IMSI\n" \ | ||||
| 		"Respond with available RAN types\n" \ | ||||
| 		"Enable UMTS service\n" \ | ||||
| 		"Disable UMTS service\n" | ||||
|  | ||||
| #define EXT_STR "External USSD Handler\n" \ | ||||
| 		"Name of External USSD Handler (IPA CCM ID)\n" | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/gsm/gsm_utils.h> | ||||
|  | ||||
| #include "hlr.h" | ||||
| #include "db.h" | ||||
| @@ -35,24 +36,23 @@ struct vty; | ||||
|  | ||||
| #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) | ||||
|  | ||||
| static char * | ||||
| get_datestr(const time_t *t, char *datebuf) | ||||
| static char *get_datestr(const time_t *t) | ||||
| { | ||||
| 	char *p, *s = ctime_r(t, datebuf); | ||||
| 	static char buf[32]; | ||||
| 	struct tm tm; | ||||
|  | ||||
| 	/* Strip trailing newline. */ | ||||
| 	p = strchr(s, '\n'); | ||||
| 	if (p) | ||||
| 		*p = '\0'; | ||||
| 	return s; | ||||
| 	tm = *gmtime(t); | ||||
|  | ||||
| 	strftime(buf, sizeof(buf), "%FT%T+00:00", &tm); | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr) | ||||
| { | ||||
| 	int rc; | ||||
| 	int i; | ||||
| 	struct osmo_sub_auth_data aud2g; | ||||
| 	struct osmo_sub_auth_data aud3g; | ||||
| 	char datebuf[26]; /* for ctime_r(3) */ | ||||
|  | ||||
| 	vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE); | ||||
|  | ||||
| @@ -88,7 +88,15 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr) | ||||
| 	if (subscr->ms_purged_ps) | ||||
| 		vty_out(vty, "    PS purged%s", VTY_NEWLINE); | ||||
| 	if (subscr->last_lu_seen) | ||||
| 		vty_out(vty, "    last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE); | ||||
| 		vty_out(vty, "    last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE); | ||||
| 	if (subscr->last_lu_rat[0]) | ||||
| 		vty_out(vty, "    last LU RAT: %s%s", subscr->last_lu_rat, VTY_NEWLINE); | ||||
| 	for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) { | ||||
| 		vty_out(vty, "    %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden", | ||||
| 			VTY_NEWLINE); | ||||
| 	} | ||||
| 	if (subscr->ms_purged_cs) | ||||
| 		vty_out(vty, "    CS purged%s", VTY_NEWLINE); | ||||
|  | ||||
| 	if (!*subscr->imsi) | ||||
| 		return; | ||||
| @@ -605,6 +613,47 @@ DEFUN(subscriber_nam, | ||||
| } | ||||
|  | ||||
|  | ||||
| DEFUN(subscriber_rat, | ||||
|       subscriber_rat_cmd, | ||||
|       SUBSCR_UPDATE "rat (geran-a|utran-iu) (allowed|forbidden)", | ||||
|       SUBSCR_UPDATE_HELP | ||||
|       "Allow or forbid specific Radio Access Types\n" | ||||
|       "Set access to GERAN-A\n" | ||||
|       "Set access to UTRAN-Iu\n" | ||||
|       "Allow access\n" | ||||
|       "Forbid access\n") | ||||
| { | ||||
| 	struct hlr_subscriber subscr; | ||||
| 	const char *id_type = argv[0]; | ||||
| 	const char *id = argv[1]; | ||||
| 	const char *rat_str = argv[2]; | ||||
| 	const char *allowed_forbidden = argv[3]; | ||||
| 	enum osmo_rat_type rat = OSMO_RAT_UNKNOWN; | ||||
| 	bool allowed; | ||||
| 	int rc; | ||||
|  | ||||
| 	if (strcmp(rat_str, "geran-a") == 0) | ||||
| 		rat = OSMO_RAT_GERAN_A; | ||||
| 	else if (strcmp(rat_str, "utran-iu") == 0) | ||||
| 		rat = OSMO_RAT_UTRAN_IU; | ||||
| 	else if (strcmp(rat_str, "eutran") == 0) | ||||
| 		rat = OSMO_RAT_EUTRAN_SGS; | ||||
|  | ||||
| 	allowed = (strcmp(allowed_forbidden, "allowed") == 0); | ||||
|  | ||||
| 	if (get_subscr_by_argv(vty, id_type, id, &subscr)) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed); | ||||
|  | ||||
| 	if (rc && rc != -ENOEXEC) { | ||||
| 		vty_out(vty, "%% Error: cannot set %s to %s%s", | ||||
| 			osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| void hlr_vty_subscriber_init(void) | ||||
| { | ||||
| 	install_element_ve(&subscriber_show_cmd); | ||||
| @@ -618,4 +667,5 @@ void hlr_vty_subscriber_init(void) | ||||
| 	install_element(ENABLE_NODE, &subscriber_aud3g_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_imei_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_nam_cmd); | ||||
| 	install_element(ENABLE_NODE, &subscriber_rat_cmd); | ||||
| } | ||||
|   | ||||
| @@ -54,6 +54,9 @@ struct lu_operation { | ||||
| 	enum lu_state state; | ||||
| 	/*! CS (false) or PS (true) Location Update? */ | ||||
| 	bool is_ps; | ||||
| 	/*! RAT type indicator: coming in on GERAN-A? UTRAN-Iu? */ | ||||
| 	enum osmo_rat_type via_rat; | ||||
|  | ||||
| 	/*! currently running timer */ | ||||
| 	struct osmo_timer_list timer; | ||||
|  | ||||
|   | ||||
| @@ -170,6 +170,7 @@ void dump_subscr(struct hlr_subscriber *subscr) | ||||
| 		Pfo(lmsi, "0x%x", subscr); | ||||
| 	Pb(true, ms_purged_cs); | ||||
| 	Pb(true, ms_purged_ps); | ||||
| 	Ps(last_lu_rat); | ||||
| 	fprintf(stderr, "}\n"); | ||||
| #undef Ps | ||||
| #undef Pd | ||||
| @@ -234,6 +235,7 @@ static const char *imsi1 = "123456789000001"; | ||||
| static const char *imsi2 = "123456789000002"; | ||||
| static const char *short_imsi = "123456"; | ||||
| static const char *unknown_imsi = "999999999"; | ||||
| static const enum osmo_rat_type rat_types[2] = { OSMO_RAT_GERAN_A, OSMO_RAT_UTRAN_IU, }; | ||||
|  | ||||
| static void test_subscr_create_update_sel_delete() | ||||
| { | ||||
| @@ -386,39 +388,44 @@ static void test_subscr_create_update_sel_delete() | ||||
|  | ||||
| 	comment("Record LU for PS and CS (SGSN and VLR names)"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "712", false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Record LU for PS and CS (SGSN and VLR names) *again*"); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Unset LU info for PS and CS (SGSN and VLR names)"); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", true, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "", false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false, NULL, 0), 0); | ||||
| 	ASSERT_SEL(id, id0, 0); | ||||
|  | ||||
| 	comment("Record LU for non-existent ID"); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true, NULL, 0), -ENOENT); | ||||
| 	ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false, NULL, 0), -ENOENT); | ||||
| 	ASSERT_SEL(id, 99999, -ENOENT); | ||||
|  | ||||
| 	comment("Purge and un-purge PS and CS"); | ||||
|   | ||||
| @@ -435,7 +435,7 @@ DAUC Cannot disable CS: no such subscriber: IMSI='foobar' | ||||
|  | ||||
| --- Record LU for PS and CS (SGSN and VLR names) | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "5952", true) --> 0 | ||||
| db_subscr_lu(dbc, id0, "5952", true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -445,7 +445,7 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '5952', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "712", false) --> 0 | ||||
| db_subscr_lu(dbc, id0, "712", false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -459,7 +459,7 @@ struct hlr_subscriber { | ||||
|  | ||||
| --- Record LU for PS and CS (SGSN and VLR names) *again* | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "111", true) --> 0 | ||||
| db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -470,7 +470,7 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '111', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "111", true) --> 0 | ||||
| db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -481,7 +481,7 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '111', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "222", false) --> 0 | ||||
| db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -492,7 +492,7 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '111', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "222", false) --> 0 | ||||
| db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -503,20 +503,44 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '111', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
|   .id = 1, | ||||
|   .imsi = '123456789000000', | ||||
|   .msisdn = '543210123456789', | ||||
|   .vlr_number = '333', | ||||
|   .sgsn_number = '111', | ||||
|   .last_lu_rat = 'GERAN-A', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
|   .id = 1, | ||||
|   .imsi = '123456789000000', | ||||
|   .msisdn = '543210123456789', | ||||
|   .vlr_number = '333', | ||||
|   .sgsn_number = '111', | ||||
|   .last_lu_rat = 'GERAN-A', | ||||
| } | ||||
|  | ||||
|  | ||||
| --- Unset LU info for PS and CS (SGSN and VLR names) | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "", true) --> 0 | ||||
| db_subscr_lu(dbc, id0, "", true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
|   .id = 1, | ||||
|   .imsi = '123456789000000', | ||||
|   .msisdn = '543210123456789', | ||||
|   .vlr_number = '222', | ||||
|   .vlr_number = '333', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "", false) --> 0 | ||||
| db_subscr_lu(dbc, id0, "", false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -525,9 +549,9 @@ struct hlr_subscriber { | ||||
|   .msisdn = '543210123456789', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "111", true) --> 0 | ||||
| db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_lu(dbc, id0, "222", false) --> 0 | ||||
| db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -538,7 +562,7 @@ struct hlr_subscriber { | ||||
|   .sgsn_number = '111', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, NULL, true) --> 0 | ||||
| db_subscr_lu(dbc, id0, NULL, true, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -548,7 +572,7 @@ struct hlr_subscriber { | ||||
|   .vlr_number = '222', | ||||
| } | ||||
|  | ||||
| db_subscr_lu(dbc, id0, NULL, false) --> 0 | ||||
| db_subscr_lu(dbc, id0, NULL, false, NULL, 0) --> 0 | ||||
|  | ||||
| db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0 | ||||
| struct hlr_subscriber { | ||||
| @@ -560,10 +584,10 @@ struct hlr_subscriber { | ||||
|  | ||||
| --- Record LU for non-existent ID | ||||
|  | ||||
| db_subscr_lu(dbc, 99999, "5952", true) --> -ENOENT | ||||
| db_subscr_lu(dbc, 99999, "5952", true, NULL, 0) --> -ENOENT | ||||
| DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber | ||||
|  | ||||
| db_subscr_lu(dbc, 99999, "712", false) --> -ENOENT | ||||
| db_subscr_lu(dbc, 99999, "712", false, NULL, 0) --> -ENOENT | ||||
| DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber | ||||
|  | ||||
| db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT | ||||
|   | ||||
		Reference in New Issue
	
	Block a user