blob: 3b17708b1d84f1f9a634712a5ec4e1e556f9464f [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;
345 bool is_ps;
346 struct lu_operation *luop;
347 struct hlr_subscriber *subscr;
348 uint8_t *peer_addr;
349
350 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
351 if (rc < 0) {
352 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
353 return rc;
354 }
355
356 luop = lu_op_alloc(conn->server);
357 luop->peer = talloc_memdup(luop, peer_addr, rc);
358 lu_op_statechg(luop, LU_S_LU_RECEIVED);
359 subscr = &luop->subscr;
360 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
361 luop->is_ps = true;
362 llist_add(&luop->list, &g_lu_ops);
363
364 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
365
366 /* check if subscriber is known at all */
367 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
368 if (rc < 0) {
369 /* Send Error back: Subscriber Unknown in HLR */
370 strcpy(luop->subscr.imsi, gsup->imsi);
371 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
372 return 0;
373 }
374
Harald Welte99909272016-05-05 18:24:15 +0200375 /* Check if subscriber is generally permitted on CS or PS
376 * service (as requested) */
Harald Weltee687be52016-05-03 18:49:27 +0200377 if (!is_ps && !subscr->nam_cs) {
378 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
379 return 0;
380 } else if (is_ps && !subscr->nam_ps) {
381 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
382 return 0;
383 }
384
385 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
386
387#if 0
388 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
389 if (luop->is_ps == false &&
390 strcmp(subscr->vlr_number, vlr_number)) {
391 /* FIXME: start location cancel towards old VLR */
392 lu_op_tx_cancel_old(luop);
393 } else if (luop->is_ps == true &&
394 strcmp(subscr->sgsn_number, sgsn_number)) {
395 /* FIXME: start location cancel towards old VLR */
396 lu_op_tx_cancel_old(luop);
397 } else
398#endif
399 {
400 /* TODO: Subscriber allowed to roam in PLMN? */
401 /* TODO: Update RoutingInfo */
402 /* TODO: Reset Flag MS Purged (cs/ps) */
403 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
404 lu_op_tx_insert_subscr_data(luop);
405 }
406 return 0;
407}
408
Harald Weltee72cf552016-04-28 07:18:49 +0200409static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
410{
411 static struct osmo_gsup_message gsup;
412 int rc;
413
Harald Weltee687be52016-05-03 18:49:27 +0200414 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200415 if (rc < 0) {
416 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
417 return rc;
418 }
419
420 switch (gsup.message_type) {
421 /* requests sent to us */
422 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
423 rx_send_auth_info(conn, &gsup);
424 break;
425 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200426 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200427 break;
428 /* responses to requests sent by us */
429 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200430 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200431 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
432 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
433 {
434 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
435 if (!luop) {
436 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
437 "unknown IMSI %s\n", gsup.message_type,
438 gsup.imsi);
439 break;
440 }
441 lu_op_rx_gsup(luop, &gsup);
442 }
Harald Weltee72cf552016-04-28 07:18:49 +0200443 break;
444 default:
445 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
446 gsup.message_type);
447 break;
448 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200449 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200450 return 0;
451}
452
Harald Welteaabae9e2016-04-28 12:48:14 +0200453static struct osmo_gsup_server *gs;
454
455static void signal_hdlr(int signal)
456{
457 switch (signal) {
458 case SIGINT:
459 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
460 osmo_gsup_server_destroy(gs);
461 db_close(g_dbc);
462 log_fini();
463 exit(0);
464 break;
465 case SIGUSR1:
466 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
467 talloc_report_full(NULL, stderr);
468 break;
469 }
470}
Harald Weltee72cf552016-04-28 07:18:49 +0200471
472int main(int argc, char **argv)
473{
Harald Weltee72cf552016-04-28 07:18:49 +0200474 int rc;
475
Harald Welteaabae9e2016-04-28 12:48:14 +0200476 talloc_enable_leak_report_full();
477
Harald Weltee72cf552016-04-28 07:18:49 +0200478 rc = osmo_init_logging(&hlr_log_info);
479 if (rc < 0) {
480 fprintf(stderr, "Error initializing logging\n");
481 exit(1);
482 }
483 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
484
485 rc = rand_init();
486 if (rc < 0) {
487 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
488 exit(1);
489 }
490
491 g_dbc = db_open(NULL, "hlr.db");
492 if (!g_dbc) {
493 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
494 exit(1);
495 }
496
497 gs = osmo_gsup_server_create(NULL, NULL, 2222, read_cb);
498 if (!gs) {
499 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
500 exit(1);
501 }
502
Harald Welteaabae9e2016-04-28 12:48:14 +0200503 osmo_init_ignore_signals();
504 signal(SIGINT, &signal_hdlr);
505 signal(SIGUSR1, &signal_hdlr);
506
507 //osmo_daemonize();
508
Harald Weltee72cf552016-04-28 07:18:49 +0200509 while (1) {
510 osmo_select_main(0);
511 }
512
513 db_close(g_dbc);
514
515 log_fini();
516
517 exit(0);
518}