blob: fcef597a998db0fe84186b02d84be594b51f153a [file] [log] [blame]
Neels Hofmeyr73d14af2017-10-24 23:26:53 +02001/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
2 *
3 * All Rights Reserved
4 *
5 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <stdlib.h>
23#include <signal.h>
24#include <stdio.h>
25#include <getopt.h>
26#include <inttypes.h>
27#include <string.h>
28
29#include <osmocom/core/logging.h>
30#include <osmocom/core/application.h>
31
32#include "logging.h"
33#include "db.h"
34#include "rand.h"
35
36struct hlr_db_tool_ctx {
37 /* DB context */
38 struct db_context *dbc;
39};
40
41struct hlr_db_tool_ctx *g_hlr_db_tool_ctx;
42
43static struct {
44 const char *db_file;
45 bool bootstrap;
46 const char *import_nitb_db;
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010047 bool db_upgrade;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020048} cmdline_opts = {
49 .db_file = "hlr.db",
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010050 .db_upgrade = false,
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020051};
52
53static void print_help()
54{
55 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010056 printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020057 printf(" -l --database db-name The OsmoHLR database to use, default '%s'.\n",
58 cmdline_opts.db_file);
59 printf(" -h --help This text.\n");
60 printf(" -d option --debug=DMAIN:DDB:DAUC Enable debugging.\n");
61 printf(" -s --disable-color Do not print ANSI colors in the log\n");
62 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
63 printf(" -e --log-level number Set a global loglevel.\n");
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010064 printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020065 printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
66 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010067 printf("Commands:\n");
68 printf("\n");
69 printf(" create Create an empty OsmoHLR database.\n");
70 printf(" (All commands imply this if none exists yet.)\n");
71 printf("\n");
Neels Hofmeyrc82e6ad2017-11-10 16:58:42 +010072 printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020073 printf(" Be aware that the import is lossy, only the\n");
74 printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
75}
76
77static void print_version(int print_copyright)
78{
79 printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION);
80 if (print_copyright)
81 printf("\n"
82 "Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n"
83 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n"
84 "This is free software: you are free to change and redistribute it.\n"
85 "There is NO WARRANTY, to the extent permitted by law.\n"
86 "\n");
87}
88
89static void handle_options(int argc, char **argv)
90{
91 const char *cmd;
92
93 while (1) {
94 int option_index = 0, c;
95 static struct option long_options[] = {
96 {"help", 0, 0, 'h'},
97 {"database", 1, 0, 'l'},
98 {"debug", 1, 0, 'd'},
99 {"disable-color", 0, 0, 's'},
100 {"timestamp", 0, 0, 'T'},
101 {"log-level", 1, 0, 'e'},
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100102 {"db-upgrade", 0, 0, 'U' },
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200103 {"version", 0, 0, 'V' },
104 {0, 0, 0, 0}
105 };
106
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100107 c = getopt_long(argc, argv, "hl:d:sTe:UV",
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200108 long_options, &option_index);
109 if (c == -1)
110 break;
111
112 switch (c) {
113 case 'h':
114 print_help();
115 exit(EXIT_SUCCESS);
116 case 'l':
117 cmdline_opts.db_file = optarg;
118 break;
119 case 'd':
120 log_parse_category_mask(osmo_stderr_target, optarg);
121 break;
122 case 's':
123 log_set_use_color(osmo_stderr_target, 0);
124 break;
125 case 'T':
126 log_set_print_timestamp(osmo_stderr_target, 1);
127 break;
128 case 'e':
129 log_set_log_level(osmo_stderr_target, atoi(optarg));
130 break;
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100131 case 'U':
132 cmdline_opts.db_upgrade = true;
133 break;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200134 case 'V':
135 print_version(1);
136 exit(EXIT_SUCCESS);
137 break;
138 default:
139 /* catch unknown options *as well as* missing arguments. */
140 fprintf(stderr, "Error in command line options. Exiting.\n");
141 exit(EXIT_FAILURE);
142 break;
143 }
144 }
145
146 if (argc - optind <= 0) {
147 fprintf(stderr, "Error: You must specify a command.\n");
148 print_help();
149 exit(EXIT_FAILURE);
150 }
151
152 cmd = argv[optind++];
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200153
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100154 if (!strcmp(cmd, "create")) {
155 /* Nothing to do, just run the main program to open the database without running any
156 * action, which will bootstrap all tables. */
157 } else if (!strcmp(cmd, "import-nitb-db")) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200158 if (argc - optind < 1) {
159 fprintf(stderr, "You must specify an input db file\n");
160 print_help();
161 exit(EXIT_FAILURE);
162 }
163 cmdline_opts.import_nitb_db = argv[optind++];
164 } else {
165 fprintf(stderr, "Error: Unknown command `%s'\n", cmd);
166 print_help();
167 exit(EXIT_FAILURE);
168 }
Neels Hofmeyr8db49062017-11-10 16:59:49 +0100169
170 if (argc - optind > 0) {
171 fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]);
172 print_help();
173 exit(EXIT_FAILURE);
174 }
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200175}
176
177static void signal_hdlr(int signal)
178{
179 switch (signal) {
180 case SIGINT:
181 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
182 db_close(g_hlr_db_tool_ctx->dbc);
183 log_fini();
184 talloc_report_full(g_hlr_db_tool_ctx, stderr);
185 exit(EXIT_SUCCESS);
186 break;
187 case SIGUSR1:
188 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
189 talloc_report_full(g_hlr_db_tool_ctx, stderr);
190 break;
191 }
192}
193
194sqlite3 *open_nitb_db(const char *filename)
195{
196 int rc;
197 sqlite3 *nitb_db = NULL;
198
199 rc = sqlite3_open(filename, &nitb_db);
200 if (rc != SQLITE_OK) {
201 LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc);
202 return NULL;
203 }
204
205 return nitb_db;
206}
207
208enum nitb_stmt {
209 NITB_SELECT_SUBSCR,
210 NITB_SELECT_AUTH_KEYS,
211};
212
213static const char *nitb_stmt_sql[] = {
214 [NITB_SELECT_SUBSCR] =
215 "SELECT imsi, id, extension, authorized"
216 " FROM Subscriber"
217 " ORDER BY id",
218 [NITB_SELECT_AUTH_KEYS] =
219 "SELECT algorithm_id, a3a8_ki from authkeys"
220 " WHERE subscriber_id = $subscr_id",
221};
222
223sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
224
225size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
226
227void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
228{
229 int rc;
230 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
231 sqlite3_stmt *stmt;
232
233 int count = 0;
234
235 stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS];
236 if (!db_bind_int(stmt, NULL, nitb_id))
237 return;
238
239 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
240 const void *blob;
241 unsigned int blob_size;
242 static unsigned char buf[4096];
243 static char ki[128];
244 int decoded_size;
245 struct sub_auth_data_str aud2g = {
246 .type = OSMO_AUTH_TYPE_GSM,
247 .algo = OSMO_AUTH_ALG_NONE,
248 .u.gsm.ki = ki,
249 };
250
251 aud2g.algo = sqlite3_column_int(stmt, 0);
252
253 if (count) {
254 LOGP(DDB, LOGL_ERROR,
255 "Warning: subscriber has more than one auth key,"
256 " importing only the first key, for IMSI=%s\n",
257 imsi);
258 break;
259 }
260
261 blob = sqlite3_column_blob(stmt, 1);
262 blob_size = sqlite3_column_bytes(stmt, 1);
263
264 if (blob_size > sizeof(buf)) {
265 LOGP(DDB, LOGL_ERROR,
266 "OsmoNITB import to %s: Cannot import auth data for IMSI %s:"
267 " too large blob: %u\n",
268 dbc->fname, imsi, blob_size);
269 db_remove_reset(stmt);
270 continue;
271 }
272
273 decoded_size = _dbd_decode_binary(blob, buf);
274 osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki));
275
276 db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g);
277 count ++;
278 }
279
280 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
281 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
282 " during stmt '%s'",
283 rc, sqlite3_errmsg(nitb_db),
284 nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]);
285 }
286
287 db_remove_reset(stmt);
288}
289
290void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
291{
292 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
293 int rc;
294 struct hlr_subscriber subscr;
295
296 int64_t nitb_id;
297 int64_t imsi;
298 char imsi_str[32];
299 bool authorized;
300
301 imsi = sqlite3_column_int64(stmt, 0);
302
Stefan Sperling705b61b2018-12-07 12:44:50 +0100303 snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200304
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100305 rc = db_subscr_create(dbc, imsi_str, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100306 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200307 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
308 dbc->fname,
309 imsi_str,
310 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100311 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200312 /* on error, still attempt to continue */
313 }
314
315 nitb_id = sqlite3_column_int64(stmt, 1);
316 copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
317 authorized = sqlite3_column_int(stmt, 3) ? true : false;
318
319 db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
320 db_subscr_nam(dbc, imsi_str, authorized, true);
321 db_subscr_nam(dbc, imsi_str, authorized, false);
322
323 /* find the just created id */
324 rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100325 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200326 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s,"
327 " but failed to get new subscriber id: %d: %s\n",
328 dbc->fname,
329 imsi_str,
330 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100331 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200332 return;
333 }
334
335 OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi));
336
337 import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id);
338}
339
340int import_nitb_db(void)
341{
342 int i;
343 int ret;
344 int rc;
345 const char *sql;
346 sqlite3_stmt *stmt;
347
348 sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db);
349
350 if (!nitb_db)
351 return -1;
352 ret = 0;
353
354 for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) {
355 sql = nitb_stmt_sql[i];
356 rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL);
357 if (rc != SQLITE_OK) {
358 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql);
359 ret = -1;
360 goto out_free;
361 }
362 }
363
364 stmt = nitb_stmt[NITB_SELECT_SUBSCR];
365
366 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
367 import_nitb_subscr(nitb_db, stmt);
368 /* On failure, carry on with the rest. */
369 }
370 if (rc != SQLITE_DONE) {
371 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
372 " during stmt '%s'",
373 rc, sqlite3_errmsg(nitb_db),
374 nitb_stmt_sql[NITB_SELECT_SUBSCR]);
375 goto out_free;
376 }
377
378 db_remove_reset(stmt);
379 sqlite3_finalize(stmt);
380
381out_free:
382 sqlite3_close(nitb_db);
383 return ret;
384}
385
386int main(int argc, char **argv)
387{
388 int rc;
389 int (*main_action)(void);
390 main_action = NULL;
391
392 g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
393 OSMO_ASSERT(g_hlr_db_tool_ctx);
394 talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool");
395
Pau Espin Pedrol51530312018-04-17 15:07:06 +0200396 rc = osmo_init_logging2(g_hlr_db_tool_ctx, &hlr_log_info);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200397 if (rc < 0) {
398 fprintf(stderr, "Error initializing logging\n");
399 exit(EXIT_FAILURE);
400 }
401
402 handle_options(argc, argv);
403
404 if (cmdline_opts.import_nitb_db) {
405 if (main_action)
406 goto too_many_actions;
407 main_action = import_nitb_db;
408 }
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100409 /* Future: add more main_actions, besides import-nitb-db, here.
410 * For command 'create', no action is required. */
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200411
412 /* Just in case any db actions need randomness */
413 rc = rand_init();
414 if (rc < 0) {
415 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
416 exit(EXIT_FAILURE);
417 }
418
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100419 g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200420 if (!g_hlr_db_tool_ctx->dbc) {
421 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
422 exit(EXIT_FAILURE);
423 }
424
425 osmo_init_ignore_signals();
426 signal(SIGINT, &signal_hdlr);
427 signal(SIGUSR1, &signal_hdlr);
428
429 rc = 0;
430 if (main_action)
431 rc = (*main_action)();
432
433 db_close(g_hlr_db_tool_ctx->dbc);
434 log_fini();
435 exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
436
437too_many_actions:
438 fprintf(stderr, "Too many actions requested.\n");
439 log_fini();
440 exit(EXIT_FAILURE);
441}
442
443/* stubs */
444void lu_op_alloc_conn(void) { OSMO_ASSERT(0); }
445void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); }
446void lu_op_free(void) { OSMO_ASSERT(0); }