add osmo-hlr-db-tool
Change-Id: I0dfa6ec033dd93161c1adc2ce1637195fe5b7a63
This commit is contained in:
parent
8f063796e9
commit
9d99d99137
|
@ -35,6 +35,7 @@ noinst_HEADERS = \
|
|||
|
||||
bin_PROGRAMS = \
|
||||
osmo-hlr \
|
||||
osmo-hlr-db-tool \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
|
@ -66,6 +67,20 @@ osmo_hlr_LDADD = \
|
|||
$(SQLITE3_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_db_tool_SOURCES = \
|
||||
hlr_db_tool.c \
|
||||
db.c \
|
||||
db_hlr.c \
|
||||
logging.c \
|
||||
rand_urandom.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_db_tool_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(SQLITE3_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
db_test_SOURCES = \
|
||||
auc.c \
|
||||
db.c \
|
||||
|
|
|
@ -38,9 +38,7 @@
|
|||
#define SL3_TXT(x, stmt, idx) \
|
||||
do { \
|
||||
const char *_txt = (const char *) sqlite3_column_text(stmt, idx);\
|
||||
if (_txt) \
|
||||
strncpy(x, _txt, sizeof(x)); \
|
||||
x[sizeof(x)-1] = '\0'; \
|
||||
osmo_strlcpy(x, _txt, sizeof(x)); \
|
||||
} while (0)
|
||||
|
||||
int db_subscr_create(struct db_context *dbc, const char *imsi)
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "db.h"
|
||||
#include "rand.h"
|
||||
|
||||
struct hlr_db_tool_ctx {
|
||||
/* DB context */
|
||||
struct db_context *dbc;
|
||||
};
|
||||
|
||||
struct hlr_db_tool_ctx *g_hlr_db_tool_ctx;
|
||||
|
||||
static struct {
|
||||
const char *db_file;
|
||||
bool bootstrap;
|
||||
const char *import_nitb_db;
|
||||
} cmdline_opts = {
|
||||
.db_file = "hlr.db",
|
||||
};
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] --import-nitb-db <nitb.db>\n");
|
||||
printf(" -l --database db-name The OsmoHLR database to use, default '%s'.\n",
|
||||
cmdline_opts.db_file);
|
||||
printf(" -n --import-nitb-db db Add OsmoNITB db's subscribers to OsmoHLR db.\n");
|
||||
printf(" Be aware that the import is lossy, only the\n");
|
||||
printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
|
||||
printf(" -h --help This text.\n");
|
||||
printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
|
||||
printf(" -s --disable-color Do not print ANSI colors in the log\n");
|
||||
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
|
||||
printf(" -e --log-level number Set a global loglevel.\n");
|
||||
printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
|
||||
}
|
||||
|
||||
static void print_version(int print_copyright)
|
||||
{
|
||||
printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION);
|
||||
if (print_copyright)
|
||||
printf("\n"
|
||||
"Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"database", 1, 0, 'l'},
|
||||
{"import-nitb-db", 1, 0, 'n'},
|
||||
{"debug", 1, 0, 'd'},
|
||||
{"disable-color", 0, 0, 's'},
|
||||
{"timestamp", 0, 0, 'T'},
|
||||
{"log-level", 1, 0, 'e'},
|
||||
{"version", 0, 0, 'V' },
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hl:n:d:sTe:V",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
case 'l':
|
||||
cmdline_opts.db_file = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
cmdline_opts.import_nitb_db = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'T':
|
||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||
break;
|
||||
case 'e':
|
||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
/* catch unknown options *as well as* missing arguments. */
|
||||
fprintf(stderr, "Error in command line options. Exiting.\n");
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_hdlr(int signal)
|
||||
{
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
|
||||
db_close(g_hlr_db_tool_ctx->dbc);
|
||||
log_fini();
|
||||
talloc_report_full(g_hlr_db_tool_ctx, stderr);
|
||||
exit(0);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
|
||||
talloc_report_full(g_hlr_db_tool_ctx, stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 *open_nitb_db(const char *filename)
|
||||
{
|
||||
int rc;
|
||||
sqlite3 *nitb_db = NULL;
|
||||
|
||||
rc = sqlite3_open(filename, &nitb_db);
|
||||
if (rc != SQLITE_OK) {
|
||||
LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return nitb_db;
|
||||
}
|
||||
|
||||
enum nitb_stmt {
|
||||
NITB_SELECT_SUBSCR,
|
||||
NITB_SELECT_AUTH_KEYS,
|
||||
NITB_SELECT_IMEI,
|
||||
};
|
||||
|
||||
static const char *nitb_stmt_sql[] = {
|
||||
[NITB_SELECT_SUBSCR] =
|
||||
"SELECT imsi, id, extension, authorized"
|
||||
" FROM Subscriber"
|
||||
" ORDER BY id",
|
||||
[NITB_SELECT_AUTH_KEYS] =
|
||||
"SELECT algorithm_id, a3a8_ki from authkeys"
|
||||
" WHERE subscriber_id = $subscr_id",
|
||||
[NITB_SELECT_IMEI] =
|
||||
"SELECT imei"
|
||||
" FROM Equipment"
|
||||
" WHERE id = $subscr_id",
|
||||
};
|
||||
|
||||
sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
|
||||
|
||||
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out){
|
||||
int i, e;
|
||||
unsigned char c;
|
||||
e = *(in++);
|
||||
i = 0;
|
||||
while( (c = *(in++))!=0 ){
|
||||
if( c==1 ){
|
||||
c = *(in++) - 1;
|
||||
}
|
||||
out[i++] = c + e;
|
||||
}
|
||||
return (size_t)i;
|
||||
}
|
||||
|
||||
#define SL3_TXT(x, stmt, idx) \
|
||||
do { \
|
||||
const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
|
||||
osmo_strlcpy(x, _txt, sizeof(x)); \
|
||||
} while (0)
|
||||
|
||||
void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
|
||||
{
|
||||
int rc;
|
||||
struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
int count = 0;
|
||||
|
||||
stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS];
|
||||
if (!db_bind_int(stmt, NULL, nitb_id))
|
||||
return;
|
||||
|
||||
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
const void *blob;
|
||||
unsigned int blob_size;
|
||||
static unsigned char buf[4096];
|
||||
static char ki[128];
|
||||
int decoded_size;
|
||||
struct sub_auth_data_str aud2g = {
|
||||
.type = OSMO_AUTH_TYPE_GSM,
|
||||
.algo = OSMO_AUTH_ALG_NONE,
|
||||
.u.gsm.ki = ki,
|
||||
};
|
||||
|
||||
aud2g.algo = sqlite3_column_int(stmt, 0);
|
||||
|
||||
if (count) {
|
||||
LOGP(DDB, LOGL_ERROR,
|
||||
"Warning: subscriber has more than one auth key,"
|
||||
" importing only the first key, for IMSI=%s\n",
|
||||
imsi);
|
||||
break;
|
||||
}
|
||||
|
||||
blob = sqlite3_column_blob(stmt, 1);
|
||||
blob_size = sqlite3_column_bytes(stmt, 1);
|
||||
|
||||
if (blob_size > sizeof(buf)) {
|
||||
LOGP(DDB, LOGL_ERROR,
|
||||
"OsmoNITB import to %s: Cannot import auth data for IMSI %s:"
|
||||
" too large blob: %u\n",
|
||||
dbc->fname, imsi, blob_size);
|
||||
db_remove_reset(stmt);
|
||||
continue;
|
||||
}
|
||||
|
||||
decoded_size = _dbd_decode_binary(blob, buf);
|
||||
osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki));
|
||||
|
||||
db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g);
|
||||
count ++;
|
||||
}
|
||||
|
||||
if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
|
||||
" during stmt '%s'",
|
||||
rc, sqlite3_errmsg(nitb_db),
|
||||
nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]);
|
||||
}
|
||||
|
||||
db_remove_reset(stmt);
|
||||
}
|
||||
|
||||
void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
|
||||
{
|
||||
struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
|
||||
int rc;
|
||||
struct hlr_subscriber subscr;
|
||||
|
||||
int64_t nitb_id;
|
||||
int64_t imsi;
|
||||
char imsi_str[32];
|
||||
bool authorized;
|
||||
|
||||
imsi = sqlite3_column_int64(stmt, 0);
|
||||
|
||||
snprintf(imsi_str, sizeof(imsi_str), "%"PRId64, imsi);
|
||||
|
||||
rc = db_subscr_create(dbc, imsi_str);
|
||||
if (rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
|
||||
dbc->fname,
|
||||
imsi_str,
|
||||
rc,
|
||||
strerror(rc));
|
||||
/* on error, still attempt to continue */
|
||||
}
|
||||
|
||||
nitb_id = sqlite3_column_int64(stmt, 1);
|
||||
SL3_TXT(subscr.msisdn, stmt, 2);
|
||||
authorized = sqlite3_column_int(stmt, 3) ? true : false;
|
||||
|
||||
db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
|
||||
db_subscr_nam(dbc, imsi_str, authorized, true);
|
||||
db_subscr_nam(dbc, imsi_str, authorized, false);
|
||||
|
||||
/* find the just created id */
|
||||
rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr);
|
||||
if (rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s,"
|
||||
" but failed to get new subscriber id: %d: %s\n",
|
||||
dbc->fname,
|
||||
imsi_str,
|
||||
rc,
|
||||
strerror(rc));
|
||||
return;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi));
|
||||
|
||||
import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id);
|
||||
}
|
||||
|
||||
int import_nitb_db(void)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int rc;
|
||||
const char *sql;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db);
|
||||
|
||||
if (!nitb_db)
|
||||
return -1;
|
||||
ret = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) {
|
||||
sql = nitb_stmt_sql[i];
|
||||
rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql);
|
||||
ret = -1;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
stmt = nitb_stmt[NITB_SELECT_SUBSCR];
|
||||
|
||||
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
import_nitb_subscr(nitb_db, stmt);
|
||||
/* On failure, carry on with the rest. */
|
||||
}
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
|
||||
" during stmt '%s'",
|
||||
rc, sqlite3_errmsg(nitb_db),
|
||||
nitb_stmt_sql[NITB_SELECT_SUBSCR]);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
db_remove_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
out_free:
|
||||
sqlite3_close(nitb_db);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
int (*main_action)(void);
|
||||
main_action = NULL;
|
||||
|
||||
g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
|
||||
OSMO_ASSERT(g_hlr_db_tool_ctx);
|
||||
talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool");
|
||||
|
||||
rc = osmo_init_logging(&hlr_log_info);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing logging\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
if (cmdline_opts.import_nitb_db) {
|
||||
if (main_action)
|
||||
goto too_many_actions;
|
||||
main_action = import_nitb_db;
|
||||
}
|
||||
|
||||
/* Just in case any db actions need randomness */
|
||||
rc = rand_init();
|
||||
if (rc < 0) {
|
||||
LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file);
|
||||
if (!g_hlr_db_tool_ctx->dbc) {
|
||||
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
signal(SIGINT, &signal_hdlr);
|
||||
signal(SIGUSR1, &signal_hdlr);
|
||||
|
||||
rc = 0;
|
||||
if (main_action)
|
||||
rc = (*main_action)();
|
||||
|
||||
db_close(g_hlr_db_tool_ctx->dbc);
|
||||
log_fini();
|
||||
exit(rc);
|
||||
|
||||
too_many_actions:
|
||||
fprintf(stderr, "Too many actions requested.\n");
|
||||
log_fini();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* stubs */
|
||||
void lu_op_alloc_conn(void) { OSMO_ASSERT(0); }
|
||||
void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); }
|
||||
void lu_op_free(void) { OSMO_ASSERT(0); }
|
|
@ -5,19 +5,19 @@ const struct log_info_cat hlr_log_info_cat[] = {
|
|||
[DMAIN] = {
|
||||
.name = "DMAIN",
|
||||
.description = "Main Program",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DDB] = {
|
||||
.name = "DDB",
|
||||
.description = "Database Layer",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DAUC] = {
|
||||
.name = "DAUC",
|
||||
.description = "Authentication Center",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue