blob: 206ddc196b7de27427edce3b4fffc2874299d021 [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) {
513 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
514 "unknown IMSI %s\n", gsup.message_type,
515 gsup.imsi);
516 break;
517 }
518 lu_op_rx_gsup(luop, &gsup);
519 }
Harald Weltee72cf552016-04-28 07:18:49 +0200520 break;
521 default:
522 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
523 gsup.message_type);
524 break;
525 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200526 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200527 return 0;
528}
529
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100530static void print_usage()
531{
532 printf("Usage: osmo-hlr\n");
533}
534
535static void print_help()
536{
537 printf(" -h --help This text.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100538 printf(" -c --config-file filename The config file to use.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100539 printf(" -l --database db-name The database to use.\n");
540 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
541 printf(" -D --daemonize Fork the process into a background daemon.\n");
542 printf(" -s --disable-color Do not print ANSI colors in the log\n");
543 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
544 printf(" -e --log-level number Set a global loglevel.\n");
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100545 printf(" -V --version Print the version of OsmoHLR.\n");
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100546}
547
548static struct {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100549 const char *config_file;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100550 const char *db_file;
551 bool daemonize;
552} cmdline_opts = {
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100553 .config_file = "osmo-hlr.cfg",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100554 .db_file = "hlr.db",
555 .daemonize = false,
556};
557
558static void handle_options(int argc, char **argv)
559{
560 while (1) {
561 int option_index = 0, c;
562 static struct option long_options[] = {
563 {"help", 0, 0, 'h'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100564 {"config-file", 1, 0, 'c'},
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100565 {"database", 1, 0, 'l'},
566 {"debug", 1, 0, 'd'},
567 {"daemonize", 0, 0, 'D'},
568 {"disable-color", 0, 0, 's'},
569 {"log-level", 1, 0, 'e'},
570 {"timestamp", 0, 0, 'T'},
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100571 {"version", 0, 0, 'V' },
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100572 {0, 0, 0, 0}
573 };
574
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100575 c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100576 long_options, &option_index);
577 if (c == -1)
578 break;
579
580 switch (c) {
581 case 'h':
582 print_usage();
583 print_help();
584 exit(0);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100585 case 'c':
586 cmdline_opts.config_file = optarg;
587 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100588 case 'l':
589 cmdline_opts.db_file = optarg;
590 break;
591 case 'd':
592 log_parse_category_mask(osmo_stderr_target, optarg);
593 break;
594 case 'D':
595 cmdline_opts.daemonize = 1;
596 break;
597 case 's':
598 log_set_use_color(osmo_stderr_target, 0);
599 break;
600 case 'e':
601 log_set_log_level(osmo_stderr_target, atoi(optarg));
602 break;
603 case 'T':
604 log_set_print_timestamp(osmo_stderr_target, 1);
605 break;
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100606 case 'V':
607 print_version(1);
608 exit(0);
609 break;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100610 default:
611 /* catch unknown options *as well as* missing arguments. */
612 fprintf(stderr, "Error in command line options. Exiting.\n");
613 exit(-1);
614 break;
615 }
616 }
617}
618
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100619static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200620static struct osmo_gsup_server *gs;
621
622static void signal_hdlr(int signal)
623{
624 switch (signal) {
625 case SIGINT:
626 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
627 osmo_gsup_server_destroy(gs);
628 db_close(g_dbc);
629 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100630 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200631 exit(0);
632 break;
633 case SIGUSR1:
634 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100635 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200636 break;
637 }
638}
Harald Weltee72cf552016-04-28 07:18:49 +0200639
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100640static struct vty_app_info vty_info = {
641 .name = "OsmoHLR",
642 .version = PACKAGE_VERSION,
643 .is_config_node = hlr_vty_is_config_node,
644};
645
Harald Weltee72cf552016-04-28 07:18:49 +0200646int main(int argc, char **argv)
647{
Harald Weltee72cf552016-04-28 07:18:49 +0200648 int rc;
649
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100650 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
651 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welteaabae9e2016-04-28 12:48:14 +0200652
Harald Weltee72cf552016-04-28 07:18:49 +0200653 rc = osmo_init_logging(&hlr_log_info);
654 if (rc < 0) {
655 fprintf(stderr, "Error initializing logging\n");
656 exit(1);
657 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100658
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100659 vty_init(&vty_info);
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100660 handle_options(argc, argv);
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100661 hlr_vty_init(&hlr_log_info);
662
663 rc = vty_read_config_file(cmdline_opts.config_file, NULL);
664 if (rc < 0) {
665 LOGP(DMAIN, LOGL_FATAL,
666 "Failed to parse the config file: '%s'\n",
667 cmdline_opts.config_file);
668 return rc;
669 }
670
671 /* start telnet after reading config for vty_get_bind_addr() */
672 rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
673 OSMO_VTY_PORT_HLR);
674 if (rc < 0)
675 return rc;
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100676
Harald Weltee72cf552016-04-28 07:18:49 +0200677 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
678
679 rc = rand_init();
680 if (rc < 0) {
681 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
682 exit(1);
683 }
684
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100685 g_dbc = db_open(hlr_ctx, cmdline_opts.db_file);
Harald Weltee72cf552016-04-28 07:18:49 +0200686 if (!g_dbc) {
687 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
688 exit(1);
689 }
690
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100691 gs = osmo_gsup_server_create(hlr_ctx, NULL, 2222, read_cb);
Harald Weltee72cf552016-04-28 07:18:49 +0200692 if (!gs) {
693 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
694 exit(1);
695 }
696
Harald Welteaabae9e2016-04-28 12:48:14 +0200697 osmo_init_ignore_signals();
698 signal(SIGINT, &signal_hdlr);
699 signal(SIGUSR1, &signal_hdlr);
700
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100701 if (cmdline_opts.daemonize) {
702 rc = osmo_daemonize();
703 if (rc < 0) {
704 perror("Error during daemonize");
705 exit(1);
706 }
707 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200708
Harald Weltee72cf552016-04-28 07:18:49 +0200709 while (1) {
710 osmo_select_main(0);
711 }
712
713 db_close(g_dbc);
714
715 log_fini();
716
717 exit(0);
718}