blob: 4fbc2688ba2418bc251104ef6a02acb8207d5a11 [file] [log] [blame]
Harald Welte936f6722016-05-03 18:51:18 +02001/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
Harald Welteaabae9e2016-04-28 12:48:14 +020020#include <signal.h>
Harald Weltee687be52016-05-03 18:49:27 +020021#include <errno.h>
Maxea8b0d42017-02-14 16:53:04 +010022#include <stdbool.h>
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +010023#include <getopt.h>
24
Harald Weltee72cf552016-04-28 07:18:49 +020025#include <osmocom/core/msgb.h>
26#include <osmocom/core/logging.h>
27#include <osmocom/core/application.h>
28#include <osmocom/gsm/gsup.h>
Max2fc63a62016-12-20 16:49:20 +010029#include <osmocom/gsm/apn.h>
Neels Hofmeyr627de842016-12-19 13:16:06 +010030#include <osmocom/gsm/gsm48_ie.h>
Neels Hofmeyr7685a782017-01-30 23:30:26 +010031#include <osmocom/vty/vty.h>
32#include <osmocom/vty/command.h>
33#include <osmocom/vty/telnet_interface.h>
34#include <osmocom/vty/ports.h>
Max372868b2017-03-02 12:12:00 +010035#include <osmocom/ctrl/control_vty.h>
Harald Weltee72cf552016-04-28 07:18:49 +020036
37#include "db.h"
Maxd4bebbd2017-03-02 12:00:19 +010038#include "hlr.h"
Max372868b2017-03-02 12:12:00 +010039#include "ctrl.h"
Harald Weltee72cf552016-04-28 07:18:49 +020040#include "logging.h"
41#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020042#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020043#include "rand.h"
Maxea8b0d42017-02-14 16:53:04 +010044#include "luop.h"
Neels Hofmeyr7685a782017-01-30 23:30:26 +010045#include "hlr_vty.h"
Harald Weltee72cf552016-04-28 07:18:49 +020046
Maxd4bebbd2017-03-02 12:00:19 +010047static struct hlr *g_hlr;
Harald Weltee72cf552016-04-28 07:18:49 +020048
Stefan Sperlingf1622522018-04-09 11:39:16 +020049/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
50 *
51 * FIXME: In order to support large-scale networks this function should skip
52 * VLRs/SGSNs which do not currently serve the subscriber.
53 *
54 * \param[in] subscr A subscriber we have new data to send for.
55 */
56void
57osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
58{
59 struct osmo_gsup_conn *co;
60
61 if (g_hlr->gs == NULL)
62 return;
63
64 llist_for_each_entry(co, &g_hlr->gs->clients, list) {
65 struct lu_operation *luop = lu_op_alloc_conn(co);
66 if (!luop) {
67 LOGP(DMAIN, LOGL_ERROR,
68 "IMSI='%s': Cannot notify GSUP client, cannot allocate lu_operation,"
69 " for %s:%u\n", subscr->imsi,
70 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
71 co && co->conn && co->conn->server? co->conn->server->port : 0);
72 continue;
73 }
74 luop->subscr = *subscr;
75 luop->state = LU_S_LU_RECEIVED; /* Pretend we received a location update. */
76 lu_op_tx_insert_subscr_data(luop);
77 lu_op_free(luop);
78 }
79}
80
Harald Weltee687be52016-05-03 18:49:27 +020081/***********************************************************************
82 * Send Auth Info handling
83 ***********************************************************************/
84
Harald Weltee72cf552016-04-28 07:18:49 +020085/* process an incoming SAI request */
86static int rx_send_auth_info(struct osmo_gsup_conn *conn,
Maxd4bebbd2017-03-02 12:00:19 +010087 const struct osmo_gsup_message *gsup,
88 struct db_context *dbc)
Harald Weltee72cf552016-04-28 07:18:49 +020089{
90 struct osmo_gsup_message gsup_out;
91 struct msgb *msg_out;
92 int rc;
93
94 /* initialize return message structure */
95 memset(&gsup_out, 0, sizeof(gsup_out));
Harald Weltee72cf552016-04-28 07:18:49 +020096 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
97
Neels Hofmeyrcab2fcd2017-03-15 00:07:43 +010098 rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
99 gsup_out.auth_vectors,
Harald Weltee72cf552016-04-28 07:18:49 +0200100 ARRAY_SIZE(gsup_out.auth_vectors),
Harald Welte9be0d2f2016-06-10 17:34:02 +0200101 gsup->rand, gsup->auts);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100102 if (rc <= 0) {
Harald Weltee72cf552016-04-28 07:18:49 +0200103 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
Neels Hofmeyr671db902017-11-22 20:38:19 +0100104 switch (rc) {
105 case 0:
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100106 /* 0 means "0 tuples generated", which shouldn't happen.
107 * Treat the same as "no auth data". */
108 case -ENOKEY:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100109 LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"
110 " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",
111 gsup->imsi);
112 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
113 break;
Neels Hofmeyr33cbde92017-11-22 20:39:59 +0100114 case -ENOENT:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100115 LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100116 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
117 break;
118 default:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100119 LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100120 gsup_out.cause = GMM_CAUSE_NET_FAIL;
121 break;
122 }
Harald Welte15db8262016-05-05 16:50:39 +0200123 } else {
124 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
125 gsup_out.num_auth_vectors = rc;
Harald Weltee72cf552016-04-28 07:18:49 +0200126 }
127
Harald Weltee687be52016-05-03 18:49:27 +0200128 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +0200129 osmo_gsup_encode(msg_out, &gsup_out);
130 return osmo_gsup_conn_send(conn, msg_out);
131}
132
Harald Weltee687be52016-05-03 18:49:27 +0200133/***********************************************************************
134 * LU Operation State / Structure
135 ***********************************************************************/
136
137static LLIST_HEAD(g_lu_ops);
138
Harald Weltee687be52016-05-03 18:49:27 +0200139/*! Receive Cancel Location Result from old VLR/SGSN */
140void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
Maxea8b0d42017-02-14 16:53:04 +0100141 const struct osmo_gsup_message *gsup)
Harald Weltee687be52016-05-03 18:49:27 +0200142{
143 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
144 /* FIXME: Check for spoofing */
145
146 osmo_timer_del(&luop->timer);
147
148 /* FIXME */
149
150 lu_op_tx_insert_subscr_data(luop);
151}
152
Harald Weltee687be52016-05-03 18:49:27 +0200153/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
154static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
155 const struct osmo_gsup_message *gsup)
156{
157 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
158 /* FIXME: Check for spoofing */
159
160 osmo_timer_del(&luop->timer);
161
162 /* Subscriber_Present_HLR */
163 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
164
165 /* Send final ACK towards inquiring VLR/SGSN */
166 lu_op_tx_ack(luop);
167}
168
169/*! Receive GSUP message for given \ref lu_operation */
170void lu_op_rx_gsup(struct lu_operation *luop,
171 const struct osmo_gsup_message *gsup)
172{
173 switch (gsup->message_type) {
174 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
175 /* FIXME */
176 break;
177 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
178 lu_op_rx_insert_subscr_data_ack(luop, gsup);
179 break;
180 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
181 /* FIXME */
182 break;
183 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
184 lu_op_rx_cancel_old_ack(luop, gsup);
185 break;
186 default:
187 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
188 gsup->message_type);
189 break;
190 }
191}
192
Harald Weltee687be52016-05-03 18:49:27 +0200193/*! Receive Update Location Request, creates new \ref lu_operation */
194static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
195 const struct osmo_gsup_message *gsup)
196{
Maxea8b0d42017-02-14 16:53:04 +0100197 struct lu_operation *luop = lu_op_alloc_conn(conn);
198 if (!luop) {
Harald Weltee687be52016-05-03 18:49:27 +0200199 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
Maxea8b0d42017-02-14 16:53:04 +0100200 return -EINVAL;
Harald Weltee687be52016-05-03 18:49:27 +0200201 }
202
Harald Weltee687be52016-05-03 18:49:27 +0200203 lu_op_statechg(luop, LU_S_LU_RECEIVED);
Maxea8b0d42017-02-14 16:53:04 +0100204
Harald Weltee687be52016-05-03 18:49:27 +0200205 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
206 luop->is_ps = true;
207 llist_add(&luop->list, &g_lu_ops);
208
209 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
210
211 /* check if subscriber is known at all */
Maxd4bebbd2017-03-02 12:00:19 +0100212 if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
Harald Weltee687be52016-05-03 18:49:27 +0200213 /* Send Error back: Subscriber Unknown in HLR */
Harald Weltebd0d5bf2017-11-06 03:55:02 +0900214 osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
Harald Weltee687be52016-05-03 18:49:27 +0200215 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
216 return 0;
217 }
218
Harald Welte99909272016-05-05 18:24:15 +0200219 /* Check if subscriber is generally permitted on CS or PS
220 * service (as requested) */
Maxea8b0d42017-02-14 16:53:04 +0100221 if (!luop->is_ps && !luop->subscr.nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200222 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
223 return 0;
Maxea8b0d42017-02-14 16:53:04 +0100224 } else if (luop->is_ps && !luop->subscr.nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200225 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
226 return 0;
227 }
228
229 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
230
231#if 0
232 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
233 if (luop->is_ps == false &&
234 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200235 lu_op_tx_cancel_old(luop);
236 } else if (luop->is_ps == true &&
237 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200238 lu_op_tx_cancel_old(luop);
239 } else
240#endif
241 {
242 /* TODO: Subscriber allowed to roam in PLMN? */
243 /* TODO: Update RoutingInfo */
244 /* TODO: Reset Flag MS Purged (cs/ps) */
245 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
246 lu_op_tx_insert_subscr_data(luop);
247 }
248 return 0;
249}
250
Harald Welteb18f0e02016-05-05 21:03:03 +0200251static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
252 const struct osmo_gsup_message *gsup)
253{
254 struct osmo_gsup_message gsup_reply = {0};
255 struct msgb *msg_out;
256 bool is_ps = false;
257 int rc;
258
259 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
260 is_ps ? "PS" : "CS");
261
262 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
263
264 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
265 is_ps = true;
266
267 /* FIXME: check if the VLR that sends the purge is the same that
268 * we have on record. Only update if yes */
269
270 /* Perform the actual update of the DB */
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200271 rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);
Harald Welteb18f0e02016-05-05 21:03:03 +0200272
Harald Welte3f2a9a22018-03-01 23:35:35 +0100273 if (rc == 0)
Harald Welteb18f0e02016-05-05 21:03:03 +0200274 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
Harald Welte3f2a9a22018-03-01 23:35:35 +0100275 else if (rc == -ENOENT) {
Harald Welteb18f0e02016-05-05 21:03:03 +0200276 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
277 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
278 } else {
279 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
280 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
281 }
282
283 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
284 osmo_gsup_encode(msg_out, &gsup_reply);
285 return osmo_gsup_conn_send(conn, msg_out);
286}
287
Harald Weltee72cf552016-04-28 07:18:49 +0200288static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
289{
290 static struct osmo_gsup_message gsup;
291 int rc;
292
Harald Weltee687be52016-05-03 18:49:27 +0200293 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200294 if (rc < 0) {
295 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
296 return rc;
297 }
298
299 switch (gsup.message_type) {
300 /* requests sent to us */
301 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
Maxd4bebbd2017-03-02 12:00:19 +0100302 rx_send_auth_info(conn, &gsup, g_hlr->dbc);
Harald Weltee72cf552016-04-28 07:18:49 +0200303 break;
304 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200305 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200306 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200307 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
308 rx_purge_ms_req(conn, &gsup);
309 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200310 /* responses to requests sent by us */
Max9cacb6f2017-02-20 17:22:56 +0100311 case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:
312 LOGP(DMAIN, LOGL_ERROR, "Error while deleting subscriber data "
313 "for IMSI %s\n", gsup.imsi);
314 break;
315 case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:
316 LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
317 gsup.imsi);
318 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200319 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200320 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200321 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
322 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
323 {
Maxea8b0d42017-02-14 16:53:04 +0100324 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi,
325 &g_lu_ops);
Harald Weltee687be52016-05-03 18:49:27 +0200326 if (!luop) {
Maxaa0fefd2017-02-16 12:25:22 +0100327 LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "
328 "unknown IMSI %s\n",
329 osmo_gsup_message_type_name(gsup.message_type),
Harald Weltee687be52016-05-03 18:49:27 +0200330 gsup.imsi);
331 break;
332 }
333 lu_op_rx_gsup(luop, &gsup);
334 }
Harald Weltee72cf552016-04-28 07:18:49 +0200335 break;
336 default:
Maxaa0fefd2017-02-16 12:25:22 +0100337 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
338 osmo_gsup_message_type_name(gsup.message_type));
Harald Weltee72cf552016-04-28 07:18:49 +0200339 break;
340 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200341 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200342 return 0;
343}
344
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100345static void print_usage()
346{
347 printf("Usage: osmo-hlr\n");
348}
349
350static void print_help()
351{
352 printf(" -h --help This text.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100353 printf(" -c --config-file filename The config file to use.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100354 printf(" -l --database db-name The database to use.\n");
355 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
356 printf(" -D --daemonize Fork the process into a background daemon.\n");
357 printf(" -s --disable-color Do not print ANSI colors in the log\n");
358 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
359 printf(" -e --log-level number Set a global loglevel.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100360 printf(" -V --version Print the version of OsmoHLR.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100361}
362
363static struct {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100364 const char *config_file;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100365 const char *db_file;
366 bool daemonize;
367} cmdline_opts = {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100368 .config_file = "osmo-hlr.cfg",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100369 .db_file = "hlr.db",
370 .daemonize = false,
371};
372
373static void handle_options(int argc, char **argv)
374{
375 while (1) {
376 int option_index = 0, c;
377 static struct option long_options[] = {
378 {"help", 0, 0, 'h'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100379 {"config-file", 1, 0, 'c'},
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100380 {"database", 1, 0, 'l'},
381 {"debug", 1, 0, 'd'},
382 {"daemonize", 0, 0, 'D'},
383 {"disable-color", 0, 0, 's'},
384 {"log-level", 1, 0, 'e'},
385 {"timestamp", 0, 0, 'T'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100386 {"version", 0, 0, 'V' },
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100387 {0, 0, 0, 0}
388 };
389
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100390 c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100391 long_options, &option_index);
392 if (c == -1)
393 break;
394
395 switch (c) {
396 case 'h':
397 print_usage();
398 print_help();
399 exit(0);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100400 case 'c':
401 cmdline_opts.config_file = optarg;
402 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100403 case 'l':
404 cmdline_opts.db_file = optarg;
405 break;
406 case 'd':
407 log_parse_category_mask(osmo_stderr_target, optarg);
408 break;
409 case 'D':
410 cmdline_opts.daemonize = 1;
411 break;
412 case 's':
413 log_set_use_color(osmo_stderr_target, 0);
414 break;
415 case 'e':
416 log_set_log_level(osmo_stderr_target, atoi(optarg));
417 break;
418 case 'T':
419 log_set_print_timestamp(osmo_stderr_target, 1);
420 break;
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100421 case 'V':
422 print_version(1);
423 exit(0);
424 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100425 default:
426 /* catch unknown options *as well as* missing arguments. */
427 fprintf(stderr, "Error in command line options. Exiting.\n");
428 exit(-1);
429 break;
430 }
431 }
432}
433
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100434static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200435
436static void signal_hdlr(int signal)
437{
438 switch (signal) {
439 case SIGINT:
440 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
Maxd4bebbd2017-03-02 12:00:19 +0100441 osmo_gsup_server_destroy(g_hlr->gs);
442 db_close(g_hlr->dbc);
Harald Welteaabae9e2016-04-28 12:48:14 +0200443 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100444 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200445 exit(0);
446 break;
447 case SIGUSR1:
448 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100449 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200450 break;
451 }
452}
Harald Weltee72cf552016-04-28 07:18:49 +0200453
Max372868b2017-03-02 12:12:00 +0100454static const char vlr_copyright[] =
455 "Copyright (C) 2016, 2017 by Harald Welte, sysmocom s.f.m.c. GmbH\r\n"
456 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
457 "This is free software: you are free to change and redistribute it.\r\n"
458 "There is NO WARRANTY, to the extent permitted by law.\r\n";
459
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100460static struct vty_app_info vty_info = {
461 .name = "OsmoHLR",
462 .version = PACKAGE_VERSION,
Max372868b2017-03-02 12:12:00 +0100463 .copyright = vlr_copyright,
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100464 .is_config_node = hlr_vty_is_config_node,
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200465 .go_parent_cb = hlr_vty_go_parent,
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100466};
467
Harald Weltee72cf552016-04-28 07:18:49 +0200468int main(int argc, char **argv)
469{
Harald Weltee72cf552016-04-28 07:18:49 +0200470 int rc;
471
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100472 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
473 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welte7ee6e552018-02-14 00:52:05 +0100474 vty_info.tall_ctx = hlr_ctx;
Harald Welteaabae9e2016-04-28 12:48:14 +0200475
Maxd4bebbd2017-03-02 12:00:19 +0100476 g_hlr = talloc_zero(hlr_ctx, struct hlr);
477
Harald Weltee72cf552016-04-28 07:18:49 +0200478 rc = osmo_init_logging(&hlr_log_info);
479 if (rc < 0) {
480 fprintf(stderr, "Error initializing logging\n");
481 exit(1);
482 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100483
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100484 vty_init(&vty_info);
Max372868b2017-03-02 12:12:00 +0100485 ctrl_vty_init(hlr_ctx);
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100486 handle_options(argc, argv);
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200487 hlr_vty_init(g_hlr, &hlr_log_info);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100488
489 rc = vty_read_config_file(cmdline_opts.config_file, NULL);
490 if (rc < 0) {
491 LOGP(DMAIN, LOGL_FATAL,
492 "Failed to parse the config file: '%s'\n",
493 cmdline_opts.config_file);
494 return rc;
495 }
496
497 /* start telnet after reading config for vty_get_bind_addr() */
498 rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
499 OSMO_VTY_PORT_HLR);
500 if (rc < 0)
501 return rc;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100502
Harald Weltee72cf552016-04-28 07:18:49 +0200503 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
504
505 rc = rand_init();
506 if (rc < 0) {
507 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
508 exit(1);
509 }
510
Neels Hofmeyrd3814b92017-11-21 12:28:07 +0100511 g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true);
Maxd4bebbd2017-03-02 12:00:19 +0100512 if (!g_hlr->dbc) {
Harald Weltee72cf552016-04-28 07:18:49 +0200513 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
514 exit(1);
515 }
516
Neels Hofmeyr84201d32017-07-21 16:00:32 +0200517 g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200518 read_cb, &g_lu_ops);
Maxd4bebbd2017-03-02 12:00:19 +0100519 if (!g_hlr->gs) {
Harald Weltee72cf552016-04-28 07:18:49 +0200520 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
521 exit(1);
522 }
523
Max372868b2017-03-02 12:12:00 +0100524 g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200525 g_hlr->ctrl = hlr_controlif_setup(g_hlr);
Max372868b2017-03-02 12:12:00 +0100526
Harald Welteaabae9e2016-04-28 12:48:14 +0200527 osmo_init_ignore_signals();
528 signal(SIGINT, &signal_hdlr);
529 signal(SIGUSR1, &signal_hdlr);
530
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100531 if (cmdline_opts.daemonize) {
532 rc = osmo_daemonize();
533 if (rc < 0) {
534 perror("Error during daemonize");
535 exit(1);
536 }
537 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200538
Harald Weltee72cf552016-04-28 07:18:49 +0200539 while (1) {
540 osmo_select_main(0);
541 }
542
Maxd4bebbd2017-03-02 12:00:19 +0100543 db_close(g_hlr->dbc);
Harald Weltee72cf552016-04-28 07:18:49 +0200544
545 log_fini();
546
547 exit(0);
548}