mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-11-04 06:03:28 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5d07d33456 | ||
| 
						 | 
					060f2032d9 | ||
| 
						 | 
					846cbeaa6e | ||
| 
						 | 
					399a0c771e | ||
| 
						 | 
					fec93b1c46 | ||
| 
						 | 
					b1e2e70476 | ||
| 
						 | 
					0d31cf5ba8 | ||
| 
						 | 
					5e4069cac7 | ||
| 
						 | 
					4bd7bdb958 | ||
| 
						 | 
					7d29e3473a | ||
| 
						 | 
					18a1b01ca8 | 
@@ -4,8 +4,9 @@ Description=Osmocom Home Location Register (OsmoHLR)
 | 
				
			|||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
					ExecStart=/usr/bin/osmo-hlr -U -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					ExecStopPost=/usr/local/bin/save_log_tail osmo-hlr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,3 +24,8 @@ hlr
 | 
				
			|||||||
  bind ip 127.0.0.1
 | 
					  bind ip 127.0.0.1
 | 
				
			||||||
 ussd route prefix *#100# internal own-msisdn
 | 
					 ussd route prefix *#100# internal own-msisdn
 | 
				
			||||||
 ussd route prefix *#101# internal own-imsi
 | 
					 ussd route prefix *#101# internal own-imsi
 | 
				
			||||||
 | 
					 ussd route prefix *#102# internal get-ran
 | 
				
			||||||
 | 
					 ussd route prefix *#200# internal gsm-off
 | 
				
			||||||
 | 
					 ussd route prefix *#201# internal gsm-on
 | 
				
			||||||
 | 
					 ussd route prefix *#300# internal umts-off
 | 
				
			||||||
 | 
					 ussd route prefix *#301# internal umts-on
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								sql/hlr.sql
									
									
									
									
									
								
							@@ -40,7 +40,12 @@ CREATE TABLE subscriber (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	-- Timestamp of last location update seen from subscriber
 | 
						-- Timestamp of last location update seen from subscriber
 | 
				
			||||||
	-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
						-- The value is a string which encodes a UTC timestamp in granularity of seconds.
 | 
				
			||||||
	last_lu_seen TIMESTAMP default NULL
 | 
						last_lu_seen TIMESTAMP default NULL,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Last Radio Access Type list as sent during Location Updating Request.
 | 
				
			||||||
 | 
						-- This is usually just one RAT name, but can be a comma separated list of strings
 | 
				
			||||||
 | 
						-- of all the RAT types sent during Location Updating Request.
 | 
				
			||||||
 | 
						last_lu_rat	TEXT default NULL
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE subscriber_apn (
 | 
					CREATE TABLE subscriber_apn (
 | 
				
			||||||
@@ -70,8 +75,17 @@ CREATE TABLE auc_3g (
 | 
				
			|||||||
	ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end
 | 
						ind_bitlen	INTEGER NOT NULL DEFAULT 5	-- nr of index bits at lower SQN end
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
 | 
				
			||||||
 | 
					-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
 | 
				
			||||||
 | 
					CREATE TABLE subscriber_rat (
 | 
				
			||||||
 | 
						subscriber_id	INTEGER,		-- subscriber.id
 | 
				
			||||||
 | 
						rat		TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL,	-- Radio Access Technology, see enum ran_type
 | 
				
			||||||
 | 
						allowed		BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
					CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
 | 
				
			||||||
 | 
					CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Set HLR database schema version number
 | 
					-- Set HLR database schema version number
 | 
				
			||||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
					-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
 | 
				
			||||||
PRAGMA user_version = 1;
 | 
					PRAGMA user_version = 3;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								sql/upgrade_v1_to_v2.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								sql/upgrade_v1_to_v2.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 = 2;
 | 
				
			||||||
							
								
								
									
										121
									
								
								src/db.c
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								src/db.c
									
									
									
									
									
								
							@@ -28,7 +28,7 @@
 | 
				
			|||||||
#include "db_bootstrap.h"
 | 
					#include "db_bootstrap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
					/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
 | 
				
			||||||
#define CURRENT_SCHEMA_VERSION	1
 | 
					#define CURRENT_SCHEMA_VERSION	3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SEL_COLUMNS \
 | 
					#define SEL_COLUMNS \
 | 
				
			||||||
	"id," \
 | 
						"id," \
 | 
				
			||||||
@@ -44,7 +44,8 @@
 | 
				
			|||||||
	"lmsi," \
 | 
						"lmsi," \
 | 
				
			||||||
	"ms_purged_cs," \
 | 
						"ms_purged_cs," \
 | 
				
			||||||
	"ms_purged_ps," \
 | 
						"ms_purged_ps," \
 | 
				
			||||||
	"last_lu_seen"
 | 
						"last_lu_seen," \
 | 
				
			||||||
 | 
						"last_lu_rat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *stmt_sql[] = {
 | 
					static const char *stmt_sql[] = {
 | 
				
			||||||
	[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
						[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 | 
				
			||||||
@@ -75,7 +76,14 @@ static const char *stmt_sql[] = {
 | 
				
			|||||||
		"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
 | 
							"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
 | 
				
			||||||
		" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
 | 
							" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
 | 
				
			||||||
	[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
 | 
						[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
 | 
				
			||||||
	[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
 | 
						[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'), last_lu_rat = $rat"
 | 
				
			||||||
 | 
									     " WHERE id = $subscriber_id",
 | 
				
			||||||
 | 
						[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)"
 | 
				
			||||||
 | 
							" VALUES ($subscriber_id, $rat, $allowed)",
 | 
				
			||||||
 | 
						[DB_STMT_RAT_BY_ID] =
 | 
				
			||||||
 | 
							"SELECT rat, allowed"
 | 
				
			||||||
 | 
							" FROM subscriber_rat"
 | 
				
			||||||
 | 
							" WHERE subscriber_id = $subscriber_id",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
					static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
 | 
				
			||||||
@@ -290,6 +298,85 @@ db_upgrade_v1(struct db_context *dbc)
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int db_upgrade_v2(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 = 2";
 | 
				
			||||||
 | 
							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_v3(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 = 3";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 3\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int db_get_user_version(struct db_context *dbc)
 | 
					static int db_get_user_version(struct db_context *dbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *user_version_sql = "PRAGMA user_version";
 | 
						const char *user_version_sql = "PRAGMA user_version";
 | 
				
			||||||
@@ -390,6 +477,8 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
	LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
 | 
						LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
 | 
						if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
 | 
				
			||||||
 | 
							int orig_version = version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (version) {
 | 
							switch (version) {
 | 
				
			||||||
		case 0:
 | 
							case 0:
 | 
				
			||||||
			rc = db_upgrade_v1(dbc);
 | 
								rc = db_upgrade_v1(dbc);
 | 
				
			||||||
@@ -400,21 +489,39 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			version = 1;
 | 
								version = 1;
 | 
				
			||||||
			/* fall through */
 | 
								/* fall through */
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								rc = db_upgrade_v2(dbc);
 | 
				
			||||||
 | 
								if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
									LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 2: (rc=%d) %s\n",
 | 
				
			||||||
 | 
									     rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
									goto out_free;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								version = 2;
 | 
				
			||||||
 | 
								/* fall through */
 | 
				
			||||||
 | 
							case 2:
 | 
				
			||||||
 | 
								rc = db_upgrade_v3(dbc);
 | 
				
			||||||
 | 
								if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
									LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 3: (rc=%d) %s\n",
 | 
				
			||||||
 | 
									     rc, sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
									goto out_free;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								version = 2;
 | 
				
			||||||
 | 
								/* fall through */
 | 
				
			||||||
		/* case N: ... */
 | 
							/* case N: ... */
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n",
 | 
							LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded from HLR DB schema version %d to %d\n",
 | 
				
			||||||
		     dbc->fname, version);
 | 
							     dbc->fname, orig_version, version);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (version != CURRENT_SCHEMA_VERSION) {
 | 
						if (version != CURRENT_SCHEMA_VERSION) {
 | 
				
			||||||
		if (version < CURRENT_SCHEMA_VERSION) {
 | 
							if (version < CURRENT_SCHEMA_VERSION) {
 | 
				
			||||||
			LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
 | 
								LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
 | 
				
			||||||
			if (!allow_upgrade) {
 | 
								if (!allow_upgrade) {
 | 
				
			||||||
				LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; "
 | 
									LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
 | 
				
			||||||
				     "use the --db-upgrade option to allow HLR database upgrades\n",
 | 
									     "use the --db-upgrade option to allow HLR database upgrades\n",
 | 
				
			||||||
				     CURRENT_SCHEMA_VERSION);
 | 
									     version, CURRENT_SCHEMA_VERSION);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
 | 
								LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								src/db.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/db.h
									
									
									
									
									
								
							@@ -3,6 +3,8 @@
 | 
				
			|||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <sqlite3.h>
 | 
					#include <sqlite3.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr;
 | 
					struct hlr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum stmt_idx {
 | 
					enum stmt_idx {
 | 
				
			||||||
@@ -26,6 +28,8 @@ enum stmt_idx {
 | 
				
			|||||||
	DB_STMT_AUC_3G_INSERT,
 | 
						DB_STMT_AUC_3G_INSERT,
 | 
				
			||||||
	DB_STMT_AUC_3G_DELETE,
 | 
						DB_STMT_AUC_3G_DELETE,
 | 
				
			||||||
	DB_STMT_SET_LAST_LU_SEEN,
 | 
						DB_STMT_SET_LAST_LU_SEEN,
 | 
				
			||||||
 | 
						DB_STMT_UPD_RAT_FLAG,
 | 
				
			||||||
 | 
						DB_STMT_RAT_BY_ID,
 | 
				
			||||||
	_NUM_DB_STMT
 | 
						_NUM_DB_STMT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,8 +89,14 @@ struct hlr_subscriber {
 | 
				
			|||||||
	bool		ms_purged_cs;
 | 
						bool		ms_purged_cs;
 | 
				
			||||||
	bool		ms_purged_ps;
 | 
						bool		ms_purged_ps;
 | 
				
			||||||
	time_t		last_lu_seen;
 | 
						time_t		last_lu_seen;
 | 
				
			||||||
 | 
						char		last_lu_rat[128];
 | 
				
			||||||
 | 
						bool		rat_types[OSMO_RAT_COUNT];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct hlr_subscriber hlr_subscriber_empty = {
 | 
				
			||||||
 | 
							.rat_types = { true, true, true },
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* A format string for use with strptime(3). This format string is
 | 
					/* A format string for use with strptime(3). This format string is
 | 
				
			||||||
 * used to parse the last_lu_seen column stored in the HLR database.
 | 
					 * used to parse the last_lu_seen column stored in the HLR database.
 | 
				
			||||||
 * See https://sqlite.org/lang_datefunc.html, function datetime(). */
 | 
					 * See https://sqlite.org/lang_datefunc.html, function datetime(). */
 | 
				
			||||||
@@ -131,13 +141,18 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
 | 
				
			|||||||
			struct hlr_subscriber *subscr);
 | 
								struct hlr_subscriber *subscr);
 | 
				
			||||||
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
					int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const char *vlr_or_sgsn_number, bool is_ps);
 | 
							 const char *vlr_or_sgsn_number, bool is_ps,
 | 
				
			||||||
 | 
							 const enum osmo_rat_type rat_types[], size_t rat_types_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
					int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 | 
				
			||||||
		    bool purge_val, bool is_ps);
 | 
							    bool purge_val, bool is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
					int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
 | 
				
			||||||
 | 
					int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
 | 
				
			||||||
 | 
					int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Call sqlite3_column_text() and copy result to a char[].
 | 
					/*! Call sqlite3_column_text() and copy result to a char[].
 | 
				
			||||||
 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
					 * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
 | 
				
			||||||
 * \param[in] stmt  An sqlite3_stmt*.
 | 
					 * \param[in] stmt  An sqlite3_stmt*.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										142
									
								
								src/db_hlr.c
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/db_hlr.c
									
									
									
									
									
								
							@@ -409,7 +409,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
 | 
				
			|||||||
	if (!subscr)
 | 
						if (!subscr)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*subscr = (struct hlr_subscriber){};
 | 
						*subscr = hlr_subscriber_empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* obtain the various columns */
 | 
						/* obtain the various columns */
 | 
				
			||||||
	subscr->id = sqlite3_column_int64(stmt, 0);
 | 
						subscr->id = sqlite3_column_int64(stmt, 0);
 | 
				
			||||||
@@ -440,10 +440,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, 14);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	db_remove_reset(stmt);
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret == 0)
 | 
				
			||||||
 | 
							db_subscr_get_rat_types(dbc, subscr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (ret) {
 | 
						switch (ret) {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		*err = NULL;
 | 
							*err = NULL;
 | 
				
			||||||
@@ -595,11 +599,14 @@ out:
 | 
				
			|||||||
 *         -EIO on database errors.
 | 
					 *         -EIO on database errors.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
					int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			||||||
		 const char *vlr_or_sgsn_number, bool is_ps)
 | 
							 const char *vlr_or_sgsn_number, bool is_ps,
 | 
				
			||||||
 | 
							 const enum osmo_rat_type rat_types[], size_t rat_types_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
	int rc, ret = 0;
 | 
						int rc, ret = 0;
 | 
				
			||||||
	struct timespec localtime;
 | 
						struct timespec localtime;
 | 
				
			||||||
 | 
						char rat_types_str[128] = "";
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
 | 
						stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
 | 
				
			||||||
			       : DB_STMT_UPD_VLR_BY_ID];
 | 
								       : DB_STMT_UPD_VLR_BY_ID];
 | 
				
			||||||
@@ -653,6 +660,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
 | 
				
			|||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < rat_types_len; i++) {
 | 
				
			||||||
 | 
							char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str));
 | 
				
			||||||
 | 
							int len = sizeof(rat_types_str) - (pos - rat_types_str);
 | 
				
			||||||
 | 
							rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i]));
 | 
				
			||||||
 | 
							if (rc > len) {
 | 
				
			||||||
 | 
								osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, "$rat", rat_types_str)) {
 | 
				
			||||||
 | 
							ret = -EIO;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = sqlite3_step(stmt);
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
	if (rc != SQLITE_DONE) {
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR,
 | 
							LOGP(DAUC, LOGL_ERROR,
 | 
				
			||||||
@@ -782,3 +804,119 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
 | 
				
			||||||
 | 
						if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* execute the statement */
 | 
				
			||||||
 | 
						rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
						if (rc != SQLITE_DONE) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
 | 
				
			||||||
 | 
							     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
				
			||||||
 | 
							     sqlite3_errmsg(dbc->db));
 | 
				
			||||||
 | 
							ret = -EIO;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* verify execution result */
 | 
				
			||||||
 | 
						rc = sqlite3_changes(dbc->db);
 | 
				
			||||||
 | 
						if (!rc) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
 | 
				
			||||||
 | 
							     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
				
			||||||
 | 
							     subscr_id);
 | 
				
			||||||
 | 
							ret = -ENOENT;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						} else if (rc != 1) {
 | 
				
			||||||
 | 
							LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
 | 
				
			||||||
 | 
							     allowed ? "enable" : "disable", osmo_rat_type_name(rat),
 | 
				
			||||||
 | 
							     rc);
 | 
				
			||||||
 | 
							ret = -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < OSMO_RAT_COUNT; i++)
 | 
				
			||||||
 | 
							subscr->rat_types[i] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* execute the statement */
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							enum osmo_rat_type rat;
 | 
				
			||||||
 | 
							bool allowed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = sqlite3_step(stmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (rc == SQLITE_DONE)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							if (rc != SQLITE_ROW)
 | 
				
			||||||
 | 
								return -rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
 | 
				
			||||||
 | 
							if (rc == -EINVAL) {
 | 
				
			||||||
 | 
								ret = -EINVAL;
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
 | 
				
			||||||
 | 
								ret = -EINVAL;
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rat = rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							allowed = sqlite3_column_int(stmt, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							subscr->rat_types[rat] = allowed;
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
 | 
				
			||||||
 | 
							     subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						db_remove_reset(stmt);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db_subscr_get_rat_types(hlr->dbc, subscr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (subscr->rat_types[rat] == allowed) {
 | 
				
			||||||
 | 
							LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
 | 
				
			||||||
 | 
							       allowed ? "enable" : "disable", osmo_rat_type_name(rat));
 | 
				
			||||||
 | 
							return -ENOEXEC;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return rc > 0? -rc : rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* FIXME: If we're disabling, send message to VLR to detach subscriber */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/hlr.c
									
									
									
									
									
								
							@@ -263,6 +263,9 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct hlr_subscriber *subscr;
 | 
						struct hlr_subscriber *subscr;
 | 
				
			||||||
	struct lu_operation *luop = lu_op_alloc_conn(conn);
 | 
						struct lu_operation *luop = lu_op_alloc_conn(conn);
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						bool allowed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!luop) {
 | 
						if (!luop) {
 | 
				
			||||||
		LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
 | 
							LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
@@ -308,6 +311,34 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
 | 
				
			||||||
 | 
						 * update' for the "No Suitable cells in location area" error code. */
 | 
				
			||||||
 | 
						allowed = false;
 | 
				
			||||||
 | 
						LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' on %s sent RAT types: %zu\n", subscr->imsi,
 | 
				
			||||||
 | 
						     gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS ? "CS" : "PS", gsup->rat_types_len);
 | 
				
			||||||
 | 
						for (i = 0; i < gsup->rat_types_len; i++) {
 | 
				
			||||||
 | 
							enum osmo_rat_type rat = gsup->rat_types[i];
 | 
				
			||||||
 | 
							if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
 | 
				
			||||||
 | 
								lu_op_tx_error(luop, GMM_CAUSE_COND_IE_ERR);
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (luop->subscr.rat_types[rat]) {
 | 
				
			||||||
 | 
								allowed = true;
 | 
				
			||||||
 | 
								LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' allowed on %s\n",
 | 
				
			||||||
 | 
								     subscr->imsi, osmo_rat_type_name(rat));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' not allowed on %s\n",
 | 
				
			||||||
 | 
								     subscr->imsi, osmo_rat_type_name(rat));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!allowed && gsup->rat_types_len > 0) {
 | 
				
			||||||
 | 
							LOGP(DAUC, LOGL_DEBUG, "ISMI='%s' not allowed on %s%s\n",
 | 
				
			||||||
 | 
							     subscr->imsi, osmo_rat_type_name(gsup->rat_types[0]),
 | 
				
			||||||
 | 
							     gsup->rat_types_len > 1 ? " (nor on the other available RAT types)" : "");
 | 
				
			||||||
 | 
							lu_op_tx_error(luop, GMM_CAUSE_NO_SUIT_CELL_IN_LA);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
						/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
@@ -325,7 +356,8 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
	LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
 | 
						LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
 | 
				
			||||||
	     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
 | 
						     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
 | 
				
			||||||
	     osmo_quote_str((const char*)luop->peer, -1));
 | 
						     osmo_quote_str((const char*)luop->peer, -1));
 | 
				
			||||||
	if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))
 | 
						if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps,
 | 
				
			||||||
 | 
								 gsup->rat_types, gsup->rat_types_len))
 | 
				
			||||||
		LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
 | 
							LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
 | 
				
			||||||
		     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
 | 
							     subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										164
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								src/hlr_ussd.c
									
									
									
									
									
								
							@@ -320,6 +320,150 @@ static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_get_ran(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								       const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								       const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						char response[512];
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						const char *rat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							if (!*subscr.last_lu_rat)
 | 
				
			||||||
 | 
								rat = "nothing, you don't exist";
 | 
				
			||||||
 | 
							else if (!strcmp(subscr.last_lu_rat, "GERAN-A"))
 | 
				
			||||||
 | 
								rat = "2G";
 | 
				
			||||||
 | 
							else if (!strcmp(subscr.last_lu_rat, "UTRAN-Iu"))
 | 
				
			||||||
 | 
								rat = "3G";
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								rat = subscr.last_lu_rat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							snprintf(response, sizeof(response),
 | 
				
			||||||
 | 
								 "Now on %s. Available:%s%s."
 | 
				
			||||||
 | 
								 " (2G on: *#201# off: *#200# -- 3G on: *#301# off: *#300#)", rat,
 | 
				
			||||||
 | 
								 subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "",
 | 
				
			||||||
 | 
								 subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, response);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_gsm_on(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								      const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								      const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, true);
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Enabled GERAN-A (2G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_gsm_off(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								       const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								       const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, false);
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Disabled GERAN-A (2G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_umts_on(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								       const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								       const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true);
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Enabled UTRAN-Iu (3G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_umts_off(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
									const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
									const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false);
 | 
				
			||||||
 | 
							rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
 | 
				
			||||||
 | 
								"Disabled UTRAN-Iu (3G)");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct hlr_iuse hlr_iuses[] = {
 | 
					static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -330,6 +474,26 @@ static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			|||||||
		.name = "own-imsi",
 | 
							.name = "own-imsi",
 | 
				
			||||||
		.handle_ussd = handle_ussd_own_imsi,
 | 
							.handle_ussd = handle_ussd_own_imsi,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "get-ran",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_get_ran,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "gsm-on",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_gsm_on,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "gsm-off",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_gsm_off,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "umts-on",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_umts_on,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "umts-off",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_umts_off,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct hlr_iuse *iuse_find(const char *name)
 | 
					const struct hlr_iuse *iuse_find(const char *name)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,10 +133,13 @@ DEFUN(cfg_hlr_gsup_bind_ip,
 | 
				
			|||||||
#define UROUTE_STR "Routing Configuration\n"
 | 
					#define UROUTE_STR "Routing Configuration\n"
 | 
				
			||||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
					#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define INT_CHOICE "(own-msisdn|own-imsi)"
 | 
					#define INT_CHOICE "(own-msisdn|own-imsi|get-ran|gsm-on|gsm-off|umts-on|umts-off)"
 | 
				
			||||||
#define INT_STR "Internal USSD Handler\n" \
 | 
					#define INT_STR "Internal USSD Handler\n" \
 | 
				
			||||||
		"Respond with subscribers' own MSISDN\n" \
 | 
							"Respond with subscribers' own MSISDN\n" \
 | 
				
			||||||
		"Respond with subscribers' own IMSI\n"
 | 
							"Respond with subscribers' own IMSI\n" \
 | 
				
			||||||
 | 
							"Respond with available RAN types\n" \
 | 
				
			||||||
 | 
							"Enable UMTS service\n" \
 | 
				
			||||||
 | 
							"Disable UMTS service\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define EXT_STR "External USSD Handler\n" \
 | 
					#define EXT_STR "External USSD Handler\n" \
 | 
				
			||||||
		"Name of External USSD Handler (IPA CCM ID)\n"
 | 
							"Name of External USSD Handler (IPA CCM ID)\n"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@
 | 
				
			|||||||
#include <osmocom/vty/vty.h>
 | 
					#include <osmocom/vty/vty.h>
 | 
				
			||||||
#include <osmocom/vty/command.h>
 | 
					#include <osmocom/vty/command.h>
 | 
				
			||||||
#include <osmocom/core/utils.h>
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "hlr.h"
 | 
					#include "hlr.h"
 | 
				
			||||||
#include "db.h"
 | 
					#include "db.h"
 | 
				
			||||||
@@ -35,24 +36,23 @@ struct vty;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
					#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char *
 | 
					static char *get_datestr(const time_t *t)
 | 
				
			||||||
get_datestr(const time_t *t, char *datebuf)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *p, *s = ctime_r(t, datebuf);
 | 
						static char buf[32];
 | 
				
			||||||
 | 
						struct tm tm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Strip trailing newline. */
 | 
						tm = *gmtime(t);
 | 
				
			||||||
	p = strchr(s, '\n');
 | 
					
 | 
				
			||||||
	if (p)
 | 
						strftime(buf, sizeof(buf), "%FT%T+00:00", &tm);
 | 
				
			||||||
		*p = '\0';
 | 
						return buf;
 | 
				
			||||||
	return s;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
					static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
	struct osmo_sub_auth_data aud2g;
 | 
						struct osmo_sub_auth_data aud2g;
 | 
				
			||||||
	struct osmo_sub_auth_data aud3g;
 | 
						struct osmo_sub_auth_data aud3g;
 | 
				
			||||||
	char datebuf[26]; /* for ctime_r(3) */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
 | 
						vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,7 +79,15 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			|||||||
	if (subscr->ms_purged_ps)
 | 
						if (subscr->ms_purged_ps)
 | 
				
			||||||
		vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
							vty_out(vty, "    PS purged%s", VTY_NEWLINE);
 | 
				
			||||||
	if (subscr->last_lu_seen)
 | 
						if (subscr->last_lu_seen)
 | 
				
			||||||
		vty_out(vty, "    last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);
 | 
							vty_out(vty, "    last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (subscr->last_lu_rat[0])
 | 
				
			||||||
 | 
							vty_out(vty, "    last LU RAT: %s%s", subscr->last_lu_rat, VTY_NEWLINE);
 | 
				
			||||||
 | 
						for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
 | 
				
			||||||
 | 
							vty_out(vty, "    %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
 | 
				
			||||||
 | 
								VTY_NEWLINE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (subscr->ms_purged_cs)
 | 
				
			||||||
 | 
							vty_out(vty, "    CS purged%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*subscr->imsi)
 | 
						if (!*subscr->imsi)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
@@ -508,6 +516,45 @@ DEFUN(subscriber_aud3g,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(subscriber_rat,
 | 
				
			||||||
 | 
					      subscriber_rat_cmd,
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE "rat (geran-a|utran-iu) (allowed|forbidden)",
 | 
				
			||||||
 | 
					      SUBSCR_UPDATE_HELP
 | 
				
			||||||
 | 
					      "Allow or forbid specific Radio Access Types\n"
 | 
				
			||||||
 | 
					      "Set access to GERAN-A\n"
 | 
				
			||||||
 | 
					      "Set access to UTRAN-Iu\n"
 | 
				
			||||||
 | 
					      "Allow access\n"
 | 
				
			||||||
 | 
					      "Forbid access\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						const char *id_type = argv[0];
 | 
				
			||||||
 | 
						const char *id = argv[1];
 | 
				
			||||||
 | 
						const char *rat_str = argv[2];
 | 
				
			||||||
 | 
						const char *allowed_forbidden = argv[3];
 | 
				
			||||||
 | 
						enum osmo_rat_type rat;
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allowed = (strcmp(allowed_forbidden, "allowed") == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (get_subscr_by_argv(vty, id_type, id, &subscr))
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rc && rc != -ENOEXEC) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Error: cannot set %s to %s%s",
 | 
				
			||||||
 | 
								osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void hlr_vty_subscriber_init(void)
 | 
					void hlr_vty_subscriber_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	install_element_ve(&subscriber_show_cmd);
 | 
						install_element_ve(&subscriber_show_cmd);
 | 
				
			||||||
@@ -519,4 +566,5 @@ void hlr_vty_subscriber_init(void)
 | 
				
			|||||||
	install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
 | 
				
			||||||
 | 
						install_element(ENABLE_NODE, &subscriber_rat_cmd);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,9 @@ struct lu_operation {
 | 
				
			|||||||
	enum lu_state state;
 | 
						enum lu_state state;
 | 
				
			||||||
	/*! CS (false) or PS (true) Location Update? */
 | 
						/*! CS (false) or PS (true) Location Update? */
 | 
				
			||||||
	bool is_ps;
 | 
						bool is_ps;
 | 
				
			||||||
 | 
						/*! RAT type indicator: coming in on GERAN-A? UTRAN-Iu? */
 | 
				
			||||||
 | 
						enum osmo_rat_type via_rat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*! currently running timer */
 | 
						/*! currently running timer */
 | 
				
			||||||
	struct osmo_timer_list timer;
 | 
						struct osmo_timer_list timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,6 +159,7 @@ void dump_subscr(struct hlr_subscriber *subscr)
 | 
				
			|||||||
		Pfo(lmsi, "0x%x", subscr);
 | 
							Pfo(lmsi, "0x%x", subscr);
 | 
				
			||||||
	Pb(true, ms_purged_cs);
 | 
						Pb(true, ms_purged_cs);
 | 
				
			||||||
	Pb(true, ms_purged_ps);
 | 
						Pb(true, ms_purged_ps);
 | 
				
			||||||
 | 
						Ps(last_lu_rat);
 | 
				
			||||||
	fprintf(stderr, "}\n");
 | 
						fprintf(stderr, "}\n");
 | 
				
			||||||
#undef Ps
 | 
					#undef Ps
 | 
				
			||||||
#undef Pd
 | 
					#undef Pd
 | 
				
			||||||
@@ -212,6 +213,7 @@ static const char *imsi1 = "123456789000001";
 | 
				
			|||||||
static const char *imsi2 = "123456789000002";
 | 
					static const char *imsi2 = "123456789000002";
 | 
				
			||||||
static const char *short_imsi = "123456";
 | 
					static const char *short_imsi = "123456";
 | 
				
			||||||
static const char *unknown_imsi = "999999999";
 | 
					static const char *unknown_imsi = "999999999";
 | 
				
			||||||
 | 
					static const enum osmo_rat_type rat_types[2] = { OSMO_RAT_GERAN_A, OSMO_RAT_UTRAN_IU, };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_subscr_create_update_sel_delete()
 | 
					static void test_subscr_create_update_sel_delete()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -337,39 +339,44 @@ static void test_subscr_create_update_sel_delete()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "712", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
						comment("Record LU for PS and CS (SGSN and VLR names) *again*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0);
 | 
				
			||||||
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "333", false, rat_types, 1), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
						comment("Unset LU info for PS and CS (SGSN and VLR names)");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "111", true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, "222", false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0);
 | 
						ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false, NULL, 0), 0);
 | 
				
			||||||
	ASSERT_SEL(id, id0, 0);
 | 
						ASSERT_SEL(id, id0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Record LU for non-existent ID");
 | 
						comment("Record LU for non-existent ID");
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true, NULL, 0), -ENOENT);
 | 
				
			||||||
	ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT);
 | 
						ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false, NULL, 0), -ENOENT);
 | 
				
			||||||
	ASSERT_SEL(id, 99999, -ENOENT);
 | 
						ASSERT_SEL(id, 99999, -ENOENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	comment("Purge and un-purge PS and CS");
 | 
						comment("Purge and un-purge PS and CS");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -373,7 +373,7 @@ DAUC Cannot disable CS: no such subscriber: IMSI='foobar'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names)
 | 
					--- Record LU for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "5952", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "5952", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -383,7 +383,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '5952',
 | 
					  .sgsn_number = '5952',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "712", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "712", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -397,7 +397,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
					--- Record LU for PS and CS (SGSN and VLR names) *again*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -408,7 +408,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -419,7 +419,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -430,7 +430,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -441,20 +441,44 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .vlr_number = '333',
 | 
				
			||||||
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
 | 
					  .last_lu_rat = 'GERAN-A',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_lu(dbc, id0, "333", false, rat_types, 1) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
 | 
					struct hlr_subscriber {
 | 
				
			||||||
 | 
					  .id = 1,
 | 
				
			||||||
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
 | 
					  .vlr_number = '333',
 | 
				
			||||||
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
 | 
					  .last_lu_rat = 'GERAN-A',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
					--- Unset LU info for PS and CS (SGSN and VLR names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
  .id = 1,
 | 
					  .id = 1,
 | 
				
			||||||
  .imsi = '123456789000000',
 | 
					  .imsi = '123456789000000',
 | 
				
			||||||
  .msisdn = '543210123456789',
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '333',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -463,9 +487,9 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .msisdn = '543210123456789',
 | 
					  .msisdn = '543210123456789',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "111", true) --> 0
 | 
					db_subscr_lu(dbc, id0, "111", true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, "222", false) --> 0
 | 
					db_subscr_lu(dbc, id0, "222", false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -476,7 +500,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .sgsn_number = '111',
 | 
					  .sgsn_number = '111',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, NULL, true) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, true, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -486,7 +510,7 @@ struct hlr_subscriber {
 | 
				
			|||||||
  .vlr_number = '222',
 | 
					  .vlr_number = '222',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, id0, NULL, false) --> 0
 | 
					db_subscr_lu(dbc, id0, NULL, false, NULL, 0) --> 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
					db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0
 | 
				
			||||||
struct hlr_subscriber {
 | 
					struct hlr_subscriber {
 | 
				
			||||||
@@ -498,10 +522,10 @@ struct hlr_subscriber {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Record LU for non-existent ID
 | 
					--- Record LU for non-existent ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, 99999, "5952", true) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "5952", true, NULL, 0) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_lu(dbc, 99999, "712", false) --> -ENOENT
 | 
					db_subscr_lu(dbc, 99999, "712", false, NULL, 0) --> -ENOENT
 | 
				
			||||||
DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
					DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
					db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user