blob: cfd86cc6bfc8fa1b247eb53cb01bacfde0d9cee6 [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>
Harald Welteaabae9e2016-04-28 12:48:14 +020022
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>
Harald Weltee72cf552016-04-28 07:18:49 +020035
36#include "db.h"
37#include "logging.h"
38#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020039#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020040#include "rand.h"
Neels Hofmeyr7685a782017-01-30 23:30:26 +010041#include "hlr_vty.h"
Harald Weltee72cf552016-04-28 07:18:49 +020042
43static struct db_context *g_dbc;
44
Harald Weltee687be52016-05-03 18:49:27 +020045/***********************************************************************
46 * Send Auth Info handling
47 ***********************************************************************/
48
Harald Weltee72cf552016-04-28 07:18:49 +020049/* process an incoming SAI request */
50static int rx_send_auth_info(struct osmo_gsup_conn *conn,
51 const struct osmo_gsup_message *gsup)
52{
53 struct osmo_gsup_message gsup_out;
54 struct msgb *msg_out;
55 int rc;
56
57 /* initialize return message structure */
58 memset(&gsup_out, 0, sizeof(gsup_out));
Harald Weltee72cf552016-04-28 07:18:49 +020059 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
60
61 rc = db_get_auc(g_dbc, gsup->imsi, gsup_out.auth_vectors,
62 ARRAY_SIZE(gsup_out.auth_vectors),
Harald Welte9be0d2f2016-06-10 17:34:02 +020063 gsup->rand, gsup->auts);
Harald Weltecfc752b2016-05-05 16:38:14 +020064 if (rc < 0) {
Harald Weltee72cf552016-04-28 07:18:49 +020065 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
Harald Weltecfc752b2016-05-05 16:38:14 +020066 gsup_out.cause = GMM_CAUSE_NET_FAIL;
67 } else if (rc == 0) {
68 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
69 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
Harald Welte15db8262016-05-05 16:50:39 +020070 } else {
71 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
72 gsup_out.num_auth_vectors = rc;
Harald Weltee72cf552016-04-28 07:18:49 +020073 }
74
Harald Weltee687be52016-05-03 18:49:27 +020075 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +020076 osmo_gsup_encode(msg_out, &gsup_out);
77 return osmo_gsup_conn_send(conn, msg_out);
78}
79
Harald Weltee687be52016-05-03 18:49:27 +020080/***********************************************************************
81 * LU Operation State / Structure
82 ***********************************************************************/
83
84static LLIST_HEAD(g_lu_ops);
85
86#define CANCEL_TIMEOUT_SECS 30
87#define ISD_TIMEOUT_SECS 30
88
89enum lu_state {
90 LU_S_NULL,
91 LU_S_LU_RECEIVED,
92 LU_S_CANCEL_SENT,
93 LU_S_CANCEL_ACK_RECEIVED,
94 LU_S_ISD_SENT,
95 LU_S_ISD_ACK_RECEIVED,
96 LU_S_COMPLETE,
97};
98
99static const struct value_string lu_state_names[] = {
100 { LU_S_NULL, "NULL" },
101 { LU_S_LU_RECEIVED, "LU RECEIVED" },
102 { LU_S_CANCEL_SENT, "CANCEL SENT" },
103 { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" },
104 { LU_S_ISD_SENT, "ISD SENT" },
105 { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" },
106 { LU_S_COMPLETE, "COMPLETE" },
107 { 0, NULL }
108};
109
110struct lu_operation {
111 /*! entry in global list of location update operations */
112 struct llist_head list;
113 /*! to which gsup_server do we belong */
114 struct osmo_gsup_server *gsup_server;
115 /*! state of the location update */
116 enum lu_state state;
117 /*! CS (false) or PS (true) Location Update? */
118 bool is_ps;
119 /*! currently running timer */
120 struct osmo_timer_list timer;
121
122 /*! subscriber related to this operation */
123 struct hlr_subscriber subscr;
124 /*! peer VLR/SGSN starting the request */
125 uint8_t *peer;
126};
127
Neels Hofmeyr6eed3222016-12-11 01:21:49 +0100128void lu_op_tx_insert_subscr_data(struct lu_operation *luop);
129
Harald Weltee687be52016-05-03 18:49:27 +0200130void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
131{
132 enum lu_state old_state = luop->state;
133
134 DEBUGP(DMAIN, "LU OP state change: %s -> ",
135 get_value_string(lu_state_names, old_state));
136 DEBUGPC(DMAIN, "%s\n",
137 get_value_string(lu_state_names, new_state));
138
139 luop->state = new_state;
140}
141
142struct lu_operation *lu_op_by_imsi(const char *imsi)
143{
144 struct lu_operation *luop;
145
146 llist_for_each_entry(luop, &g_lu_ops, list) {
147 if (!strcmp(imsi, luop->subscr.imsi))
148 return luop;
149 }
150 return NULL;
151}
152
153/* Send a msgb to a given address using routing */
154int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
155 const uint8_t *addr, size_t addrlen,
156 struct msgb *msg)
157{
158 struct osmo_gsup_conn *conn;
159
160 conn = gsup_route_find(gs, addr, addrlen);
161 if (!conn) {
162 DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr);
163 msgb_free(msg);
164 return -ENODEV;
165 }
166
167 return osmo_gsup_conn_send(conn, msg);
168}
169
Harald Welte99909272016-05-05 18:24:15 +0200170/* Transmit a given GSUP message for the given LU operation */
Harald Weltee687be52016-05-03 18:49:27 +0200171static void _luop_tx_gsup(struct lu_operation *luop,
172 const struct osmo_gsup_message *gsup)
173{
174 struct msgb *msg_out;
175
176 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
177 osmo_gsup_encode(msg_out, gsup);
178
179 osmo_gsup_addr_send(luop->gsup_server, luop->peer,
180 talloc_total_size(luop->peer),
181 msg_out);
182}
183
184/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
185void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
186{
187 struct osmo_gsup_message gsup;
188
189 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n",
190 luop->subscr.imsi, cause);
191
192 memset(&gsup, 0, sizeof(gsup));
193 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
Neels Hofmeyrec1b9592016-12-11 01:22:23 +0100194 strncpy((char*)&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi));
Harald Weltee687be52016-05-03 18:49:27 +0200195 gsup.imsi[sizeof(gsup.imsi)-1] = '\0';
196 gsup.cause = cause;
197
198 _luop_tx_gsup(luop, &gsup);
199
200 llist_del(&luop->list);
201 talloc_free(luop);
202}
203
Harald Welte99909272016-05-05 18:24:15 +0200204/* timer call-back in case LU operation doesn't receive an response */
Harald Weltee687be52016-05-03 18:49:27 +0200205static void lu_op_timer_cb(void *data)
206{
207 struct lu_operation *luop = data;
208
209 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
210 get_value_string(lu_state_names, luop->state));
211
212 switch (luop->state) {
213 case LU_S_CANCEL_SENT:
214 break;
215 case LU_S_ISD_SENT:
216 break;
217 default:
218 break;
219 }
220
221 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
222}
223
224/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
225void lu_op_tx_ack(struct lu_operation *luop)
226{
227 struct osmo_gsup_message gsup;
228
229 memset(&gsup, 0, sizeof(gsup));
230 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT;
231 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
232 //FIXME gsup.hlr_enc;
233
234 _luop_tx_gsup(luop, &gsup);
235
236 llist_del(&luop->list);
237 talloc_free(luop);
238}
239
240/*! Send Cancel Location to old VLR/SGSN */
241void lu_op_tx_cancel_old(struct lu_operation *luop)
242{
243 struct osmo_gsup_message gsup;
244
245 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
246
247 memset(&gsup, 0, sizeof(gsup));
248 gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST;
249 //gsup.cause = FIXME;
250 //gsup.cancel_type = FIXME;
251
252 _luop_tx_gsup(luop, &gsup);
253
254 lu_op_statechg(luop, LU_S_CANCEL_SENT);
255 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
256}
257
258/*! Receive Cancel Location Result from old VLR/SGSN */
259void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
260 const struct osmo_gsup_message *gsup)
261{
262 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
263 /* FIXME: Check for spoofing */
264
265 osmo_timer_del(&luop->timer);
266
267 /* FIXME */
268
269 lu_op_tx_insert_subscr_data(luop);
270}
271
272/*! Transmit Insert Subscriber Data to new VLR/SGSN */
273void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
274{
275 struct osmo_gsup_message gsup;
Max2fc63a62016-12-20 16:49:20 +0100276 uint8_t apn[APN_MAXLEN];
Neels Hofmeyr627de842016-12-19 13:16:06 +0100277 uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */
Max2fc63a62016-12-20 16:49:20 +0100278 int l;
Harald Weltee687be52016-05-03 18:49:27 +0200279
280 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
281 luop->state == LU_S_CANCEL_ACK_RECEIVED);
282
283 memset(&gsup, 0, sizeof(gsup));
284 gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
285 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
Neels Hofmeyr627de842016-12-19 13:16:06 +0100286
287 l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0,
288 luop->subscr.msisdn);
289 if (l < 1) {
290 LOGP(DMAIN, LOGL_ERROR,
291 "%s: Error: cannot encode MSISDN '%s'\n",
292 luop->subscr.imsi, luop->subscr.msisdn);
293 lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC);
294 return;
295 }
296 gsup.msisdn_enc = msisdn_enc;
297 gsup.msisdn_enc_len = l;
298
Harald Welte99909272016-05-05 18:24:15 +0200299 /* FIXME: deal with encoding the following data */
Harald Weltee687be52016-05-03 18:49:27 +0200300 gsup.hlr_enc;
301
302 if (luop->is_ps) {
Max2fc63a62016-12-20 16:49:20 +0100303 /* FIXME: PDP infos - use more fine-grained access control
304 instead of wildcard APN */
305 l = osmo_apn_from_str(apn, sizeof(apn), "*");
306 if (l > 0) {
307 gsup.pdp_infos[0].apn_enc = apn;
308 gsup.pdp_infos[0].apn_enc_len = l;
309 gsup.pdp_infos[0].have_info = 1;
310 gsup.num_pdp_infos = 1;
311 /* FIXME: use real value: */
312 gsup.pdp_infos[0].context_id = 1;
313 }
Harald Weltee687be52016-05-03 18:49:27 +0200314 }
315
316 /* Send ISD to new VLR/SGSN */
317 _luop_tx_gsup(luop, &gsup);
318
319 lu_op_statechg(luop, LU_S_ISD_SENT);
320 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
321}
322
323/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
324static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
325 const struct osmo_gsup_message *gsup)
326{
327 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
328 /* FIXME: Check for spoofing */
329
330 osmo_timer_del(&luop->timer);
331
332 /* Subscriber_Present_HLR */
333 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
334
335 /* Send final ACK towards inquiring VLR/SGSN */
336 lu_op_tx_ack(luop);
337}
338
339/*! Receive GSUP message for given \ref lu_operation */
340void lu_op_rx_gsup(struct lu_operation *luop,
341 const struct osmo_gsup_message *gsup)
342{
343 switch (gsup->message_type) {
344 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
345 /* FIXME */
346 break;
347 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
348 lu_op_rx_insert_subscr_data_ack(luop, gsup);
349 break;
350 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
351 /* FIXME */
352 break;
353 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
354 lu_op_rx_cancel_old_ack(luop, gsup);
355 break;
356 default:
357 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
358 gsup->message_type);
359 break;
360 }
361}
362
363static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
364{
365 struct lu_operation *luop;
366
367 luop = talloc_zero(srv, struct lu_operation);
368 OSMO_ASSERT(luop);
369 luop->gsup_server = srv;
370 luop->timer.cb = lu_op_timer_cb;
371 luop->timer.data = luop;
372
373 return luop;
374}
375
376/*! Receive Update Location Request, creates new \ref lu_operation */
377static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
378 const struct osmo_gsup_message *gsup)
379{
380 int rc;
Harald Weltee687be52016-05-03 18:49:27 +0200381 struct lu_operation *luop;
382 struct hlr_subscriber *subscr;
383 uint8_t *peer_addr;
384
385 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
386 if (rc < 0) {
387 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
388 return rc;
389 }
390
391 luop = lu_op_alloc(conn->server);
392 luop->peer = talloc_memdup(luop, peer_addr, rc);
393 lu_op_statechg(luop, LU_S_LU_RECEIVED);
394 subscr = &luop->subscr;
395 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
396 luop->is_ps = true;
397 llist_add(&luop->list, &g_lu_ops);
398
399 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
400
401 /* check if subscriber is known at all */
402 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
403 if (rc < 0) {
404 /* Send Error back: Subscriber Unknown in HLR */
405 strcpy(luop->subscr.imsi, gsup->imsi);
406 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
407 return 0;
408 }
409
Harald Welte99909272016-05-05 18:24:15 +0200410 /* Check if subscriber is generally permitted on CS or PS
411 * service (as requested) */
Harald Welte53b86782016-05-05 21:04:11 +0200412 if (!luop->is_ps && !subscr->nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200413 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
414 return 0;
Harald Welte53b86782016-05-05 21:04:11 +0200415 } else if (luop->is_ps && !subscr->nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200416 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
417 return 0;
418 }
419
420 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
421
422#if 0
423 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
424 if (luop->is_ps == false &&
425 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200426 lu_op_tx_cancel_old(luop);
427 } else if (luop->is_ps == true &&
428 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200429 lu_op_tx_cancel_old(luop);
430 } else
431#endif
432 {
433 /* TODO: Subscriber allowed to roam in PLMN? */
434 /* TODO: Update RoutingInfo */
435 /* TODO: Reset Flag MS Purged (cs/ps) */
436 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
437 lu_op_tx_insert_subscr_data(luop);
438 }
439 return 0;
440}
441
Harald Welteb18f0e02016-05-05 21:03:03 +0200442static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
443 const struct osmo_gsup_message *gsup)
444{
445 struct osmo_gsup_message gsup_reply = {0};
446 struct msgb *msg_out;
447 bool is_ps = false;
448 int rc;
449
450 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
451 is_ps ? "PS" : "CS");
452
453 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
454
455 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
456 is_ps = true;
457
458 /* FIXME: check if the VLR that sends the purge is the same that
459 * we have on record. Only update if yes */
460
461 /* Perform the actual update of the DB */
462 rc = db_subscr_purge(g_dbc, gsup->imsi, is_ps);
463
464 if (rc == 1)
465 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
466 else if (rc == 0) {
467 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
468 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
469 } else {
470 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
471 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
472 }
473
474 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
475 osmo_gsup_encode(msg_out, &gsup_reply);
476 return osmo_gsup_conn_send(conn, msg_out);
477}
478
Harald Weltee72cf552016-04-28 07:18:49 +0200479static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
480{
481 static struct osmo_gsup_message gsup;
482 int rc;
483
Harald Weltee687be52016-05-03 18:49:27 +0200484 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200485 if (rc < 0) {
486 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
487 return rc;
488 }
489
490 switch (gsup.message_type) {
491 /* requests sent to us */
492 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
493 rx_send_auth_info(conn, &gsup);
494 break;
495 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200496 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200497 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200498 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
499 rx_purge_ms_req(conn, &gsup);
500 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200501 /* responses to requests sent by us */
502 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200503 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200504 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
505 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
506 {
507 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
508 if (!luop) {
509 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
510 "unknown IMSI %s\n", gsup.message_type,
511 gsup.imsi);
512 break;
513 }
514 lu_op_rx_gsup(luop, &gsup);
515 }
Harald Weltee72cf552016-04-28 07:18:49 +0200516 break;
517 default:
518 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
519 gsup.message_type);
520 break;
521 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200522 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200523 return 0;
524}
525
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100526static void print_usage()
527{
528 printf("Usage: osmo-hlr\n");
529}
530
531static void print_help()
532{
533 printf(" -h --help This text.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100534 printf(" -c --config-file filename The config file to use.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100535 printf(" -l --database db-name The database to use.\n");
536 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
537 printf(" -D --daemonize Fork the process into a background daemon.\n");
538 printf(" -s --disable-color Do not print ANSI colors in the log\n");
539 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
540 printf(" -e --log-level number Set a global loglevel.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100541 printf(" -V --version Print the version of OsmoHLR.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100542}
543
544static struct {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100545 const char *config_file;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100546 const char *db_file;
547 bool daemonize;
548} cmdline_opts = {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100549 .config_file = "osmo-hlr.cfg",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100550 .db_file = "hlr.db",
551 .daemonize = false,
552};
553
554static void handle_options(int argc, char **argv)
555{
556 while (1) {
557 int option_index = 0, c;
558 static struct option long_options[] = {
559 {"help", 0, 0, 'h'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100560 {"config-file", 1, 0, 'c'},
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100561 {"database", 1, 0, 'l'},
562 {"debug", 1, 0, 'd'},
563 {"daemonize", 0, 0, 'D'},
564 {"disable-color", 0, 0, 's'},
565 {"log-level", 1, 0, 'e'},
566 {"timestamp", 0, 0, 'T'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100567 {"version", 0, 0, 'V' },
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100568 {0, 0, 0, 0}
569 };
570
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100571 c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100572 long_options, &option_index);
573 if (c == -1)
574 break;
575
576 switch (c) {
577 case 'h':
578 print_usage();
579 print_help();
580 exit(0);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100581 case 'c':
582 cmdline_opts.config_file = optarg;
583 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100584 case 'l':
585 cmdline_opts.db_file = optarg;
586 break;
587 case 'd':
588 log_parse_category_mask(osmo_stderr_target, optarg);
589 break;
590 case 'D':
591 cmdline_opts.daemonize = 1;
592 break;
593 case 's':
594 log_set_use_color(osmo_stderr_target, 0);
595 break;
596 case 'e':
597 log_set_log_level(osmo_stderr_target, atoi(optarg));
598 break;
599 case 'T':
600 log_set_print_timestamp(osmo_stderr_target, 1);
601 break;
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100602 case 'V':
603 print_version(1);
604 exit(0);
605 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100606 default:
607 /* catch unknown options *as well as* missing arguments. */
608 fprintf(stderr, "Error in command line options. Exiting.\n");
609 exit(-1);
610 break;
611 }
612 }
613}
614
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100615static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200616static struct osmo_gsup_server *gs;
617
618static void signal_hdlr(int signal)
619{
620 switch (signal) {
621 case SIGINT:
622 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
623 osmo_gsup_server_destroy(gs);
624 db_close(g_dbc);
625 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100626 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200627 exit(0);
628 break;
629 case SIGUSR1:
630 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100631 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200632 break;
633 }
634}
Harald Weltee72cf552016-04-28 07:18:49 +0200635
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100636static struct vty_app_info vty_info = {
637 .name = "OsmoHLR",
638 .version = PACKAGE_VERSION,
639 .is_config_node = hlr_vty_is_config_node,
640};
641
Harald Weltee72cf552016-04-28 07:18:49 +0200642int main(int argc, char **argv)
643{
Harald Weltee72cf552016-04-28 07:18:49 +0200644 int rc;
645
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100646 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
647 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welteaabae9e2016-04-28 12:48:14 +0200648
Harald Weltee72cf552016-04-28 07:18:49 +0200649 rc = osmo_init_logging(&hlr_log_info);
650 if (rc < 0) {
651 fprintf(stderr, "Error initializing logging\n");
652 exit(1);
653 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100654
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100655 vty_init(&vty_info);
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100656 handle_options(argc, argv);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100657 hlr_vty_init(&hlr_log_info);
658
659 rc = vty_read_config_file(cmdline_opts.config_file, NULL);
660 if (rc < 0) {
661 LOGP(DMAIN, LOGL_FATAL,
662 "Failed to parse the config file: '%s'\n",
663 cmdline_opts.config_file);
664 return rc;
665 }
666
667 /* start telnet after reading config for vty_get_bind_addr() */
668 rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
669 OSMO_VTY_PORT_HLR);
670 if (rc < 0)
671 return rc;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100672
Harald Weltee72cf552016-04-28 07:18:49 +0200673 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
674
675 rc = rand_init();
676 if (rc < 0) {
677 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
678 exit(1);
679 }
680
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100681 g_dbc = db_open(hlr_ctx, cmdline_opts.db_file);
Harald Weltee72cf552016-04-28 07:18:49 +0200682 if (!g_dbc) {
683 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
684 exit(1);
685 }
686
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100687 gs = osmo_gsup_server_create(hlr_ctx, NULL, 2222, read_cb);
Harald Weltee72cf552016-04-28 07:18:49 +0200688 if (!gs) {
689 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
690 exit(1);
691 }
692
Harald Welteaabae9e2016-04-28 12:48:14 +0200693 osmo_init_ignore_signals();
694 signal(SIGINT, &signal_hdlr);
695 signal(SIGUSR1, &signal_hdlr);
696
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100697 if (cmdline_opts.daemonize) {
698 rc = osmo_daemonize();
699 if (rc < 0) {
700 perror("Error during daemonize");
701 exit(1);
702 }
703 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200704
Harald Weltee72cf552016-04-28 07:18:49 +0200705 while (1) {
706 osmo_select_main(0);
707 }
708
709 db_close(g_dbc);
710
711 log_fini();
712
713 exit(0);
714}