From c8fddc509df4f3c2c0941cee263a3568cee71f3c Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Thu, 27 Dec 2012 00:02:01 +0100 Subject: Stricter limits for T3212 (location update period) libcommon: Default to 30min location update period libbsc: Limit VTY value for periodic update and disallow the value 0 According to GSM 04.08 Table 10.5.33 "The value 0 is used for infinite timeout value i.e. periodic updating shall not be used within the cell." This was the default value until now, but the code that deals with expiring inactive subscribers in the next commit can't handle that case so this remains a TODO for now. --- openbsc/src/libbsc/bsc_vty.c | 7 +++---- openbsc/src/libcommon/gsm_data.c | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index dfe80c9e7..70263dd6b 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -490,9 +490,8 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) (sp->penalty_time*20)+20, VTY_NEWLINE); } - if (bts->si_common.chan_desc.t3212) - vty_out(vty, " periodic location update %u%s", - bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); + vty_out(vty, " periodic location update %u%s", + bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); vty_out(vty, " channel allocator %s%s", bts->chan_alloc_reverse ? "descending" : "ascending", VTY_NEWLINE); @@ -2018,7 +2017,7 @@ DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, } DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, - "periodic location update <0-1530>", + "periodic location update <6-1530>", "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c index 352700adf..4ccb82004 100644 --- a/openbsc/src/libcommon/gsm_data.c +++ b/openbsc/src/libcommon/gsm_data.c @@ -408,6 +408,7 @@ struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_typ bts->si_common.chan_desc.att = 1; /* attachment required */ bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ + bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ llist_add_tail(&bts->list, &net->bts_list); -- cgit v1.2.3 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 From d85f6d54da2f45919f0ea4de0ad54562f05b0b20 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 27 Dec 2012 00:40:32 +0100 Subject: osmo-nitb: Add a timer to regularly expire --- openbsc/src/osmo-nitb/bsc_hack.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c index 40c6f4337..93630ae6a 100644 --- a/openbsc/src/osmo-nitb/bsc_hack.c +++ b/openbsc/src/osmo-nitb/bsc_hack.c @@ -63,6 +63,8 @@ static int use_db_counter = 1; /* timer to store statistics */ #define DB_SYNC_INTERVAL 60, 0 +#define EXPIRE_INTERVAL 10, 0 + static struct osmo_timer_list db_sync_timer; static void create_pcap_file(char *file) @@ -223,6 +225,12 @@ static void db_sync_timer_cb(void *data) osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); } +static void subscr_expire_cb(void *data) +{ + subscr_expire(bsc_gsmnet); + osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); +} + void talloc_ctx_init(void); extern enum node_type bsc_vty_go_parent(struct vty *vty); @@ -308,6 +316,10 @@ int main(int argc, char **argv) if (use_db_counter) osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); + bsc_gsmnet->subscr_expire_timer.cb = subscr_expire_cb; + bsc_gsmnet->subscr_expire_timer.data = NULL; + osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); + signal(SIGINT, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); -- cgit v1.2.3