From 49c291a574a181a6b4c643865e72558a67c2a4f4 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 27 Dec 2012 00:27:16 +0100 Subject: libmsc: Track and update the location update expiry Set the subscriber expiry timeout to twice the duration of the location update period and provide functions subscr_expire() and db_subscriber_expire() to mark subscribers offline that have missed two location update periods. This patch increases the DB revision to 3, so the hlr will be incompatible with prior versions. We should allow 0 for T3212 as well to disable the location update period. In that case we will need a way to indicate that in the database. --- openbsc/include/openbsc/db.h | 1 + openbsc/include/openbsc/gsm_data.h | 3 + openbsc/include/openbsc/gsm_subscriber.h | 2 + openbsc/src/libmsc/db.c | 103 +++++++++++++++++++++++++++++-- openbsc/src/libmsc/gsm_subscriber.c | 35 +++++++++++ 5 files changed, 138 insertions(+), 6 deletions(-) diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h index d0c85ea30..25c2aea70 100644 --- a/openbsc/include/openbsc/db.h +++ b/openbsc/include/openbsc/db.h @@ -41,6 +41,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, enum gsm_subscriber_field field, const char *subscr); int db_sync_subscriber(struct gsm_subscriber *subscriber); +int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)); int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber); int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber); int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token); diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index ea9f601ea..feb692f7c 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -247,6 +247,9 @@ struct gsm_network { int T3122; int T3141; + /* timer to expire old location updates */ + struct osmo_timer_list subscr_expire_timer; + /* Radio Resource Location Protocol (TS 04.31) */ struct { enum rrlp_mode mode; diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h index 6cf85731b..78f9710c5 100644 --- a/openbsc/include/openbsc/gsm_subscriber.h +++ b/openbsc/include/openbsc/gsm_subscriber.h @@ -38,6 +38,7 @@ struct gsm_subscriber { char name[GSM_NAME_LENGTH]; char extension[GSM_EXTENSION_LENGTH]; int authorized; + time_t expire_lu; /* Temporary field which is not stored in the DB/HLR */ uint32_t flags; @@ -98,6 +99,7 @@ char *subscr_name(struct gsm_subscriber *subscr); int subscr_purge_inactive(struct gsm_network *net); void subscr_update_from_db(struct gsm_subscriber *subscr); +void subscr_expire(struct gsm_network *net); /* internal */ struct gsm_subscriber *subscr_alloc(void); diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c index 9a5f18db7..4e20e2318 100644 --- a/openbsc/src/libmsc/db.c +++ b/openbsc/src/libmsc/db.c @@ -42,6 +42,8 @@ static char *db_basename = NULL; static char *db_dirname = NULL; static dbi_conn conn; +#define SCHEMA_REVISION "3" + static char *create_stmts[] = { "CREATE TABLE IF NOT EXISTS Meta (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -51,7 +53,7 @@ static char *create_stmts[] = { "INSERT OR IGNORE INTO Meta " "(key, value) " "VALUES " - "('revision', '2')", + "('revision', " SCHEMA_REVISION ")", "CREATE TABLE IF NOT EXISTS Subscriber (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " @@ -61,7 +63,8 @@ static char *create_stmts[] = { "extension TEXT UNIQUE, " "authorized INTEGER NOT NULL DEFAULT 0, " "tmsi TEXT UNIQUE, " - "lac INTEGER NOT NULL DEFAULT 0" + "lac INTEGER NOT NULL DEFAULT 0, " + "expire_lu TIMESTAMP DEFAULT NULL" ")", "CREATE TABLE IF NOT EXISTS AuthToken (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -158,10 +161,39 @@ void db_error_func(dbi_conn conn, void *data) LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); } +static int update_db_revision_2(void) +{ + dbi_result result; + + result = dbi_conn_query(conn, + "ALTER TABLE Subscriber " + "ADD COLUMN expire_lu " + "TIMESTAMP DEFAULT NULL"); + if (!result) { + LOGP(DDB, LOGL_ERROR, + "Failed to alter table Subscriber (upgrade vom rev 2).\n"); + return -EINVAL; + } + dbi_result_free(result); + + result = dbi_conn_query(conn, + "UPDATE Meta " + "SET value = '3' " + "WHERE key = 'revision'"); + if (!result) { + LOGP(DDB, LOGL_ERROR, + "Failed set new revision (upgrade vom rev 2).\n"); + return -EINVAL; + } + dbi_result_free(result); + + return 0; +} + static int check_db_revision(void) { dbi_result result; - const char *rev; + const char *rev_s; result = dbi_conn_query(conn, "SELECT value FROM Meta WHERE key='revision'"); @@ -172,11 +204,37 @@ static int check_db_revision(void) dbi_result_free(result); return -EINVAL; } - rev = dbi_result_get_string(result, "value"); - if (!rev || atoi(rev) != 2) { + rev_s = dbi_result_get_string(result, "value"); + if (!rev_s) { dbi_result_free(result); return -EINVAL; } + if (!strcmp(rev_s, "2")) { + if (update_db_revision_2()) { + LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s); + dbi_result_free(result); + return -EINVAL; + } + } else if (!strcmp(rev_s, SCHEMA_REVISION)) { + /* everything is fine */ + } else { + LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%s'.\n", rev_s); + dbi_result_free(result); + return -EINVAL; + } + + dbi_result_free(result); + return 0; +} + +static int db_configure(void) +{ + dbi_result result; + + result = dbi_conn_query(conn, + "PRAGMA synchronous = FULL"); + if (!result) + return -EINVAL; dbi_result_free(result); return 0; @@ -242,6 +300,8 @@ int db_prepare(void) return -1; } + db_configure(); + return 0; } @@ -575,6 +635,12 @@ static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); subscr->lac = dbi_result_get_uint(result, "lac"); + + if (!dbi_result_field_is_null(result, "expire_lu")) + subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu"); + else + subscr->expire_lu = 0; + subscr->authorized = dbi_result_get_uint(result, "authorized"); } @@ -707,13 +773,15 @@ int db_sync_subscriber(struct gsm_subscriber *subscriber) "extension = %s, " "authorized = %i, " "tmsi = %s, " - "lac = %i " + "lac = %i, " + "expire_lu = datetime(%i, 'unixepoch') " "WHERE imsi = %s ", q_name, q_extension, subscriber->authorized, q_tmsi, subscriber->lac, + subscriber->expire_lu, subscriber->imsi); free(q_tmsi); @@ -776,6 +844,29 @@ int db_sync_equipment(struct gsm_equipment *equip) return 0; } +int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)) +{ + dbi_result result; + + result = dbi_conn_query(conn, + "SELECT id " + "FROM Subscriber " + "WHERE lac != 0 AND " + "( expire_lu is NULL " + "OR expire_lu < datetime('now') ) " + "LIMIT 1"); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n"); + return -EIO; + } + + while (dbi_result_next_row(result)) + callback(priv, dbi_result_get_ulonglong(result, "id")); + + dbi_result_free(result); + return 0; +} + int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber) { dbi_result result = NULL; diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c index 0889400b8..3e65176b1 100644 --- a/openbsc/src/libmsc/gsm_subscriber.c +++ b/openbsc/src/libmsc/gsm_subscriber.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -333,6 +334,21 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) s->net = bts->network; /* Indicate "attached to LAC" */ s->lac = bts->location_area_code; + + /* FIXME: We should allow 0 for T3212 as well to disable the + * location update period. In that case we will need a way to + * indicate that in the database and then reenable that value in + * VTY. + */ + + /* Table 10.5.33: The T3212 timeout value field is coded as the + * binary representation of the timeout value for + * periodic updating in decihours. Mark the subscriber as + * inactive if it missed two consecutive location updates. + * Timeout is twice the t3212 value plus one minute */ + s->expire_lu = time(NULL) + + (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; + LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", subscr_name(s), s->lac); rc = db_sync_subscriber(s); @@ -364,6 +380,25 @@ void subscr_update_from_db(struct gsm_subscriber *sub) db_subscriber_update(sub); } +static void subscr_expire_callback(void *data, long long unsigned int id) +{ + struct gsm_network *net = data; + struct gsm_subscriber *s = + subscr_get_by_id(net, id); + + LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %i)\n", + subscr_name(s), id); + s->lac = GSM_LAC_RESERVED_DETACHED; + db_sync_subscriber(s); + + subscr_put(s); +} + +void subscr_expire(struct gsm_network *net) +{ + db_subscriber_expire(net, subscr_expire_callback); +} + int subscr_pending_requests(struct gsm_subscriber *sub) { struct subscr_request *req; -- cgit v1.2.3