blob: 744d966ab94cb6a7b35f6c0fc9fcdff55a6c1e19 [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
Harald Weltee72cf552016-04-28 07:18:49 +020023#include <osmocom/core/msgb.h>
24#include <osmocom/core/logging.h>
25#include <osmocom/core/application.h>
26#include <osmocom/gsm/gsup.h>
27
28#include "db.h"
29#include "logging.h"
30#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020031#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020032#include "rand.h"
33
34static struct db_context *g_dbc;
35
Harald Weltee687be52016-05-03 18:49:27 +020036/***********************************************************************
37 * Send Auth Info handling
38 ***********************************************************************/
39
Harald Weltee72cf552016-04-28 07:18:49 +020040/* process an incoming SAI request */
41static int rx_send_auth_info(struct osmo_gsup_conn *conn,
42 const struct osmo_gsup_message *gsup)
43{
44 struct osmo_gsup_message gsup_out;
45 struct msgb *msg_out;
46 int rc;
47
48 /* initialize return message structure */
49 memset(&gsup_out, 0, sizeof(gsup_out));
Harald Weltee72cf552016-04-28 07:18:49 +020050 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
51
52 rc = db_get_auc(g_dbc, gsup->imsi, gsup_out.auth_vectors,
53 ARRAY_SIZE(gsup_out.auth_vectors),
54 NULL /* gsup->rand_auts */, gsup->auts);
Harald Weltecfc752b2016-05-05 16:38:14 +020055 if (rc < 0) {
Harald Weltee72cf552016-04-28 07:18:49 +020056 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
Harald Weltecfc752b2016-05-05 16:38:14 +020057 gsup_out.cause = GMM_CAUSE_NET_FAIL;
58 } else if (rc == 0) {
59 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
60 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
Harald Welte15db8262016-05-05 16:50:39 +020061 } else {
62 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
63 gsup_out.num_auth_vectors = rc;
Harald Weltee72cf552016-04-28 07:18:49 +020064 }
65
Harald Weltee687be52016-05-03 18:49:27 +020066 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +020067 osmo_gsup_encode(msg_out, &gsup_out);
68 return osmo_gsup_conn_send(conn, msg_out);
69}
70
Harald Weltee687be52016-05-03 18:49:27 +020071/***********************************************************************
72 * LU Operation State / Structure
73 ***********************************************************************/
74
75static LLIST_HEAD(g_lu_ops);
76
77#define CANCEL_TIMEOUT_SECS 30
78#define ISD_TIMEOUT_SECS 30
79
80enum lu_state {
81 LU_S_NULL,
82 LU_S_LU_RECEIVED,
83 LU_S_CANCEL_SENT,
84 LU_S_CANCEL_ACK_RECEIVED,
85 LU_S_ISD_SENT,
86 LU_S_ISD_ACK_RECEIVED,
87 LU_S_COMPLETE,
88};
89
90static const struct value_string lu_state_names[] = {
91 { LU_S_NULL, "NULL" },
92 { LU_S_LU_RECEIVED, "LU RECEIVED" },
93 { LU_S_CANCEL_SENT, "CANCEL SENT" },
94 { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" },
95 { LU_S_ISD_SENT, "ISD SENT" },
96 { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" },
97 { LU_S_COMPLETE, "COMPLETE" },
98 { 0, NULL }
99};
100
101struct lu_operation {
102 /*! entry in global list of location update operations */
103 struct llist_head list;
104 /*! to which gsup_server do we belong */
105 struct osmo_gsup_server *gsup_server;
106 /*! state of the location update */
107 enum lu_state state;
108 /*! CS (false) or PS (true) Location Update? */
109 bool is_ps;
110 /*! currently running timer */
111 struct osmo_timer_list timer;
112
113 /*! subscriber related to this operation */
114 struct hlr_subscriber subscr;
115 /*! peer VLR/SGSN starting the request */
116 uint8_t *peer;
117};
118
119void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
120{
121 enum lu_state old_state = luop->state;
122
123 DEBUGP(DMAIN, "LU OP state change: %s -> ",
124 get_value_string(lu_state_names, old_state));
125 DEBUGPC(DMAIN, "%s\n",
126 get_value_string(lu_state_names, new_state));
127
128 luop->state = new_state;
129}
130
131struct lu_operation *lu_op_by_imsi(const char *imsi)
132{
133 struct lu_operation *luop;
134
135 llist_for_each_entry(luop, &g_lu_ops, list) {
136 if (!strcmp(imsi, luop->subscr.imsi))
137 return luop;
138 }
139 return NULL;
140}
141
142/* Send a msgb to a given address using routing */
143int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
144 const uint8_t *addr, size_t addrlen,
145 struct msgb *msg)
146{
147 struct osmo_gsup_conn *conn;
148
149 conn = gsup_route_find(gs, addr, addrlen);
150 if (!conn) {
151 DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr);
152 msgb_free(msg);
153 return -ENODEV;
154 }
155
156 return osmo_gsup_conn_send(conn, msg);
157}
158
Harald Welte99909272016-05-05 18:24:15 +0200159/* Transmit a given GSUP message for the given LU operation */
Harald Weltee687be52016-05-03 18:49:27 +0200160static void _luop_tx_gsup(struct lu_operation *luop,
161 const struct osmo_gsup_message *gsup)
162{
163 struct msgb *msg_out;
164
165 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
166 osmo_gsup_encode(msg_out, gsup);
167
168 osmo_gsup_addr_send(luop->gsup_server, luop->peer,
169 talloc_total_size(luop->peer),
170 msg_out);
171}
172
173/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
174void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
175{
176 struct osmo_gsup_message gsup;
177
178 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n",
179 luop->subscr.imsi, cause);
180
181 memset(&gsup, 0, sizeof(gsup));
182 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
183 strncpy(&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi));
184 gsup.imsi[sizeof(gsup.imsi)-1] = '\0';
185 gsup.cause = cause;
186
187 _luop_tx_gsup(luop, &gsup);
188
189 llist_del(&luop->list);
190 talloc_free(luop);
191}
192
Harald Welte99909272016-05-05 18:24:15 +0200193/* timer call-back in case LU operation doesn't receive an response */
Harald Weltee687be52016-05-03 18:49:27 +0200194static void lu_op_timer_cb(void *data)
195{
196 struct lu_operation *luop = data;
197
198 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
199 get_value_string(lu_state_names, luop->state));
200
201 switch (luop->state) {
202 case LU_S_CANCEL_SENT:
203 break;
204 case LU_S_ISD_SENT:
205 break;
206 default:
207 break;
208 }
209
210 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
211}
212
213/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
214void lu_op_tx_ack(struct lu_operation *luop)
215{
216 struct osmo_gsup_message gsup;
217
218 memset(&gsup, 0, sizeof(gsup));
219 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT;
220 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
221 //FIXME gsup.hlr_enc;
222
223 _luop_tx_gsup(luop, &gsup);
224
225 llist_del(&luop->list);
226 talloc_free(luop);
227}
228
229/*! Send Cancel Location to old VLR/SGSN */
230void lu_op_tx_cancel_old(struct lu_operation *luop)
231{
232 struct osmo_gsup_message gsup;
233
234 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
235
236 memset(&gsup, 0, sizeof(gsup));
237 gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST;
238 //gsup.cause = FIXME;
239 //gsup.cancel_type = FIXME;
240
241 _luop_tx_gsup(luop, &gsup);
242
243 lu_op_statechg(luop, LU_S_CANCEL_SENT);
244 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
245}
246
247/*! Receive Cancel Location Result from old VLR/SGSN */
248void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
249 const struct osmo_gsup_message *gsup)
250{
251 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
252 /* FIXME: Check for spoofing */
253
254 osmo_timer_del(&luop->timer);
255
256 /* FIXME */
257
258 lu_op_tx_insert_subscr_data(luop);
259}
260
261/*! Transmit Insert Subscriber Data to new VLR/SGSN */
262void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
263{
264 struct osmo_gsup_message gsup;
265
266 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
267 luop->state == LU_S_CANCEL_ACK_RECEIVED);
268
269 memset(&gsup, 0, sizeof(gsup));
270 gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
271 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
Harald Welte99909272016-05-05 18:24:15 +0200272 /* FIXME: deal with encoding the following data */
Harald Weltee687be52016-05-03 18:49:27 +0200273 gsup.msisdn_enc;
274 gsup.hlr_enc;
275
276 if (luop->is_ps) {
277 /* FIXME: PDP infos */
278 }
279
280 /* Send ISD to new VLR/SGSN */
281 _luop_tx_gsup(luop, &gsup);
282
283 lu_op_statechg(luop, LU_S_ISD_SENT);
284 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
285}
286
287/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
288static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
289 const struct osmo_gsup_message *gsup)
290{
291 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
292 /* FIXME: Check for spoofing */
293
294 osmo_timer_del(&luop->timer);
295
296 /* Subscriber_Present_HLR */
297 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
298
299 /* Send final ACK towards inquiring VLR/SGSN */
300 lu_op_tx_ack(luop);
301}
302
303/*! Receive GSUP message for given \ref lu_operation */
304void lu_op_rx_gsup(struct lu_operation *luop,
305 const struct osmo_gsup_message *gsup)
306{
307 switch (gsup->message_type) {
308 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
309 /* FIXME */
310 break;
311 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
312 lu_op_rx_insert_subscr_data_ack(luop, gsup);
313 break;
314 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
315 /* FIXME */
316 break;
317 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
318 lu_op_rx_cancel_old_ack(luop, gsup);
319 break;
320 default:
321 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
322 gsup->message_type);
323 break;
324 }
325}
326
327static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
328{
329 struct lu_operation *luop;
330
331 luop = talloc_zero(srv, struct lu_operation);
332 OSMO_ASSERT(luop);
333 luop->gsup_server = srv;
334 luop->timer.cb = lu_op_timer_cb;
335 luop->timer.data = luop;
336
337 return luop;
338}
339
340/*! Receive Update Location Request, creates new \ref lu_operation */
341static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
342 const struct osmo_gsup_message *gsup)
343{
344 int rc;
Harald Weltee687be52016-05-03 18:49:27 +0200345 struct lu_operation *luop;
346 struct hlr_subscriber *subscr;
347 uint8_t *peer_addr;
348
349 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
350 if (rc < 0) {
351 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
352 return rc;
353 }
354
355 luop = lu_op_alloc(conn->server);
356 luop->peer = talloc_memdup(luop, peer_addr, rc);
357 lu_op_statechg(luop, LU_S_LU_RECEIVED);
358 subscr = &luop->subscr;
359 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
360 luop->is_ps = true;
361 llist_add(&luop->list, &g_lu_ops);
362
363 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
364
365 /* check if subscriber is known at all */
366 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
367 if (rc < 0) {
368 /* Send Error back: Subscriber Unknown in HLR */
369 strcpy(luop->subscr.imsi, gsup->imsi);
370 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
371 return 0;
372 }
373
Harald Welte99909272016-05-05 18:24:15 +0200374 /* Check if subscriber is generally permitted on CS or PS
375 * service (as requested) */
Harald Welte53b86782016-05-05 21:04:11 +0200376 if (!luop->is_ps && !subscr->nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200377 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
378 return 0;
Harald Welte53b86782016-05-05 21:04:11 +0200379 } else if (luop->is_ps && !subscr->nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200380 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
381 return 0;
382 }
383
384 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
385
386#if 0
387 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
388 if (luop->is_ps == false &&
389 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200390 lu_op_tx_cancel_old(luop);
391 } else if (luop->is_ps == true &&
392 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200393 lu_op_tx_cancel_old(luop);
394 } else
395#endif
396 {
397 /* TODO: Subscriber allowed to roam in PLMN? */
398 /* TODO: Update RoutingInfo */
399 /* TODO: Reset Flag MS Purged (cs/ps) */
400 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
401 lu_op_tx_insert_subscr_data(luop);
402 }
403 return 0;
404}
405
Harald Welteb18f0e02016-05-05 21:03:03 +0200406static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
407 const struct osmo_gsup_message *gsup)
408{
409 struct osmo_gsup_message gsup_reply = {0};
410 struct msgb *msg_out;
411 bool is_ps = false;
412 int rc;
413
414 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
415 is_ps ? "PS" : "CS");
416
417 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
418
419 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
420 is_ps = true;
421
422 /* FIXME: check if the VLR that sends the purge is the same that
423 * we have on record. Only update if yes */
424
425 /* Perform the actual update of the DB */
426 rc = db_subscr_purge(g_dbc, gsup->imsi, is_ps);
427
428 if (rc == 1)
429 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
430 else if (rc == 0) {
431 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
432 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
433 } else {
434 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
435 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
436 }
437
438 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
439 osmo_gsup_encode(msg_out, &gsup_reply);
440 return osmo_gsup_conn_send(conn, msg_out);
441}
442
Harald Weltee72cf552016-04-28 07:18:49 +0200443static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
444{
445 static struct osmo_gsup_message gsup;
446 int rc;
447
Harald Weltee687be52016-05-03 18:49:27 +0200448 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200449 if (rc < 0) {
450 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
451 return rc;
452 }
453
454 switch (gsup.message_type) {
455 /* requests sent to us */
456 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
457 rx_send_auth_info(conn, &gsup);
458 break;
459 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200460 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200461 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200462 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
463 rx_purge_ms_req(conn, &gsup);
464 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200465 /* responses to requests sent by us */
466 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200467 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200468 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
469 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
470 {
471 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
472 if (!luop) {
473 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
474 "unknown IMSI %s\n", gsup.message_type,
475 gsup.imsi);
476 break;
477 }
478 lu_op_rx_gsup(luop, &gsup);
479 }
Harald Weltee72cf552016-04-28 07:18:49 +0200480 break;
481 default:
482 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
483 gsup.message_type);
484 break;
485 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200486 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200487 return 0;
488}
489
Harald Welteaabae9e2016-04-28 12:48:14 +0200490static struct osmo_gsup_server *gs;
491
492static void signal_hdlr(int signal)
493{
494 switch (signal) {
495 case SIGINT:
496 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
497 osmo_gsup_server_destroy(gs);
498 db_close(g_dbc);
499 log_fini();
500 exit(0);
501 break;
502 case SIGUSR1:
503 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
504 talloc_report_full(NULL, stderr);
505 break;
506 }
507}
Harald Weltee72cf552016-04-28 07:18:49 +0200508
509int main(int argc, char **argv)
510{
Harald Weltee72cf552016-04-28 07:18:49 +0200511 int rc;
512
Harald Welteaabae9e2016-04-28 12:48:14 +0200513 talloc_enable_leak_report_full();
514
Harald Weltee72cf552016-04-28 07:18:49 +0200515 rc = osmo_init_logging(&hlr_log_info);
516 if (rc < 0) {
517 fprintf(stderr, "Error initializing logging\n");
518 exit(1);
519 }
520 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
521
522 rc = rand_init();
523 if (rc < 0) {
524 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
525 exit(1);
526 }
527
528 g_dbc = db_open(NULL, "hlr.db");
529 if (!g_dbc) {
530 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
531 exit(1);
532 }
533
534 gs = osmo_gsup_server_create(NULL, NULL, 2222, read_cb);
535 if (!gs) {
536 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
537 exit(1);
538 }
539
Harald Welteaabae9e2016-04-28 12:48:14 +0200540 osmo_init_ignore_signals();
541 signal(SIGINT, &signal_hdlr);
542 signal(SIGUSR1, &signal_hdlr);
543
544 //osmo_daemonize();
545
Harald Weltee72cf552016-04-28 07:18:49 +0200546 while (1) {
547 osmo_select_main(0);
548 }
549
550 db_close(g_dbc);
551
552 log_fini();
553
554 exit(0);
555}