db v6: determine 3G AUC IND from VLR name
Each VLR requesting auth tuples should use a distinct IND pool for 3G auth. So far we tied the IND to the GSUP peer connection; MSC and SGSN were always distinct GSUP peers, they ended up using distinct INDs. However, we have implemented a GSUP proxy, so that, in a distributed setup, a remotely roaming subscriber has only one direct GSUP peer proxying for both remote MSC and SGSN. That means as soon as a subscriber roams to a different site, we would use the GSUP proxy name to determine the IND instead of the separate MSC and SGSN. The site's MSC and SGSN appear as the same client, get the same IND bucket, waste SQNs rapidly and cause auth tuple generation load. So instead of using the local client as IND, persistently keep a list of VLR names and assign a different IND to each. Use the gsup_req->source_name as indicator, which reflects the actual remote VLR's name (remote MSC or SGSN). Persist the site <-> IND assignments in the database. Add an IND test to db_test.c There was an earlier patch version that separated the IND pools by cn_domain, but it turned out to add complex semantics, while only solving one aspect of the "adjacent VLR" problem. We need a solution not only for CS vs PS, but also for 2,3G vs 4G, and for sites that are physically adjacent to each other. This patch version does not offer any automatic solution for that -- as soon as more than 2^IND_bitlen (usually 32) VLRs show up, it is the responsibility of the admin to ensure the 'ind' table in the hlr.db does not have unfortunate IND assignments. So far no VTY commands exist for that, they may be added in the future. Related: OS#4319 Change-Id: I6f0a6bbef3a27507605c3b4a0e1a89bdfd468374
This commit is contained in:
parent
40d3a96f9d
commit
dd73ccfc99
|
@ -4,6 +4,7 @@
|
|||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
struct hlr;
|
||||
|
||||
|
@ -33,6 +34,9 @@ enum stmt_idx {
|
|||
DB_STMT_SET_LAST_LU_SEEN_PS,
|
||||
DB_STMT_EXISTS_BY_IMSI,
|
||||
DB_STMT_EXISTS_BY_MSISDN,
|
||||
DB_STMT_IND_ADD,
|
||||
DB_STMT_IND_SELECT,
|
||||
DB_STMT_IND_DEL,
|
||||
_NUM_DB_STMT
|
||||
};
|
||||
|
||||
|
@ -163,6 +167,9 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
|||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
|
||||
bool purge_val, bool is_ps);
|
||||
|
||||
int db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr, unsigned int *ind);
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr);
|
||||
|
||||
/*! 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*.
|
||||
|
|
10
sql/hlr.sql
10
sql/hlr.sql
|
@ -79,8 +79,16 @@ CREATE TABLE auc_3g (
|
|||
ind_bitlen INTEGER NOT NULL DEFAULT 5
|
||||
);
|
||||
|
||||
CREATE TABLE ind (
|
||||
-- 3G auth IND pool to be used for this VLR
|
||||
ind INTEGER PRIMARY KEY,
|
||||
-- VLR identification, usually the GSUP source_name
|
||||
vlr TEXT NOT NULL,
|
||||
UNIQUE (vlr)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||
|
||||
-- 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 = 5;
|
||||
PRAGMA user_version = 6;
|
||||
|
|
29
src/db.c
29
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 5
|
||||
#define CURRENT_SCHEMA_VERSION 6
|
||||
|
||||
#define SEL_COLUMNS \
|
||||
"id," \
|
||||
|
@ -85,6 +85,9 @@ static const char *stmt_sql[] = {
|
|||
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
|
||||
[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
|
||||
[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
|
||||
[DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)",
|
||||
[DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr",
|
||||
[DB_STMT_IND_DEL] = "DELETE FROM ind WHERE vlr = $vlr",
|
||||
};
|
||||
|
||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
|
||||
|
@ -479,6 +482,29 @@ static int db_upgrade_v5(struct db_context *dbc)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int db_upgrade_v6(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"CREATE TABLE ind (\n"
|
||||
" -- 3G auth IND pool to be used for this VLR\n"
|
||||
" ind INTEGER PRIMARY KEY,\n"
|
||||
" -- VLR identification, usually the GSUP source_name\n"
|
||||
" vlr TEXT NOT NULL,\n"
|
||||
" UNIQUE (vlr)\n"
|
||||
")"
|
||||
,
|
||||
"PRAGMA user_version = 6",
|
||||
};
|
||||
|
||||
rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 6\n");
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef int (*db_upgrade_func_t)(struct db_context *dbc);
|
||||
static db_upgrade_func_t db_upgrade_path[] = {
|
||||
db_upgrade_v1,
|
||||
|
@ -486,6 +512,7 @@ static db_upgrade_func_t db_upgrade_path[] = {
|
|||
db_upgrade_v3,
|
||||
db_upgrade_v4,
|
||||
db_upgrade_v5,
|
||||
db_upgrade_v6,
|
||||
};
|
||||
|
||||
static int db_get_user_version(struct db_context *dbc)
|
||||
|
|
103
src/db_hlr.c
103
src/db_hlr.c
|
@ -884,3 +884,106 @@ out:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _db_ind_run(struct db_context *dbc, sqlite3_stmt *stmt, const char *vlr, bool reset)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!db_bind_text(stmt, "$vlr", vlr))
|
||||
return -EIO;
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
if (reset)
|
||||
db_remove_reset(stmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _db_ind_add(struct db_context *dbc, const char *vlr)
|
||||
{
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_ADD];
|
||||
if (_db_ind_run(dbc, stmt, vlr, true) != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "Cannot create IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr, -1));
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _db_ind_del(struct db_context *dbc, const char *vlr)
|
||||
{
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_DEL];
|
||||
_db_ind_run(dbc, stmt, vlr, true);
|
||||
/* We don't really care about the result. If it didn't exist, then that was the goal anyway. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _db_ind_get(struct db_context *dbc, const char *vlr, unsigned int *ind)
|
||||
{
|
||||
int ret = 0;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_SELECT];
|
||||
int rc = _db_ind_run(dbc, stmt, vlr, false);
|
||||
if (rc == SQLITE_DONE) {
|
||||
/* Does not exist yet */
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
} else if (rc != SQLITE_ROW) {
|
||||
LOGP(DDB, LOGL_ERROR, "Error executing SQL: %d\n", rc);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ind);
|
||||
*ind = sqlite3_column_int64(stmt, 0);
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr,
|
||||
unsigned int *ind, bool del)
|
||||
{
|
||||
const char *vlr_name = NULL;
|
||||
int rc;
|
||||
|
||||
switch (vlr->type) {
|
||||
case OSMO_GSUP_PEER_ID_IPA_NAME:
|
||||
if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') {
|
||||
LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n",
|
||||
osmo_ipa_name_to_str(&vlr->ipa_name));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
vlr_name = (const char*)vlr->ipa_name.val;
|
||||
break;
|
||||
default:
|
||||
LOGP(DDB, LOGL_ERROR, "Unsupported osmo_gsup_peer_id type: %s\n",
|
||||
osmo_gsup_peer_id_type_name(vlr->type));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (del)
|
||||
return _db_ind_del(dbc, vlr_name);
|
||||
|
||||
rc = _db_ind_get(dbc, vlr_name, ind);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
/* Does not exist yet, create. */
|
||||
rc = _db_ind_add(dbc, vlr_name);
|
||||
if (rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "Error creating IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr_name, -1));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* To be sure, query again from scratch. */
|
||||
return _db_ind_get(dbc, vlr_name, ind);
|
||||
}
|
||||
|
||||
int db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr, unsigned int *ind)
|
||||
{
|
||||
return _db_ind(dbc, vlr, ind, false);
|
||||
}
|
||||
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr)
|
||||
{
|
||||
return _db_ind(dbc, vlr, NULL, true);
|
||||
}
|
||||
|
|
13
src/hlr.c
13
src/hlr.c
|
@ -280,13 +280,14 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
|||
***********************************************************************/
|
||||
|
||||
/* process an incoming SAI request */
|
||||
static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
||||
static int rx_send_auth_info(struct osmo_gsup_req *req)
|
||||
{
|
||||
struct osmo_gsup_message gsup_out = {
|
||||
.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
|
||||
};
|
||||
bool separation_bit = false;
|
||||
int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
|
||||
unsigned int auc_3g_ind;
|
||||
int rc;
|
||||
|
||||
subscr_create_on_demand(req->gsup.imsi);
|
||||
|
@ -297,6 +298,14 @@ static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
|||
if (req->gsup.num_auth_vectors > 0 &&
|
||||
req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
|
||||
num_auth_vectors = req->gsup.num_auth_vectors;
|
||||
rc = db_ind(g_hlr->dbc, &req->source_name, &auc_3g_ind);
|
||||
if (rc) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR,
|
||||
"Unable to determine 3G auth IND for source %s (rc=%d),"
|
||||
" generating tuples with IND = 0\n",
|
||||
osmo_gsup_peer_id_to_str(&req->source_name), rc);
|
||||
auc_3g_ind = 0;
|
||||
}
|
||||
|
||||
rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,
|
||||
gsup_out.auth_vectors,
|
||||
|
@ -516,7 +525,7 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
|
|||
switch (req->gsup.message_type) {
|
||||
/* requests sent to us */
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
|
||||
rx_send_auth_info(conn->auc_3g_ind, req);
|
||||
rx_send_auth_info(req);
|
||||
break;
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
|
||||
rx_upd_loc_req(conn, req);
|
||||
|
|
|
@ -918,6 +918,50 @@ static void test_subscr_sqn()
|
|||
comment_end();
|
||||
}
|
||||
|
||||
static void test_ind()
|
||||
{
|
||||
comment_start();
|
||||
|
||||
#define ASSERT_IND(VLR, IND) do { \
|
||||
unsigned int ind; \
|
||||
struct osmo_gsup_peer_id vlr; \
|
||||
OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
|
||||
ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \
|
||||
fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \
|
||||
if (ind != (IND)) \
|
||||
fprintf(stderr, " ERROR: expected " #IND "\n"); \
|
||||
} while (0)
|
||||
#define IND_DEL(VLR) do { \
|
||||
struct osmo_gsup_peer_id vlr; \
|
||||
OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
|
||||
ASSERT_RC(db_ind_del(dbc, &vlr), 0); \
|
||||
fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \
|
||||
} while (0)
|
||||
|
||||
ASSERT_IND("msc-23", 1);
|
||||
ASSERT_IND("sgsn-11", 2);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-0x17", 5);
|
||||
ASSERT_IND("sgsn-0xaa", 6);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-0x17", 5);
|
||||
ASSERT_IND("sgsn-0xaa", 6);
|
||||
ASSERT_IND("sgsn-0xbb", 7);
|
||||
ASSERT_IND("msc-0x2a", 8);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-23", 1);
|
||||
ASSERT_IND("sgsn-11", 2);
|
||||
|
||||
IND_DEL("msc-0x17"); /* dropped IND == 5 */
|
||||
ASSERT_IND("msc-0x2a", 8); /* known CS remains where it is */
|
||||
ASSERT_IND("any-unknown", 9); /* new VLR takes a new IND from the end */
|
||||
|
||||
comment_end();
|
||||
}
|
||||
|
||||
static struct {
|
||||
bool verbose;
|
||||
} cmdline_opts = {
|
||||
|
@ -998,6 +1042,7 @@ int main(int argc, char **argv)
|
|||
test_subscr_aud();
|
||||
test_subscr_aud_invalid_len();
|
||||
test_subscr_sqn();
|
||||
test_ind();
|
||||
|
||||
printf("Done\n");
|
||||
db_close(dbc);
|
||||
|
|
|
@ -1613,3 +1613,83 @@ db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
|
|||
|
||||
===== test_subscr_sqn: SUCCESS
|
||||
|
||||
|
||||
===== test_ind
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-23\0" ind = 1
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-11\0" ind = 2
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x17\0" ind = 5
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xaa\0" ind = 6
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x17\0" ind = 5
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xaa\0" ind = 6
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xbb\0" ind = 7
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x2a\0" ind = 8
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-23\0" ind = 1
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-11\0" ind = 2
|
||||
|
||||
db_ind_del(dbc, &vlr) --> 0
|
||||
|
||||
"msc-0x17\0" ind deleted
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x2a\0" ind = 8
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"any-unknown\0" ind = 9
|
||||
|
||||
===== test_ind: SUCCESS
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 2
|
|||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 3
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 4
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 5
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6
|
||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
|
||||
|
||||
Resulting db:
|
||||
|
@ -117,6 +118,13 @@ algo_id_3g|ind_bitlen|k|op|opc|sqn|subscriber_id
|
|||
5|5|44444444444444444444444444444444|44444444444444444444444444444444||0|5
|
||||
5|5|55555555555555555555555555555555||55555555555555555555555555555555|0|6
|
||||
|
||||
Table: ind
|
||||
name|type|notnull|dflt_value|pk
|
||||
ind|INTEGER|0||1
|
||||
vlr|TEXT|1||0
|
||||
|
||||
Table ind contents:
|
||||
|
||||
Table: subscriber
|
||||
name|type|notnull|dflt_value|pk
|
||||
ggsn_number|VARCHAR(15)|0||0
|
||||
|
@ -171,5 +179,5 @@ osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg
|
|||
rc = 0
|
||||
DMAIN hlr starting
|
||||
DDB using database: <PATH>test.db
|
||||
DDB Database <PATH>test.db' has HLR DB schema version 5
|
||||
DDB Database <PATH>test.db' has HLR DB schema version 6
|
||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
|
||||
|
|
Loading…
Reference in New Issue