blob: d8a35848d65bf0b84a869cf5ebc5b34f87545d1d [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;
47} cmdline_opts = {
48 .db_file = "hlr.db",
49};
50
51static void print_help()
52{
53 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010054 printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020055 printf(" -l --database db-name The OsmoHLR database to use, default '%s'.\n",
56 cmdline_opts.db_file);
57 printf(" -h --help This text.\n");
58 printf(" -d option --debug=DMAIN:DDB:DAUC Enable debugging.\n");
59 printf(" -s --disable-color Do not print ANSI colors in the log\n");
60 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
61 printf(" -e --log-level number Set a global loglevel.\n");
62 printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
63 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010064 printf("Commands:\n");
65 printf("\n");
66 printf(" create Create an empty OsmoHLR database.\n");
67 printf(" (All commands imply this if none exists yet.)\n");
68 printf("\n");
Neels Hofmeyrc82e6ad2017-11-10 16:58:42 +010069 printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020070 printf(" Be aware that the import is lossy, only the\n");
71 printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
72}
73
74static void print_version(int print_copyright)
75{
76 printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION);
77 if (print_copyright)
78 printf("\n"
79 "Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n"
80 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n"
81 "This is free software: you are free to change and redistribute it.\n"
82 "There is NO WARRANTY, to the extent permitted by law.\n"
83 "\n");
84}
85
86static void handle_options(int argc, char **argv)
87{
88 const char *cmd;
89
90 while (1) {
91 int option_index = 0, c;
92 static struct option long_options[] = {
93 {"help", 0, 0, 'h'},
94 {"database", 1, 0, 'l'},
95 {"debug", 1, 0, 'd'},
96 {"disable-color", 0, 0, 's'},
97 {"timestamp", 0, 0, 'T'},
98 {"log-level", 1, 0, 'e'},
99 {"version", 0, 0, 'V' },
100 {0, 0, 0, 0}
101 };
102
103 c = getopt_long(argc, argv, "hl:d:sTe:V",
104 long_options, &option_index);
105 if (c == -1)
106 break;
107
108 switch (c) {
109 case 'h':
110 print_help();
111 exit(EXIT_SUCCESS);
112 case 'l':
113 cmdline_opts.db_file = optarg;
114 break;
115 case 'd':
116 log_parse_category_mask(osmo_stderr_target, optarg);
117 break;
118 case 's':
119 log_set_use_color(osmo_stderr_target, 0);
120 break;
121 case 'T':
122 log_set_print_timestamp(osmo_stderr_target, 1);
123 break;
124 case 'e':
125 log_set_log_level(osmo_stderr_target, atoi(optarg));
126 break;
127 case 'V':
128 print_version(1);
129 exit(EXIT_SUCCESS);
130 break;
131 default:
132 /* catch unknown options *as well as* missing arguments. */
133 fprintf(stderr, "Error in command line options. Exiting.\n");
134 exit(EXIT_FAILURE);
135 break;
136 }
137 }
138
139 if (argc - optind <= 0) {
140 fprintf(stderr, "Error: You must specify a command.\n");
141 print_help();
142 exit(EXIT_FAILURE);
143 }
144
145 cmd = argv[optind++];
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200146
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100147 if (!strcmp(cmd, "create")) {
148 /* Nothing to do, just run the main program to open the database without running any
149 * action, which will bootstrap all tables. */
150 } else if (!strcmp(cmd, "import-nitb-db")) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200151 if (argc - optind < 1) {
152 fprintf(stderr, "You must specify an input db file\n");
153 print_help();
154 exit(EXIT_FAILURE);
155 }
156 cmdline_opts.import_nitb_db = argv[optind++];
157 } else {
158 fprintf(stderr, "Error: Unknown command `%s'\n", cmd);
159 print_help();
160 exit(EXIT_FAILURE);
161 }
Neels Hofmeyr8db49062017-11-10 16:59:49 +0100162
163 if (argc - optind > 0) {
164 fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]);
165 print_help();
166 exit(EXIT_FAILURE);
167 }
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200168}
169
170static void signal_hdlr(int signal)
171{
172 switch (signal) {
173 case SIGINT:
174 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
175 db_close(g_hlr_db_tool_ctx->dbc);
176 log_fini();
177 talloc_report_full(g_hlr_db_tool_ctx, stderr);
178 exit(EXIT_SUCCESS);
179 break;
180 case SIGUSR1:
181 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
182 talloc_report_full(g_hlr_db_tool_ctx, stderr);
183 break;
184 }
185}
186
187sqlite3 *open_nitb_db(const char *filename)
188{
189 int rc;
190 sqlite3 *nitb_db = NULL;
191
192 rc = sqlite3_open(filename, &nitb_db);
193 if (rc != SQLITE_OK) {
194 LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc);
195 return NULL;
196 }
197
198 return nitb_db;
199}
200
201enum nitb_stmt {
202 NITB_SELECT_SUBSCR,
203 NITB_SELECT_AUTH_KEYS,
204};
205
206static const char *nitb_stmt_sql[] = {
207 [NITB_SELECT_SUBSCR] =
208 "SELECT imsi, id, extension, authorized"
209 " FROM Subscriber"
210 " ORDER BY id",
211 [NITB_SELECT_AUTH_KEYS] =
212 "SELECT algorithm_id, a3a8_ki from authkeys"
213 " WHERE subscriber_id = $subscr_id",
214};
215
216sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
217
218size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
219
220void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
221{
222 int rc;
223 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
224 sqlite3_stmt *stmt;
225
226 int count = 0;
227
228 stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS];
229 if (!db_bind_int(stmt, NULL, nitb_id))
230 return;
231
232 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
233 const void *blob;
234 unsigned int blob_size;
235 static unsigned char buf[4096];
236 static char ki[128];
237 int decoded_size;
238 struct sub_auth_data_str aud2g = {
239 .type = OSMO_AUTH_TYPE_GSM,
240 .algo = OSMO_AUTH_ALG_NONE,
241 .u.gsm.ki = ki,
242 };
243
244 aud2g.algo = sqlite3_column_int(stmt, 0);
245
246 if (count) {
247 LOGP(DDB, LOGL_ERROR,
248 "Warning: subscriber has more than one auth key,"
249 " importing only the first key, for IMSI=%s\n",
250 imsi);
251 break;
252 }
253
254 blob = sqlite3_column_blob(stmt, 1);
255 blob_size = sqlite3_column_bytes(stmt, 1);
256
257 if (blob_size > sizeof(buf)) {
258 LOGP(DDB, LOGL_ERROR,
259 "OsmoNITB import to %s: Cannot import auth data for IMSI %s:"
260 " too large blob: %u\n",
261 dbc->fname, imsi, blob_size);
262 db_remove_reset(stmt);
263 continue;
264 }
265
266 decoded_size = _dbd_decode_binary(blob, buf);
267 osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki));
268
269 db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g);
270 count ++;
271 }
272
273 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
274 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
275 " during stmt '%s'",
276 rc, sqlite3_errmsg(nitb_db),
277 nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]);
278 }
279
280 db_remove_reset(stmt);
281}
282
283void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
284{
285 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
286 int rc;
287 struct hlr_subscriber subscr;
288
289 int64_t nitb_id;
290 int64_t imsi;
291 char imsi_str[32];
292 bool authorized;
293
294 imsi = sqlite3_column_int64(stmt, 0);
295
296 snprintf(imsi_str, sizeof(imsi_str), "%"PRId64, imsi);
297
298 rc = db_subscr_create(dbc, imsi_str);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100299 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200300 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
301 dbc->fname,
302 imsi_str,
303 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100304 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200305 /* on error, still attempt to continue */
306 }
307
308 nitb_id = sqlite3_column_int64(stmt, 1);
309 copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
310 authorized = sqlite3_column_int(stmt, 3) ? true : false;
311
312 db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
313 db_subscr_nam(dbc, imsi_str, authorized, true);
314 db_subscr_nam(dbc, imsi_str, authorized, false);
315
316 /* find the just created id */
317 rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100318 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200319 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s,"
320 " but failed to get new subscriber id: %d: %s\n",
321 dbc->fname,
322 imsi_str,
323 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100324 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200325 return;
326 }
327
328 OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi));
329
330 import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id);
331}
332
333int import_nitb_db(void)
334{
335 int i;
336 int ret;
337 int rc;
338 const char *sql;
339 sqlite3_stmt *stmt;
340
341 sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db);
342
343 if (!nitb_db)
344 return -1;
345 ret = 0;
346
347 for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) {
348 sql = nitb_stmt_sql[i];
349 rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL);
350 if (rc != SQLITE_OK) {
351 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql);
352 ret = -1;
353 goto out_free;
354 }
355 }
356
357 stmt = nitb_stmt[NITB_SELECT_SUBSCR];
358
359 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
360 import_nitb_subscr(nitb_db, stmt);
361 /* On failure, carry on with the rest. */
362 }
363 if (rc != SQLITE_DONE) {
364 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
365 " during stmt '%s'",
366 rc, sqlite3_errmsg(nitb_db),
367 nitb_stmt_sql[NITB_SELECT_SUBSCR]);
368 goto out_free;
369 }
370
371 db_remove_reset(stmt);
372 sqlite3_finalize(stmt);
373
374out_free:
375 sqlite3_close(nitb_db);
376 return ret;
377}
378
379int main(int argc, char **argv)
380{
381 int rc;
382 int (*main_action)(void);
383 main_action = NULL;
384
385 g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
386 OSMO_ASSERT(g_hlr_db_tool_ctx);
387 talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool");
388
389 rc = osmo_init_logging(&hlr_log_info);
390 if (rc < 0) {
391 fprintf(stderr, "Error initializing logging\n");
392 exit(EXIT_FAILURE);
393 }
394
395 handle_options(argc, argv);
396
397 if (cmdline_opts.import_nitb_db) {
398 if (main_action)
399 goto too_many_actions;
400 main_action = import_nitb_db;
401 }
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100402 /* Future: add more main_actions, besides import-nitb-db, here.
403 * For command 'create', no action is required. */
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200404
405 /* Just in case any db actions need randomness */
406 rc = rand_init();
407 if (rc < 0) {
408 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
409 exit(EXIT_FAILURE);
410 }
411
412 g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file);
413 if (!g_hlr_db_tool_ctx->dbc) {
414 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
415 exit(EXIT_FAILURE);
416 }
417
418 osmo_init_ignore_signals();
419 signal(SIGINT, &signal_hdlr);
420 signal(SIGUSR1, &signal_hdlr);
421
422 rc = 0;
423 if (main_action)
424 rc = (*main_action)();
425
426 db_close(g_hlr_db_tool_ctx->dbc);
427 log_fini();
428 exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
429
430too_many_actions:
431 fprintf(stderr, "Too many actions requested.\n");
432 log_fini();
433 exit(EXIT_FAILURE);
434}
435
436/* stubs */
437void lu_op_alloc_conn(void) { OSMO_ASSERT(0); }
438void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); }
439void lu_op_free(void) { OSMO_ASSERT(0); }