blob: 4da7b9b9229f762795d511fdffcbe55b408513b5 [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>
Neels Hofmeyr7685a782017-01-30 23:30:26 +010029#include <osmocom/vty/vty.h>
30#include <osmocom/vty/command.h>
31#include <osmocom/vty/telnet_interface.h>
32#include <osmocom/vty/ports.h>
Max372868b2017-03-02 12:12:00 +010033#include <osmocom/ctrl/control_vty.h>
Neels Hofmeyr5aeb4382018-05-04 16:02:44 +020034#include <osmocom/gsm/apn.h>
Harald Weltee72cf552016-04-28 07:18:49 +020035
36#include "db.h"
Maxd4bebbd2017-03-02 12:00:19 +010037#include "hlr.h"
Max372868b2017-03-02 12:12:00 +010038#include "ctrl.h"
Harald Weltee72cf552016-04-28 07:18:49 +020039#include "logging.h"
40#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020041#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020042#include "rand.h"
Maxea8b0d42017-02-14 16:53:04 +010043#include "luop.h"
Neels Hofmeyr7685a782017-01-30 23:30:26 +010044#include "hlr_vty.h"
Harald Weltee72cf552016-04-28 07:18:49 +020045
Maxd4bebbd2017-03-02 12:00:19 +010046static struct hlr *g_hlr;
Harald Weltee72cf552016-04-28 07:18:49 +020047
Stefan Sperlingf1622522018-04-09 11:39:16 +020048/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
49 *
50 * FIXME: In order to support large-scale networks this function should skip
51 * VLRs/SGSNs which do not currently serve the subscriber.
52 *
53 * \param[in] subscr A subscriber we have new data to send for.
54 */
55void
56osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
57{
58 struct osmo_gsup_conn *co;
59
60 if (g_hlr->gs == NULL)
61 return;
62
63 llist_for_each_entry(co, &g_hlr->gs->clients, list) {
Stefan Sperlingf83432c2018-05-03 14:26:59 +020064 struct osmo_gsup_message gsup = { };
65 uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
66 uint8_t apn[APN_MAXLEN];
67 struct msgb *msg_out;
Stefan Sperling93c5b102018-04-10 19:26:14 +020068 uint8_t *peer;
69 int peer_len;
Stefan Sperlingf83432c2018-05-03 14:26:59 +020070 enum osmo_gsup_cn_domain cn_domain;
Stefan Sperling93c5b102018-04-10 19:26:14 +020071
Stefan Sperlingf83432c2018-05-03 14:26:59 +020072 if (co->supports_ps)
73 cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
74 else if (co->supports_cs)
75 cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
76 else {
77 /* We have not yet received a location update from this subscriber .*/
78 continue;
79 }
80
81 if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
82 sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
Stefan Sperlingf1622522018-04-09 11:39:16 +020083 LOGP(DMAIN, LOGL_ERROR,
Stefan Sperlingf83432c2018-05-03 14:26:59 +020084 "IMSI='%s': Cannot notify GSUP client; could not create gsup message "
Stefan Sperling93c5b102018-04-10 19:26:14 +020085 "for %s:%u\n", subscr->imsi,
Stefan Sperlingf1622522018-04-09 11:39:16 +020086 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
87 co && co->conn && co->conn->server? co->conn->server->port : 0);
88 continue;
89 }
Stefan Sperling93c5b102018-04-10 19:26:14 +020090
Stefan Sperling93c5b102018-04-10 19:26:14 +020091 /* Send ISD to MSC/SGSN */
92 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE");
93 if (msg_out == NULL) {
94 LOGP(DMAIN, LOGL_ERROR,
95 "IMSI='%s': Cannot notify GSUP client; could not allocate msg buffer "
96 "for %s:%u\n", subscr->imsi,
97 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
98 co && co->conn && co->conn->server? co->conn->server->port : 0);
99 continue;
100 }
Stefan Sperling93c5b102018-04-10 19:26:14 +0200101 osmo_gsup_encode(msg_out, &gsup);
Stefan Sperlingf83432c2018-05-03 14:26:59 +0200102
103 peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR);
104 if (peer_len < 0) {
105 LOGP(DMAIN, LOGL_ERROR,
106 "IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi,
107 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
108 co && co->conn && co->conn->server? co->conn->server->port : 0);
109 continue;
110 }
111
Stefan Sperling93c5b102018-04-10 19:26:14 +0200112 if (osmo_gsup_addr_send(g_hlr->gs, peer, peer_len, msg_out) < 0) {
113 LOGP(DMAIN, LOGL_ERROR,
114 "IMSI='%s': Cannot notify GSUP client; send operation failed "
115 "for %s:%u\n", subscr->imsi,
116 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
117 co && co->conn && co->conn->server? co->conn->server->port : 0);
118 continue;
119 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200120 }
121}
122
Harald Weltee687be52016-05-03 18:49:27 +0200123/***********************************************************************
124 * Send Auth Info handling
125 ***********************************************************************/
126
Harald Weltee72cf552016-04-28 07:18:49 +0200127/* process an incoming SAI request */
128static int rx_send_auth_info(struct osmo_gsup_conn *conn,
Maxd4bebbd2017-03-02 12:00:19 +0100129 const struct osmo_gsup_message *gsup,
130 struct db_context *dbc)
Harald Weltee72cf552016-04-28 07:18:49 +0200131{
132 struct osmo_gsup_message gsup_out;
133 struct msgb *msg_out;
134 int rc;
135
136 /* initialize return message structure */
137 memset(&gsup_out, 0, sizeof(gsup_out));
Harald Weltee72cf552016-04-28 07:18:49 +0200138 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
139
Neels Hofmeyrcab2fcd2017-03-15 00:07:43 +0100140 rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
141 gsup_out.auth_vectors,
Harald Weltee72cf552016-04-28 07:18:49 +0200142 ARRAY_SIZE(gsup_out.auth_vectors),
Harald Welte9be0d2f2016-06-10 17:34:02 +0200143 gsup->rand, gsup->auts);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100144 if (rc <= 0) {
Harald Weltee72cf552016-04-28 07:18:49 +0200145 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
Neels Hofmeyr671db902017-11-22 20:38:19 +0100146 switch (rc) {
147 case 0:
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100148 /* 0 means "0 tuples generated", which shouldn't happen.
149 * Treat the same as "no auth data". */
150 case -ENOKEY:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100151 LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"
152 " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",
153 gsup->imsi);
154 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
155 break;
Neels Hofmeyr33cbde92017-11-22 20:39:59 +0100156 case -ENOENT:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100157 LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100158 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
159 break;
160 default:
Neels Hofmeyrab4d5092017-11-23 15:31:12 +0100161 LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
Neels Hofmeyr671db902017-11-22 20:38:19 +0100162 gsup_out.cause = GMM_CAUSE_NET_FAIL;
163 break;
164 }
Harald Welte15db8262016-05-05 16:50:39 +0200165 } else {
166 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
167 gsup_out.num_auth_vectors = rc;
Harald Weltee72cf552016-04-28 07:18:49 +0200168 }
169
Harald Weltee687be52016-05-03 18:49:27 +0200170 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +0200171 osmo_gsup_encode(msg_out, &gsup_out);
172 return osmo_gsup_conn_send(conn, msg_out);
173}
174
Harald Weltee687be52016-05-03 18:49:27 +0200175/***********************************************************************
176 * LU Operation State / Structure
177 ***********************************************************************/
178
179static LLIST_HEAD(g_lu_ops);
180
Harald Weltee687be52016-05-03 18:49:27 +0200181/*! Receive Cancel Location Result from old VLR/SGSN */
182void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
Maxea8b0d42017-02-14 16:53:04 +0100183 const struct osmo_gsup_message *gsup)
Harald Weltee687be52016-05-03 18:49:27 +0200184{
185 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
186 /* FIXME: Check for spoofing */
187
188 osmo_timer_del(&luop->timer);
189
190 /* FIXME */
191
192 lu_op_tx_insert_subscr_data(luop);
193}
194
Harald Weltee687be52016-05-03 18:49:27 +0200195/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
196static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
197 const struct osmo_gsup_message *gsup)
198{
199 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
200 /* FIXME: Check for spoofing */
201
202 osmo_timer_del(&luop->timer);
203
204 /* Subscriber_Present_HLR */
205 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
206
207 /* Send final ACK towards inquiring VLR/SGSN */
208 lu_op_tx_ack(luop);
209}
210
211/*! Receive GSUP message for given \ref lu_operation */
212void lu_op_rx_gsup(struct lu_operation *luop,
213 const struct osmo_gsup_message *gsup)
214{
215 switch (gsup->message_type) {
216 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
217 /* FIXME */
218 break;
219 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
220 lu_op_rx_insert_subscr_data_ack(luop, gsup);
221 break;
222 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
223 /* FIXME */
224 break;
225 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
226 lu_op_rx_cancel_old_ack(luop, gsup);
227 break;
228 default:
229 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
230 gsup->message_type);
231 break;
232 }
233}
234
Harald Weltee687be52016-05-03 18:49:27 +0200235/*! Receive Update Location Request, creates new \ref lu_operation */
236static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
237 const struct osmo_gsup_message *gsup)
238{
Maxea8b0d42017-02-14 16:53:04 +0100239 struct lu_operation *luop = lu_op_alloc_conn(conn);
240 if (!luop) {
Harald Weltee687be52016-05-03 18:49:27 +0200241 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
Maxea8b0d42017-02-14 16:53:04 +0100242 return -EINVAL;
Harald Weltee687be52016-05-03 18:49:27 +0200243 }
244
Harald Weltee687be52016-05-03 18:49:27 +0200245 lu_op_statechg(luop, LU_S_LU_RECEIVED);
Maxea8b0d42017-02-14 16:53:04 +0100246
Stefan Sperling93c5b102018-04-10 19:26:14 +0200247 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS)
248 conn->supports_cs = true;
249 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
250 conn->supports_ps = true;
Harald Weltee687be52016-05-03 18:49:27 +0200251 luop->is_ps = true;
Stefan Sperling93c5b102018-04-10 19:26:14 +0200252 } else {
253 /* The client didn't send a CN_DOMAIN IE; assume packet-switched in
254 * accordance with the GSUP spec in osmo-hlr's user manual (section
255 * 11.6.15 "CN Domain" says "if no CN Domain IE is present within
256 * a request, the PS Domain is assumed." */
257 conn->supports_ps = true;
Stefan Sperling1cb48922018-05-03 14:05:56 +0200258 luop->is_ps = true;
Stefan Sperling93c5b102018-04-10 19:26:14 +0200259 }
Harald Weltee687be52016-05-03 18:49:27 +0200260 llist_add(&luop->list, &g_lu_ops);
261
262 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
263
264 /* check if subscriber is known at all */
Maxd4bebbd2017-03-02 12:00:19 +0100265 if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
Harald Weltee687be52016-05-03 18:49:27 +0200266 /* Send Error back: Subscriber Unknown in HLR */
Harald Weltebd0d5bf2017-11-06 03:55:02 +0900267 osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
Harald Weltee687be52016-05-03 18:49:27 +0200268 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
269 return 0;
270 }
271
Harald Welte99909272016-05-05 18:24:15 +0200272 /* Check if subscriber is generally permitted on CS or PS
273 * service (as requested) */
Maxea8b0d42017-02-14 16:53:04 +0100274 if (!luop->is_ps && !luop->subscr.nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200275 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
276 return 0;
Maxea8b0d42017-02-14 16:53:04 +0100277 } else if (luop->is_ps && !luop->subscr.nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200278 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
279 return 0;
280 }
281
282 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
283
284#if 0
285 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
286 if (luop->is_ps == false &&
287 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200288 lu_op_tx_cancel_old(luop);
289 } else if (luop->is_ps == true &&
290 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200291 lu_op_tx_cancel_old(luop);
292 } else
293#endif
294 {
295 /* TODO: Subscriber allowed to roam in PLMN? */
296 /* TODO: Update RoutingInfo */
297 /* TODO: Reset Flag MS Purged (cs/ps) */
298 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
299 lu_op_tx_insert_subscr_data(luop);
300 }
301 return 0;
302}
303
Harald Welteb18f0e02016-05-05 21:03:03 +0200304static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
305 const struct osmo_gsup_message *gsup)
306{
307 struct osmo_gsup_message gsup_reply = {0};
308 struct msgb *msg_out;
309 bool is_ps = false;
310 int rc;
311
312 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
313 is_ps ? "PS" : "CS");
314
315 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
316
317 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
318 is_ps = true;
319
320 /* FIXME: check if the VLR that sends the purge is the same that
321 * we have on record. Only update if yes */
322
323 /* Perform the actual update of the DB */
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200324 rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);
Harald Welteb18f0e02016-05-05 21:03:03 +0200325
Harald Welte3f2a9a22018-03-01 23:35:35 +0100326 if (rc == 0)
Harald Welteb18f0e02016-05-05 21:03:03 +0200327 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
Harald Welte3f2a9a22018-03-01 23:35:35 +0100328 else if (rc == -ENOENT) {
Harald Welteb18f0e02016-05-05 21:03:03 +0200329 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
330 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
331 } else {
332 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
333 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
334 }
335
336 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
337 osmo_gsup_encode(msg_out, &gsup_reply);
338 return osmo_gsup_conn_send(conn, msg_out);
339}
340
Harald Weltee72cf552016-04-28 07:18:49 +0200341static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
342{
343 static struct osmo_gsup_message gsup;
344 int rc;
345
Harald Weltee687be52016-05-03 18:49:27 +0200346 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200347 if (rc < 0) {
348 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
349 return rc;
350 }
351
352 switch (gsup.message_type) {
353 /* requests sent to us */
354 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
Maxd4bebbd2017-03-02 12:00:19 +0100355 rx_send_auth_info(conn, &gsup, g_hlr->dbc);
Harald Weltee72cf552016-04-28 07:18:49 +0200356 break;
357 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200358 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200359 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200360 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
361 rx_purge_ms_req(conn, &gsup);
362 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200363 /* responses to requests sent by us */
Max9cacb6f2017-02-20 17:22:56 +0100364 case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:
365 LOGP(DMAIN, LOGL_ERROR, "Error while deleting subscriber data "
366 "for IMSI %s\n", gsup.imsi);
367 break;
368 case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:
369 LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
370 gsup.imsi);
371 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200372 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200373 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200374 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
375 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
376 {
Maxea8b0d42017-02-14 16:53:04 +0100377 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi,
378 &g_lu_ops);
Harald Weltee687be52016-05-03 18:49:27 +0200379 if (!luop) {
Maxaa0fefd2017-02-16 12:25:22 +0100380 LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "
381 "unknown IMSI %s\n",
382 osmo_gsup_message_type_name(gsup.message_type),
Harald Weltee687be52016-05-03 18:49:27 +0200383 gsup.imsi);
384 break;
385 }
386 lu_op_rx_gsup(luop, &gsup);
387 }
Harald Weltee72cf552016-04-28 07:18:49 +0200388 break;
389 default:
Maxaa0fefd2017-02-16 12:25:22 +0100390 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
391 osmo_gsup_message_type_name(gsup.message_type));
Harald Weltee72cf552016-04-28 07:18:49 +0200392 break;
393 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200394 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200395 return 0;
396}
397
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100398static void print_usage()
399{
400 printf("Usage: osmo-hlr\n");
401}
402
403static void print_help()
404{
405 printf(" -h --help This text.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100406 printf(" -c --config-file filename The config file to use.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100407 printf(" -l --database db-name The database to use.\n");
408 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
409 printf(" -D --daemonize Fork the process into a background daemon.\n");
410 printf(" -s --disable-color Do not print ANSI colors in the log\n");
411 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
412 printf(" -e --log-level number Set a global loglevel.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100413 printf(" -V --version Print the version of OsmoHLR.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100414}
415
416static struct {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100417 const char *config_file;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100418 const char *db_file;
419 bool daemonize;
420} cmdline_opts = {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100421 .config_file = "osmo-hlr.cfg",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100422 .db_file = "hlr.db",
423 .daemonize = false,
424};
425
426static void handle_options(int argc, char **argv)
427{
428 while (1) {
429 int option_index = 0, c;
430 static struct option long_options[] = {
431 {"help", 0, 0, 'h'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100432 {"config-file", 1, 0, 'c'},
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100433 {"database", 1, 0, 'l'},
434 {"debug", 1, 0, 'd'},
435 {"daemonize", 0, 0, 'D'},
436 {"disable-color", 0, 0, 's'},
437 {"log-level", 1, 0, 'e'},
438 {"timestamp", 0, 0, 'T'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100439 {"version", 0, 0, 'V' },
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100440 {0, 0, 0, 0}
441 };
442
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100443 c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100444 long_options, &option_index);
445 if (c == -1)
446 break;
447
448 switch (c) {
449 case 'h':
450 print_usage();
451 print_help();
452 exit(0);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100453 case 'c':
454 cmdline_opts.config_file = optarg;
455 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100456 case 'l':
457 cmdline_opts.db_file = optarg;
458 break;
459 case 'd':
460 log_parse_category_mask(osmo_stderr_target, optarg);
461 break;
462 case 'D':
463 cmdline_opts.daemonize = 1;
464 break;
465 case 's':
466 log_set_use_color(osmo_stderr_target, 0);
467 break;
468 case 'e':
469 log_set_log_level(osmo_stderr_target, atoi(optarg));
470 break;
471 case 'T':
472 log_set_print_timestamp(osmo_stderr_target, 1);
473 break;
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100474 case 'V':
475 print_version(1);
476 exit(0);
477 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100478 default:
479 /* catch unknown options *as well as* missing arguments. */
480 fprintf(stderr, "Error in command line options. Exiting.\n");
481 exit(-1);
482 break;
483 }
484 }
485}
486
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100487static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200488
489static void signal_hdlr(int signal)
490{
491 switch (signal) {
492 case SIGINT:
493 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
Maxd4bebbd2017-03-02 12:00:19 +0100494 osmo_gsup_server_destroy(g_hlr->gs);
495 db_close(g_hlr->dbc);
Harald Welteaabae9e2016-04-28 12:48:14 +0200496 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100497 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200498 exit(0);
499 break;
500 case SIGUSR1:
501 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100502 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200503 break;
504 }
505}
Harald Weltee72cf552016-04-28 07:18:49 +0200506
Max372868b2017-03-02 12:12:00 +0100507static const char vlr_copyright[] =
508 "Copyright (C) 2016, 2017 by Harald Welte, sysmocom s.f.m.c. GmbH\r\n"
509 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
510 "This is free software: you are free to change and redistribute it.\r\n"
511 "There is NO WARRANTY, to the extent permitted by law.\r\n";
512
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100513static struct vty_app_info vty_info = {
514 .name = "OsmoHLR",
515 .version = PACKAGE_VERSION,
Max372868b2017-03-02 12:12:00 +0100516 .copyright = vlr_copyright,
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100517 .is_config_node = hlr_vty_is_config_node,
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200518 .go_parent_cb = hlr_vty_go_parent,
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100519};
520
Harald Weltee72cf552016-04-28 07:18:49 +0200521int main(int argc, char **argv)
522{
Harald Weltee72cf552016-04-28 07:18:49 +0200523 int rc;
524
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100525 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
526 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welte7ee6e552018-02-14 00:52:05 +0100527 vty_info.tall_ctx = hlr_ctx;
Harald Welteaabae9e2016-04-28 12:48:14 +0200528
Maxd4bebbd2017-03-02 12:00:19 +0100529 g_hlr = talloc_zero(hlr_ctx, struct hlr);
530
Pau Espin Pedrol51530312018-04-17 15:07:06 +0200531 rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
Harald Weltee72cf552016-04-28 07:18:49 +0200532 if (rc < 0) {
533 fprintf(stderr, "Error initializing logging\n");
534 exit(1);
535 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100536
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100537 vty_init(&vty_info);
Max372868b2017-03-02 12:12:00 +0100538 ctrl_vty_init(hlr_ctx);
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100539 handle_options(argc, argv);
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200540 hlr_vty_init(g_hlr, &hlr_log_info);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100541
542 rc = vty_read_config_file(cmdline_opts.config_file, NULL);
543 if (rc < 0) {
544 LOGP(DMAIN, LOGL_FATAL,
545 "Failed to parse the config file: '%s'\n",
546 cmdline_opts.config_file);
547 return rc;
548 }
549
550 /* start telnet after reading config for vty_get_bind_addr() */
551 rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
552 OSMO_VTY_PORT_HLR);
553 if (rc < 0)
554 return rc;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100555
Harald Weltee72cf552016-04-28 07:18:49 +0200556 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
557
558 rc = rand_init();
559 if (rc < 0) {
560 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
561 exit(1);
562 }
563
Neels Hofmeyrd3814b92017-11-21 12:28:07 +0100564 g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true);
Maxd4bebbd2017-03-02 12:00:19 +0100565 if (!g_hlr->dbc) {
Harald Weltee72cf552016-04-28 07:18:49 +0200566 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
567 exit(1);
568 }
569
Neels Hofmeyr84201d32017-07-21 16:00:32 +0200570 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 +0200571 read_cb, &g_lu_ops);
Maxd4bebbd2017-03-02 12:00:19 +0100572 if (!g_hlr->gs) {
Harald Weltee72cf552016-04-28 07:18:49 +0200573 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
574 exit(1);
575 }
576
Max372868b2017-03-02 12:12:00 +0100577 g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200578 g_hlr->ctrl = hlr_controlif_setup(g_hlr);
Max372868b2017-03-02 12:12:00 +0100579
Harald Welteaabae9e2016-04-28 12:48:14 +0200580 osmo_init_ignore_signals();
581 signal(SIGINT, &signal_hdlr);
582 signal(SIGUSR1, &signal_hdlr);
583
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100584 if (cmdline_opts.daemonize) {
585 rc = osmo_daemonize();
586 if (rc < 0) {
587 perror("Error during daemonize");
588 exit(1);
589 }
590 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200591
Harald Weltee72cf552016-04-28 07:18:49 +0200592 while (1) {
593 osmo_select_main(0);
594 }
595
Maxd4bebbd2017-03-02 12:00:19 +0100596 db_close(g_hlr->dbc);
Harald Weltee72cf552016-04-28 07:18:49 +0200597
598 log_fini();
599
600 exit(0);
601}