allow/disallow 2G/3G wip
Add DB storage for enabling / disabling arbitrary RAT types. So far we have only GERAN-A and UTRAN-Iu, but to be future compatible, implement an arbitrary length list of RAT types: add DB table subscriber_rat. Backwards compatibility: if there is no entry in subscriber_rat, all RAT types shall be allowed. As soon as there is an entry, it can either be false to forbid a RAT or true to still allow a RAT type. Depends: I93850710ab55a605bf61b95063a69682a2899bb1 (libosmocore) Change-Id: I3e399ca8a85421f77a9a15e608413d1507722955
This commit is contained in:
parent
18a1b01ca8
commit
7d29e3473a
11
sql/hlr.sql
11
sql/hlr.sql
|
@ -70,8 +70,17 @@ CREATE TABLE auc_3g (
|
|||
ind_bitlen INTEGER NOT NULL DEFAULT 5 -- nr of index bits at lower SQN end
|
||||
);
|
||||
|
||||
-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
|
||||
-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
|
||||
CREATE TABLE subscriber_rat (
|
||||
subscriber_id INTEGER, -- subscriber.id
|
||||
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type
|
||||
allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||
CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
|
||||
|
||||
-- Set HLR database schema version number
|
||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
|
||||
PRAGMA user_version = 1;
|
||||
PRAGMA user_version = 2;
|
||||
|
|
|
@ -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;
|
72
src/db.c
72
src/db.c
|
@ -28,7 +28,7 @@
|
|||
#include "db_bootstrap.h"
|
||||
|
||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
|
||||
#define CURRENT_SCHEMA_VERSION 1
|
||||
#define CURRENT_SCHEMA_VERSION 2
|
||||
|
||||
#define SEL_COLUMNS \
|
||||
"id," \
|
||||
|
@ -76,6 +76,12 @@ static const char *stmt_sql[] = {
|
|||
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
|
||||
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
|
||||
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
|
||||
[DB_STMT_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)
|
||||
|
@ -290,6 +296,51 @@ db_upgrade_v1(struct db_context *dbc)
|
|||
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_get_user_version(struct db_context *dbc)
|
||||
{
|
||||
const char *user_version_sql = "PRAGMA user_version";
|
||||
|
@ -390,6 +441,8 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
|
|||
LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
|
||||
|
||||
if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
|
||||
int orig_version = version;
|
||||
|
||||
switch (version) {
|
||||
case 0:
|
||||
rc = db_upgrade_v1(dbc);
|
||||
|
@ -400,21 +453,30 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
|
|||
}
|
||||
version = 1;
|
||||
/* 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 N: ... */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n",
|
||||
dbc->fname, version);
|
||||
LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded from HLR DB schema version %d to %d\n",
|
||||
dbc->fname, orig_version, version);
|
||||
}
|
||||
|
||||
if (version != CURRENT_SCHEMA_VERSION) {
|
||||
if (version < CURRENT_SCHEMA_VERSION) {
|
||||
LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
|
||||
if (!allow_upgrade) {
|
||||
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; "
|
||||
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
|
||||
"use the --db-upgrade option to allow HLR database upgrades\n",
|
||||
CURRENT_SCHEMA_VERSION);
|
||||
version, CURRENT_SCHEMA_VERSION);
|
||||
}
|
||||
} else
|
||||
LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
|
||||
|
|
13
src/db.h
13
src/db.h
|
@ -3,6 +3,8 @@
|
|||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
struct hlr;
|
||||
|
||||
enum stmt_idx {
|
||||
|
@ -26,6 +28,8 @@ enum stmt_idx {
|
|||
DB_STMT_AUC_3G_INSERT,
|
||||
DB_STMT_AUC_3G_DELETE,
|
||||
DB_STMT_SET_LAST_LU_SEEN,
|
||||
DB_STMT_UPD_RAT_FLAG,
|
||||
DB_STMT_RAT_BY_ID,
|
||||
_NUM_DB_STMT
|
||||
};
|
||||
|
||||
|
@ -85,8 +89,13 @@ struct hlr_subscriber {
|
|||
bool ms_purged_cs;
|
||||
bool ms_purged_ps;
|
||||
time_t last_lu_seen;
|
||||
bool rat_types[OSMO_RAT_COUNT];
|
||||
};
|
||||
|
||||
static const struct hlr_subscriber hlr_subscriber_empty = {
|
||||
.rat_types = { true, true, true },
|
||||
};
|
||||
|
||||
/* A format string for use with strptime(3). This format string is
|
||||
* used to parse the last_lu_seen column stored in the HLR database.
|
||||
* See https://sqlite.org/lang_datefunc.html, function datetime(). */
|
||||
|
@ -138,6 +147,10 @@ int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
|
|||
|
||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
|
||||
|
||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
|
||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
|
||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
|
||||
|
||||
/*! Call sqlite3_column_text() and copy result to a char[].
|
||||
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
|
||||
* \param[in] stmt An sqlite3_stmt*.
|
||||
|
|
121
src/db_hlr.c
121
src/db_hlr.c
|
@ -409,7 +409,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
|||
if (!subscr)
|
||||
goto out;
|
||||
|
||||
*subscr = (struct hlr_subscriber){};
|
||||
*subscr = hlr_subscriber_empty;
|
||||
|
||||
/* obtain the various columns */
|
||||
subscr->id = sqlite3_column_int64(stmt, 0);
|
||||
|
@ -444,6 +444,9 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
|||
out:
|
||||
db_remove_reset(stmt);
|
||||
|
||||
if (ret == 0)
|
||||
db_subscr_get_rat_types(dbc, subscr);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
*err = NULL;
|
||||
|
@ -782,3 +785,119 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
|
||||
{
|
||||
int rc;
|
||||
int ret = 0;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
|
||||
|
||||
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
|
||||
return -EIO;
|
||||
|
||||
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||
if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
|
||||
return -EIO;
|
||||
|
||||
if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
|
||||
return -EIO;
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
sqlite3_errmsg(dbc->db));
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify execution result */
|
||||
rc = sqlite3_changes(dbc->db);
|
||||
if (!rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
subscr_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
} else if (rc != 1) {
|
||||
LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
rc);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
int ret = 0;
|
||||
int i;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
|
||||
|
||||
if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < OSMO_RAT_COUNT; i++)
|
||||
subscr->rat_types[i] = true;
|
||||
|
||||
/* execute the statement */
|
||||
while (1) {
|
||||
enum osmo_rat_type rat;
|
||||
bool allowed;
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_DONE)
|
||||
break;
|
||||
if (rc != SQLITE_ROW)
|
||||
return -rc;
|
||||
|
||||
rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
|
||||
if (rc == -EINVAL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rat = rc;
|
||||
|
||||
allowed = sqlite3_column_int(stmt, 1);
|
||||
|
||||
subscr->rat_types[rat] = allowed;
|
||||
LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
|
||||
subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
|
||||
}
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
|
||||
{
|
||||
int rc;
|
||||
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||
|
||||
db_subscr_get_rat_types(hlr->dbc, subscr);
|
||||
|
||||
if (subscr->rat_types[rat] == allowed) {
|
||||
LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat));
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
|
||||
if (rc)
|
||||
return rc > 0? -rc : rc;
|
||||
|
||||
/* FIXME: If we're disabling, send message to VLR to detach subscriber */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
31
src/hlr.c
31
src/hlr.c
|
@ -263,6 +263,9 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
|||
{
|
||||
struct hlr_subscriber *subscr;
|
||||
struct lu_operation *luop = lu_op_alloc_conn(conn);
|
||||
int i;
|
||||
bool allowed;
|
||||
|
||||
if (!luop) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
|
||||
return -EINVAL;
|
||||
|
@ -308,6 +311,34 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
|
||||
* update' for the "No Suitable cells in location area" error code. */
|
||||
allowed = false;
|
||||
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' on %s sent RAT types: %zu\n", subscr->imsi,
|
||||
gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS ? "CS" : "PS", gsup->rat_types_len);
|
||||
for (i = 0; i < gsup->rat_types_len; i++) {
|
||||
enum osmo_rat_type rat = gsup->rat_types[i];
|
||||
if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
|
||||
lu_op_tx_error(luop, GMM_CAUSE_COND_IE_ERR);
|
||||
return 0;
|
||||
}
|
||||
if (luop->subscr.rat_types[rat]) {
|
||||
allowed = true;
|
||||
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' allowed on %s\n",
|
||||
subscr->imsi, osmo_rat_type_name(rat));
|
||||
} else {
|
||||
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' not allowed on %s\n",
|
||||
subscr->imsi, osmo_rat_type_name(rat));
|
||||
}
|
||||
}
|
||||
if (!allowed && gsup->rat_types_len > 0) {
|
||||
LOGP(DAUC, LOGL_DEBUG, "ISMI='%s' not allowed on %s%s\n",
|
||||
subscr->imsi, osmo_rat_type_name(gsup->rat_types[0]),
|
||||
gsup->rat_types_len > 1 ? " (nor on the other available RAT types)" : "");
|
||||
lu_op_tx_error(luop, GMM_CAUSE_NO_SUIT_CELL_IN_LA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include "hlr.h"
|
||||
#include "db.h"
|
||||
|
@ -49,6 +50,7 @@ static char *get_datestr(const time_t *t)
|
|||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
struct osmo_sub_auth_data aud2g;
|
||||
struct osmo_sub_auth_data aud3g;
|
||||
|
||||
|
@ -78,6 +80,12 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
|||
vty_out(vty, " PS purged%s", VTY_NEWLINE);
|
||||
if (subscr->last_lu_seen)
|
||||
vty_out(vty, " last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE);
|
||||
for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
|
||||
vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
if (subscr->ms_purged_cs)
|
||||
vty_out(vty, " CS purged%s", VTY_NEWLINE);
|
||||
|
||||
if (!*subscr->imsi)
|
||||
return;
|
||||
|
@ -506,6 +514,45 @@ DEFUN(subscriber_aud3g,
|
|||
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)
|
||||
{
|
||||
install_element_ve(&subscriber_show_cmd);
|
||||
|
@ -517,4 +564,5 @@ void hlr_vty_subscriber_init(void)
|
|||
install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_no_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;
|
||||
/*! CS (false) or PS (true) Location Update? */
|
||||
bool is_ps;
|
||||
/*! RAT type indicator: coming in on GERAN-A? UTRAN-Iu? */
|
||||
enum osmo_rat_type via_rat;
|
||||
|
||||
/*! currently running timer */
|
||||
struct osmo_timer_list timer;
|
||||
|
||||
|
|
Loading…
Reference in New Issue