blob: 6420cc5c6ffff6aa7f29becc0db9182afdc7bdf9 [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
Max27c6b902017-02-13 15:55:03 +0100184static inline void fill_gsup_msg(struct osmo_gsup_message *out,
185 const struct lu_operation *lu,
186 enum osmo_gsup_message_type mt)
187{
188 memset(out, 0, sizeof(struct osmo_gsup_message));
189 if (lu)
190 osmo_strlcpy(out->imsi, lu->subscr.imsi,
191 GSM23003_IMSI_MAX_DIGITS + 1);
192 out->message_type = mt;
193}
194
Harald Weltee687be52016-05-03 18:49:27 +0200195/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
196void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
197{
198 struct osmo_gsup_message gsup;
199
Maxf8c7b6f2017-02-13 15:53:38 +0100200 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause %s)\n",
201 luop->subscr.imsi, get_value_string(gsm48_gmm_cause_names,
202 cause));
Harald Weltee687be52016-05-03 18:49:27 +0200203
Max27c6b902017-02-13 15:55:03 +0100204 fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR);
Harald Weltee687be52016-05-03 18:49:27 +0200205 gsup.cause = cause;
206
207 _luop_tx_gsup(luop, &gsup);
208
209 llist_del(&luop->list);
210 talloc_free(luop);
211}
212
Harald Welte99909272016-05-05 18:24:15 +0200213/* timer call-back in case LU operation doesn't receive an response */
Harald Weltee687be52016-05-03 18:49:27 +0200214static void lu_op_timer_cb(void *data)
215{
216 struct lu_operation *luop = data;
217
218 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
219 get_value_string(lu_state_names, luop->state));
220
221 switch (luop->state) {
222 case LU_S_CANCEL_SENT:
223 break;
224 case LU_S_ISD_SENT:
225 break;
226 default:
227 break;
228 }
229
230 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
231}
232
233/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
234void lu_op_tx_ack(struct lu_operation *luop)
235{
236 struct osmo_gsup_message gsup;
237
Max27c6b902017-02-13 15:55:03 +0100238 fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT);
Harald Weltee687be52016-05-03 18:49:27 +0200239 //FIXME gsup.hlr_enc;
240
241 _luop_tx_gsup(luop, &gsup);
242
243 llist_del(&luop->list);
244 talloc_free(luop);
245}
246
247/*! Send Cancel Location to old VLR/SGSN */
248void lu_op_tx_cancel_old(struct lu_operation *luop)
249{
250 struct osmo_gsup_message gsup;
251
252 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
253
Max27c6b902017-02-13 15:55:03 +0100254 fill_gsup_msg(&gsup, NULL, OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST);
Harald Weltee687be52016-05-03 18:49:27 +0200255 //gsup.cause = FIXME;
256 //gsup.cancel_type = FIXME;
257
258 _luop_tx_gsup(luop, &gsup);
259
260 lu_op_statechg(luop, LU_S_CANCEL_SENT);
261 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
262}
263
264/*! Receive Cancel Location Result from old VLR/SGSN */
265void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
266 const struct osmo_gsup_message *gsup)
267{
268 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
269 /* FIXME: Check for spoofing */
270
271 osmo_timer_del(&luop->timer);
272
273 /* FIXME */
274
275 lu_op_tx_insert_subscr_data(luop);
276}
277
278/*! Transmit Insert Subscriber Data to new VLR/SGSN */
279void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
280{
281 struct osmo_gsup_message gsup;
Max2fc63a62016-12-20 16:49:20 +0100282 uint8_t apn[APN_MAXLEN];
Neels Hofmeyr627de842016-12-19 13:16:06 +0100283 uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */
Max2fc63a62016-12-20 16:49:20 +0100284 int l;
Harald Weltee687be52016-05-03 18:49:27 +0200285
286 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
287 luop->state == LU_S_CANCEL_ACK_RECEIVED);
288
Max27c6b902017-02-13 15:55:03 +0100289 fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_INSERT_DATA_REQUEST);
Neels Hofmeyr627de842016-12-19 13:16:06 +0100290
291 l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0,
292 luop->subscr.msisdn);
293 if (l < 1) {
294 LOGP(DMAIN, LOGL_ERROR,
295 "%s: Error: cannot encode MSISDN '%s'\n",
296 luop->subscr.imsi, luop->subscr.msisdn);
297 lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC);
298 return;
299 }
300 gsup.msisdn_enc = msisdn_enc;
301 gsup.msisdn_enc_len = l;
302
Harald Welte99909272016-05-05 18:24:15 +0200303 /* FIXME: deal with encoding the following data */
Harald Weltee687be52016-05-03 18:49:27 +0200304 gsup.hlr_enc;
305
306 if (luop->is_ps) {
Max2fc63a62016-12-20 16:49:20 +0100307 /* FIXME: PDP infos - use more fine-grained access control
308 instead of wildcard APN */
309 l = osmo_apn_from_str(apn, sizeof(apn), "*");
310 if (l > 0) {
311 gsup.pdp_infos[0].apn_enc = apn;
312 gsup.pdp_infos[0].apn_enc_len = l;
313 gsup.pdp_infos[0].have_info = 1;
314 gsup.num_pdp_infos = 1;
315 /* FIXME: use real value: */
316 gsup.pdp_infos[0].context_id = 1;
317 }
Harald Weltee687be52016-05-03 18:49:27 +0200318 }
319
320 /* Send ISD to new VLR/SGSN */
321 _luop_tx_gsup(luop, &gsup);
322
323 lu_op_statechg(luop, LU_S_ISD_SENT);
324 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
325}
326
327/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
328static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
329 const struct osmo_gsup_message *gsup)
330{
331 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
332 /* FIXME: Check for spoofing */
333
334 osmo_timer_del(&luop->timer);
335
336 /* Subscriber_Present_HLR */
337 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
338
339 /* Send final ACK towards inquiring VLR/SGSN */
340 lu_op_tx_ack(luop);
341}
342
343/*! Receive GSUP message for given \ref lu_operation */
344void lu_op_rx_gsup(struct lu_operation *luop,
345 const struct osmo_gsup_message *gsup)
346{
347 switch (gsup->message_type) {
348 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
349 /* FIXME */
350 break;
351 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
352 lu_op_rx_insert_subscr_data_ack(luop, gsup);
353 break;
354 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
355 /* FIXME */
356 break;
357 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
358 lu_op_rx_cancel_old_ack(luop, gsup);
359 break;
360 default:
361 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
362 gsup->message_type);
363 break;
364 }
365}
366
367static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
368{
369 struct lu_operation *luop;
370
371 luop = talloc_zero(srv, struct lu_operation);
372 OSMO_ASSERT(luop);
373 luop->gsup_server = srv;
374 luop->timer.cb = lu_op_timer_cb;
375 luop->timer.data = luop;
376
377 return luop;
378}
379
380/*! Receive Update Location Request, creates new \ref lu_operation */
381static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
382 const struct osmo_gsup_message *gsup)
383{
384 int rc;
Harald Weltee687be52016-05-03 18:49:27 +0200385 struct lu_operation *luop;
386 struct hlr_subscriber *subscr;
387 uint8_t *peer_addr;
388
389 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
390 if (rc < 0) {
391 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
392 return rc;
393 }
394
395 luop = lu_op_alloc(conn->server);
396 luop->peer = talloc_memdup(luop, peer_addr, rc);
397 lu_op_statechg(luop, LU_S_LU_RECEIVED);
398 subscr = &luop->subscr;
399 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
400 luop->is_ps = true;
401 llist_add(&luop->list, &g_lu_ops);
402
403 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
404
405 /* check if subscriber is known at all */
406 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
407 if (rc < 0) {
408 /* Send Error back: Subscriber Unknown in HLR */
409 strcpy(luop->subscr.imsi, gsup->imsi);
410 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
411 return 0;
412 }
413
Harald Welte99909272016-05-05 18:24:15 +0200414 /* Check if subscriber is generally permitted on CS or PS
415 * service (as requested) */
Harald Welte53b86782016-05-05 21:04:11 +0200416 if (!luop->is_ps && !subscr->nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200417 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
418 return 0;
Harald Welte53b86782016-05-05 21:04:11 +0200419 } else if (luop->is_ps && !subscr->nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200420 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
421 return 0;
422 }
423
424 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
425
426#if 0
427 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
428 if (luop->is_ps == false &&
429 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200430 lu_op_tx_cancel_old(luop);
431 } else if (luop->is_ps == true &&
432 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200433 lu_op_tx_cancel_old(luop);
434 } else
435#endif
436 {
437 /* TODO: Subscriber allowed to roam in PLMN? */
438 /* TODO: Update RoutingInfo */
439 /* TODO: Reset Flag MS Purged (cs/ps) */
440 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
441 lu_op_tx_insert_subscr_data(luop);
442 }
443 return 0;
444}
445
Harald Welteb18f0e02016-05-05 21:03:03 +0200446static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
447 const struct osmo_gsup_message *gsup)
448{
449 struct osmo_gsup_message gsup_reply = {0};
450 struct msgb *msg_out;
451 bool is_ps = false;
452 int rc;
453
454 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
455 is_ps ? "PS" : "CS");
456
457 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
458
459 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
460 is_ps = true;
461
462 /* FIXME: check if the VLR that sends the purge is the same that
463 * we have on record. Only update if yes */
464
465 /* Perform the actual update of the DB */
466 rc = db_subscr_purge(g_dbc, gsup->imsi, is_ps);
467
468 if (rc == 1)
469 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
470 else if (rc == 0) {
471 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
472 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
473 } else {
474 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
475 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
476 }
477
478 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
479 osmo_gsup_encode(msg_out, &gsup_reply);
480 return osmo_gsup_conn_send(conn, msg_out);
481}
482
Harald Weltee72cf552016-04-28 07:18:49 +0200483static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
484{
485 static struct osmo_gsup_message gsup;
486 int rc;
487
Harald Weltee687be52016-05-03 18:49:27 +0200488 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200489 if (rc < 0) {
490 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
491 return rc;
492 }
493
494 switch (gsup.message_type) {
495 /* requests sent to us */
496 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
497 rx_send_auth_info(conn, &gsup);
498 break;
499 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200500 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200501 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200502 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
503 rx_purge_ms_req(conn, &gsup);
504 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200505 /* responses to requests sent by us */
506 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200507 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200508 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
509 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
510 {
511 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
512 if (!luop) {
Maxaa0fefd2017-02-16 12:25:22 +0100513 LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "
514 "unknown IMSI %s\n",
515 osmo_gsup_message_type_name(gsup.message_type),
Harald Weltee687be52016-05-03 18:49:27 +0200516 gsup.imsi);
517 break;
518 }
519 lu_op_rx_gsup(luop, &gsup);
520 }
Harald Weltee72cf552016-04-28 07:18:49 +0200521 break;
522 default:
Maxaa0fefd2017-02-16 12:25:22 +0100523 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
524 osmo_gsup_message_type_name(gsup.message_type));
Harald Weltee72cf552016-04-28 07:18:49 +0200525 break;
526 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200527 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200528 return 0;
529}
530
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100531static void print_usage()
532{
533 printf("Usage: osmo-hlr\n");
534}
535
536static void print_help()
537{
538 printf(" -h --help This text.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100539 printf(" -c --config-file filename The config file to use.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100540 printf(" -l --database db-name The database to use.\n");
541 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
542 printf(" -D --daemonize Fork the process into a background daemon.\n");
543 printf(" -s --disable-color Do not print ANSI colors in the log\n");
544 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
545 printf(" -e --log-level number Set a global loglevel.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100546 printf(" -V --version Print the version of OsmoHLR.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100547}
548
549static struct {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100550 const char *config_file;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100551 const char *db_file;
552 bool daemonize;
553} cmdline_opts = {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100554 .config_file = "osmo-hlr.cfg",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100555 .db_file = "hlr.db",
556 .daemonize = false,
557};
558
559static void handle_options(int argc, char **argv)
560{
561 while (1) {
562 int option_index = 0, c;
563 static struct option long_options[] = {
564 {"help", 0, 0, 'h'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100565 {"config-file", 1, 0, 'c'},
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100566 {"database", 1, 0, 'l'},
567 {"debug", 1, 0, 'd'},
568 {"daemonize", 0, 0, 'D'},
569 {"disable-color", 0, 0, 's'},
570 {"log-level", 1, 0, 'e'},
571 {"timestamp", 0, 0, 'T'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100572 {"version", 0, 0, 'V' },
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100573 {0, 0, 0, 0}
574 };
575
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100576 c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100577 long_options, &option_index);
578 if (c == -1)
579 break;
580
581 switch (c) {
582 case 'h':
583 print_usage();
584 print_help();
585 exit(0);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100586 case 'c':
587 cmdline_opts.config_file = optarg;
588 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100589 case 'l':
590 cmdline_opts.db_file = optarg;
591 break;
592 case 'd':
593 log_parse_category_mask(osmo_stderr_target, optarg);
594 break;
595 case 'D':
596 cmdline_opts.daemonize = 1;
597 break;
598 case 's':
599 log_set_use_color(osmo_stderr_target, 0);
600 break;
601 case 'e':
602 log_set_log_level(osmo_stderr_target, atoi(optarg));
603 break;
604 case 'T':
605 log_set_print_timestamp(osmo_stderr_target, 1);
606 break;
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100607 case 'V':
608 print_version(1);
609 exit(0);
610 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100611 default:
612 /* catch unknown options *as well as* missing arguments. */
613 fprintf(stderr, "Error in command line options. Exiting.\n");
614 exit(-1);
615 break;
616 }
617 }
618}
619
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100620static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200621static struct osmo_gsup_server *gs;
622
623static void signal_hdlr(int signal)
624{
625 switch (signal) {
626 case SIGINT:
627 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
628 osmo_gsup_server_destroy(gs);
629 db_close(g_dbc);
630 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100631 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200632 exit(0);
633 break;
634 case SIGUSR1:
635 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100636 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200637 break;
638 }
639}
Harald Weltee72cf552016-04-28 07:18:49 +0200640
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100641static struct vty_app_info vty_info = {
642 .name = "OsmoHLR",
643 .version = PACKAGE_VERSION,
644 .is_config_node = hlr_vty_is_config_node,
645};
646
Harald Weltee72cf552016-04-28 07:18:49 +0200647int main(int argc, char **argv)
648{
Harald Weltee72cf552016-04-28 07:18:49 +0200649 int rc;
650
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100651 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
652 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welteaabae9e2016-04-28 12:48:14 +0200653
Harald Weltee72cf552016-04-28 07:18:49 +0200654 rc = osmo_init_logging(&hlr_log_info);
655 if (rc < 0) {
656 fprintf(stderr, "Error initializing logging\n");
657 exit(1);
658 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100659
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100660 vty_init(&vty_info);
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100661 handle_options(argc, argv);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100662 hlr_vty_init(&hlr_log_info);
663
664 rc = vty_read_config_file(cmdline_opts.config_file, NULL);
665 if (rc < 0) {
666 LOGP(DMAIN, LOGL_FATAL,
667 "Failed to parse the config file: '%s'\n",
668 cmdline_opts.config_file);
669 return rc;
670 }
671
672 /* start telnet after reading config for vty_get_bind_addr() */
673 rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
674 OSMO_VTY_PORT_HLR);
675 if (rc < 0)
676 return rc;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100677
Harald Weltee72cf552016-04-28 07:18:49 +0200678 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
679
680 rc = rand_init();
681 if (rc < 0) {
682 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
683 exit(1);
684 }
685
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100686 g_dbc = db_open(hlr_ctx, cmdline_opts.db_file);
Harald Weltee72cf552016-04-28 07:18:49 +0200687 if (!g_dbc) {
688 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
689 exit(1);
690 }
691
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100692 gs = osmo_gsup_server_create(hlr_ctx, NULL, 2222, read_cb);
Harald Weltee72cf552016-04-28 07:18:49 +0200693 if (!gs) {
694 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
695 exit(1);
696 }
697
Harald Welteaabae9e2016-04-28 12:48:14 +0200698 osmo_init_ignore_signals();
699 signal(SIGINT, &signal_hdlr);
700 signal(SIGUSR1, &signal_hdlr);
701
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100702 if (cmdline_opts.daemonize) {
703 rc = osmo_daemonize();
704 if (rc < 0) {
705 perror("Error during daemonize");
706 exit(1);
707 }
708 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200709
Harald Weltee72cf552016-04-28 07:18:49 +0200710 while (1) {
711 osmo_select_main(0);
712 }
713
714 db_close(g_dbc);
715
716 log_fini();
717
718 exit(0);
719}