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