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.
This commit is contained in:
parent
4d62d63151
commit
bfbdeec714
|
@ -41,6 +41,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
||||||
enum gsm_subscriber_field field,
|
enum gsm_subscriber_field field,
|
||||||
const char *subscr);
|
const char *subscr);
|
||||||
int db_sync_subscriber(struct gsm_subscriber *subscriber);
|
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_tmsi(struct gsm_subscriber *subscriber);
|
||||||
int db_subscriber_alloc_exten(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);
|
int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token);
|
||||||
|
|
|
@ -247,6 +247,9 @@ struct gsm_network {
|
||||||
int T3122;
|
int T3122;
|
||||||
int T3141;
|
int T3141;
|
||||||
|
|
||||||
|
/* timer to expire old location updates */
|
||||||
|
struct osmo_timer_list subscr_expire_timer;
|
||||||
|
|
||||||
/* Radio Resource Location Protocol (TS 04.31) */
|
/* Radio Resource Location Protocol (TS 04.31) */
|
||||||
struct {
|
struct {
|
||||||
enum rrlp_mode mode;
|
enum rrlp_mode mode;
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct gsm_subscriber {
|
||||||
char name[GSM_NAME_LENGTH];
|
char name[GSM_NAME_LENGTH];
|
||||||
char extension[GSM_EXTENSION_LENGTH];
|
char extension[GSM_EXTENSION_LENGTH];
|
||||||
int authorized;
|
int authorized;
|
||||||
|
time_t expire_lu;
|
||||||
|
|
||||||
/* Temporary field which is not stored in the DB/HLR */
|
/* Temporary field which is not stored in the DB/HLR */
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
@ -98,6 +99,7 @@ char *subscr_name(struct gsm_subscriber *subscr);
|
||||||
|
|
||||||
int subscr_purge_inactive(struct gsm_network *net);
|
int subscr_purge_inactive(struct gsm_network *net);
|
||||||
void subscr_update_from_db(struct gsm_subscriber *subscr);
|
void subscr_update_from_db(struct gsm_subscriber *subscr);
|
||||||
|
void subscr_expire(struct gsm_network *net);
|
||||||
|
|
||||||
/* internal */
|
/* internal */
|
||||||
struct gsm_subscriber *subscr_alloc(void);
|
struct gsm_subscriber *subscr_alloc(void);
|
||||||
|
|
|
@ -42,6 +42,8 @@ static char *db_basename = NULL;
|
||||||
static char *db_dirname = NULL;
|
static char *db_dirname = NULL;
|
||||||
static dbi_conn conn;
|
static dbi_conn conn;
|
||||||
|
|
||||||
|
#define SCHEMA_REVISION "3"
|
||||||
|
|
||||||
static char *create_stmts[] = {
|
static char *create_stmts[] = {
|
||||||
"CREATE TABLE IF NOT EXISTS Meta ("
|
"CREATE TABLE IF NOT EXISTS Meta ("
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
|
@ -51,7 +53,7 @@ static char *create_stmts[] = {
|
||||||
"INSERT OR IGNORE INTO Meta "
|
"INSERT OR IGNORE INTO Meta "
|
||||||
"(key, value) "
|
"(key, value) "
|
||||||
"VALUES "
|
"VALUES "
|
||||||
"('revision', '2')",
|
"('revision', " SCHEMA_REVISION ")",
|
||||||
"CREATE TABLE IF NOT EXISTS Subscriber ("
|
"CREATE TABLE IF NOT EXISTS Subscriber ("
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
"created TIMESTAMP NOT NULL, "
|
"created TIMESTAMP NOT NULL, "
|
||||||
|
@ -61,7 +63,8 @@ static char *create_stmts[] = {
|
||||||
"extension TEXT UNIQUE, "
|
"extension TEXT UNIQUE, "
|
||||||
"authorized INTEGER NOT NULL DEFAULT 0, "
|
"authorized INTEGER NOT NULL DEFAULT 0, "
|
||||||
"tmsi TEXT UNIQUE, "
|
"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 ("
|
"CREATE TABLE IF NOT EXISTS AuthToken ("
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"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);
|
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)
|
static int check_db_revision(void)
|
||||||
{
|
{
|
||||||
dbi_result result;
|
dbi_result result;
|
||||||
const char *rev;
|
const char *rev_s;
|
||||||
|
|
||||||
result = dbi_conn_query(conn,
|
result = dbi_conn_query(conn,
|
||||||
"SELECT value FROM Meta WHERE key='revision'");
|
"SELECT value FROM Meta WHERE key='revision'");
|
||||||
|
@ -172,11 +204,37 @@ static int check_db_revision(void)
|
||||||
dbi_result_free(result);
|
dbi_result_free(result);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
rev = dbi_result_get_string(result, "value");
|
rev_s = dbi_result_get_string(result, "value");
|
||||||
if (!rev || atoi(rev) != 2) {
|
if (!rev_s) {
|
||||||
dbi_result_free(result);
|
dbi_result_free(result);
|
||||||
return -EINVAL;
|
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);
|
dbi_result_free(result);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -242,6 +300,8 @@ int db_prepare(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db_configure();
|
||||||
|
|
||||||
return 0;
|
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);
|
strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
|
||||||
|
|
||||||
subscr->lac = dbi_result_get_uint(result, "lac");
|
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");
|
subscr->authorized = dbi_result_get_uint(result, "authorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,13 +773,15 @@ int db_sync_subscriber(struct gsm_subscriber *subscriber)
|
||||||
"extension = %s, "
|
"extension = %s, "
|
||||||
"authorized = %i, "
|
"authorized = %i, "
|
||||||
"tmsi = %s, "
|
"tmsi = %s, "
|
||||||
"lac = %i "
|
"lac = %i, "
|
||||||
|
"expire_lu = datetime(%i, 'unixepoch') "
|
||||||
"WHERE imsi = %s ",
|
"WHERE imsi = %s ",
|
||||||
q_name,
|
q_name,
|
||||||
q_extension,
|
q_extension,
|
||||||
subscriber->authorized,
|
subscriber->authorized,
|
||||||
q_tmsi,
|
q_tmsi,
|
||||||
subscriber->lac,
|
subscriber->lac,
|
||||||
|
subscriber->expire_lu,
|
||||||
subscriber->imsi);
|
subscriber->imsi);
|
||||||
|
|
||||||
free(q_tmsi);
|
free(q_tmsi);
|
||||||
|
@ -776,6 +844,29 @@ int db_sync_equipment(struct gsm_equipment *equip)
|
||||||
return 0;
|
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)
|
int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
|
||||||
{
|
{
|
||||||
dbi_result result = NULL;
|
dbi_result result = NULL;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
|
|
||||||
|
@ -333,6 +334,21 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
|
||||||
s->net = bts->network;
|
s->net = bts->network;
|
||||||
/* Indicate "attached to LAC" */
|
/* Indicate "attached to LAC" */
|
||||||
s->lac = bts->location_area_code;
|
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",
|
LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n",
|
||||||
subscr_name(s), s->lac);
|
subscr_name(s), s->lac);
|
||||||
rc = db_sync_subscriber(s);
|
rc = db_sync_subscriber(s);
|
||||||
|
@ -364,6 +380,25 @@ void subscr_update_from_db(struct gsm_subscriber *sub)
|
||||||
db_subscriber_update(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)
|
int subscr_pending_requests(struct gsm_subscriber *sub)
|
||||||
{
|
{
|
||||||
struct subscr_request *req;
|
struct subscr_request *req;
|
||||||
|
|
Loading…
Reference in New Issue