aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am15
-rw-r--r--src/db_hlr.c4
-rw-r--r--src/hlr_db_tool.c425
-rw-r--r--src/logging.c6
4 files changed, 444 insertions, 6 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3b09b7b..81d4ee6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/db_hlr.c b/src/db_hlr.c
index e8db7d2..9be6fef 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.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)
diff --git a/src/hlr_db_tool.c b/src/hlr_db_tool.c
new file mode 100644
index 0000000..24fd857
--- /dev/null
+++ b/src/hlr_db_tool.c
@@ -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); }
diff --git a/src/logging.c b/src/logging.c
index 9b8de45..f81781d 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -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,
},
};