blob: f60b24b5d6497455245dffea1df81e17d75700fd [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
159static void _luop_tx_gsup(struct lu_operation *luop,
160 const struct osmo_gsup_message *gsup)
161{
162 struct msgb *msg_out;
163
164 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
165 osmo_gsup_encode(msg_out, gsup);
166
167 osmo_gsup_addr_send(luop->gsup_server, luop->peer,
168 talloc_total_size(luop->peer),
169 msg_out);
170}
171
172/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
173void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
174{
175 struct osmo_gsup_message gsup;
176
177 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n",
178 luop->subscr.imsi, cause);
179
180 memset(&gsup, 0, sizeof(gsup));
181 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
182 strncpy(&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi));
183 gsup.imsi[sizeof(gsup.imsi)-1] = '\0';
184 gsup.cause = cause;
185
186 _luop_tx_gsup(luop, &gsup);
187
188 llist_del(&luop->list);
189 talloc_free(luop);
190}
191
192static void lu_op_timer_cb(void *data)
193{
194 struct lu_operation *luop = data;
195
196 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
197 get_value_string(lu_state_names, luop->state));
198
199 switch (luop->state) {
200 case LU_S_CANCEL_SENT:
201 break;
202 case LU_S_ISD_SENT:
203 break;
204 default:
205 break;
206 }
207
208 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
209}
210
211/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
212void lu_op_tx_ack(struct lu_operation *luop)
213{
214 struct osmo_gsup_message gsup;
215
216 memset(&gsup, 0, sizeof(gsup));
217 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT;
218 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
219 //FIXME gsup.hlr_enc;
220
221 _luop_tx_gsup(luop, &gsup);
222
223 llist_del(&luop->list);
224 talloc_free(luop);
225}
226
227/*! Send Cancel Location to old VLR/SGSN */
228void lu_op_tx_cancel_old(struct lu_operation *luop)
229{
230 struct osmo_gsup_message gsup;
231
232 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
233
234 memset(&gsup, 0, sizeof(gsup));
235 gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST;
236 //gsup.cause = FIXME;
237 //gsup.cancel_type = FIXME;
238
239 _luop_tx_gsup(luop, &gsup);
240
241 lu_op_statechg(luop, LU_S_CANCEL_SENT);
242 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
243}
244
245/*! Receive Cancel Location Result from old VLR/SGSN */
246void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
247 const struct osmo_gsup_message *gsup)
248{
249 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
250 /* FIXME: Check for spoofing */
251
252 osmo_timer_del(&luop->timer);
253
254 /* FIXME */
255
256 lu_op_tx_insert_subscr_data(luop);
257}
258
259/*! Transmit Insert Subscriber Data to new VLR/SGSN */
260void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
261{
262 struct osmo_gsup_message gsup;
263
264 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
265 luop->state == LU_S_CANCEL_ACK_RECEIVED);
266
267 memset(&gsup, 0, sizeof(gsup));
268 gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
269 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
270 gsup.msisdn_enc;
271 gsup.hlr_enc;
272
273 if (luop->is_ps) {
274 /* FIXME: PDP infos */
275 }
276
277 /* Send ISD to new VLR/SGSN */
278 _luop_tx_gsup(luop, &gsup);
279
280 lu_op_statechg(luop, LU_S_ISD_SENT);
281 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
282}
283
284/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
285static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
286 const struct osmo_gsup_message *gsup)
287{
288 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
289 /* FIXME: Check for spoofing */
290
291 osmo_timer_del(&luop->timer);
292
293 /* Subscriber_Present_HLR */
294 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
295
296 /* Send final ACK towards inquiring VLR/SGSN */
297 lu_op_tx_ack(luop);
298}
299
300/*! Receive GSUP message for given \ref lu_operation */
301void lu_op_rx_gsup(struct lu_operation *luop,
302 const struct osmo_gsup_message *gsup)
303{
304 switch (gsup->message_type) {
305 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
306 /* FIXME */
307 break;
308 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
309 lu_op_rx_insert_subscr_data_ack(luop, gsup);
310 break;
311 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
312 /* FIXME */
313 break;
314 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
315 lu_op_rx_cancel_old_ack(luop, gsup);
316 break;
317 default:
318 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
319 gsup->message_type);
320 break;
321 }
322}
323
324static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
325{
326 struct lu_operation *luop;
327
328 luop = talloc_zero(srv, struct lu_operation);
329 OSMO_ASSERT(luop);
330 luop->gsup_server = srv;
331 luop->timer.cb = lu_op_timer_cb;
332 luop->timer.data = luop;
333
334 return luop;
335}
336
337/*! Receive Update Location Request, creates new \ref lu_operation */
338static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
339 const struct osmo_gsup_message *gsup)
340{
341 int rc;
342 bool is_ps;
343 struct lu_operation *luop;
344 struct hlr_subscriber *subscr;
345 uint8_t *peer_addr;
346
347 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
348 if (rc < 0) {
349 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
350 return rc;
351 }
352
353 luop = lu_op_alloc(conn->server);
354 luop->peer = talloc_memdup(luop, peer_addr, rc);
355 lu_op_statechg(luop, LU_S_LU_RECEIVED);
356 subscr = &luop->subscr;
357 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
358 luop->is_ps = true;
359 llist_add(&luop->list, &g_lu_ops);
360
361 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
362
363 /* check if subscriber is known at all */
364 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
365 if (rc < 0) {
366 /* Send Error back: Subscriber Unknown in HLR */
367 strcpy(luop->subscr.imsi, gsup->imsi);
368 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
369 return 0;
370 }
371
372 if (!is_ps && !subscr->nam_cs) {
373 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
374 return 0;
375 } else if (is_ps && !subscr->nam_ps) {
376 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
377 return 0;
378 }
379
380 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
381
382#if 0
383 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
384 if (luop->is_ps == false &&
385 strcmp(subscr->vlr_number, vlr_number)) {
386 /* FIXME: start location cancel towards old VLR */
387 lu_op_tx_cancel_old(luop);
388 } else if (luop->is_ps == true &&
389 strcmp(subscr->sgsn_number, sgsn_number)) {
390 /* FIXME: start location cancel towards old VLR */
391 lu_op_tx_cancel_old(luop);
392 } else
393#endif
394 {
395 /* TODO: Subscriber allowed to roam in PLMN? */
396 /* TODO: Update RoutingInfo */
397 /* TODO: Reset Flag MS Purged (cs/ps) */
398 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
399 lu_op_tx_insert_subscr_data(luop);
400 }
401 return 0;
402}
403
Harald Weltee72cf552016-04-28 07:18:49 +0200404static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
405{
406 static struct osmo_gsup_message gsup;
407 int rc;
408
Harald Weltee687be52016-05-03 18:49:27 +0200409 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200410 if (rc < 0) {
411 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
412 return rc;
413 }
414
415 switch (gsup.message_type) {
416 /* requests sent to us */
417 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
418 rx_send_auth_info(conn, &gsup);
419 break;
420 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200421 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200422 break;
423 /* responses to requests sent by us */
424 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200425 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200426 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
427 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
428 {
429 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
430 if (!luop) {
431 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
432 "unknown IMSI %s\n", gsup.message_type,
433 gsup.imsi);
434 break;
435 }
436 lu_op_rx_gsup(luop, &gsup);
437 }
Harald Weltee72cf552016-04-28 07:18:49 +0200438 break;
439 default:
440 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
441 gsup.message_type);
442 break;
443 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200444 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200445 return 0;
446}
447
Harald Welteaabae9e2016-04-28 12:48:14 +0200448static struct osmo_gsup_server *gs;
449
450static void signal_hdlr(int signal)
451{
452 switch (signal) {
453 case SIGINT:
454 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
455 osmo_gsup_server_destroy(gs);
456 db_close(g_dbc);
457 log_fini();
458 exit(0);
459 break;
460 case SIGUSR1:
461 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
462 talloc_report_full(NULL, stderr);
463 break;
464 }
465}
Harald Weltee72cf552016-04-28 07:18:49 +0200466
467int main(int argc, char **argv)
468{
Harald Weltee72cf552016-04-28 07:18:49 +0200469 int rc;
470
Harald Welteaabae9e2016-04-28 12:48:14 +0200471 talloc_enable_leak_report_full();
472
Harald Weltee72cf552016-04-28 07:18:49 +0200473 rc = osmo_init_logging(&hlr_log_info);
474 if (rc < 0) {
475 fprintf(stderr, "Error initializing logging\n");
476 exit(1);
477 }
478 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
479
480 rc = rand_init();
481 if (rc < 0) {
482 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
483 exit(1);
484 }
485
486 g_dbc = db_open(NULL, "hlr.db");
487 if (!g_dbc) {
488 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
489 exit(1);
490 }
491
492 gs = osmo_gsup_server_create(NULL, NULL, 2222, read_cb);
493 if (!gs) {
494 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
495 exit(1);
496 }
497
Harald Welteaabae9e2016-04-28 12:48:14 +0200498 osmo_init_ignore_signals();
499 signal(SIGINT, &signal_hdlr);
500 signal(SIGUSR1, &signal_hdlr);
501
502 //osmo_daemonize();
503
Harald Weltee72cf552016-04-28 07:18:49 +0200504 while (1) {
505 osmo_select_main(0);
506 }
507
508 db_close(g_dbc);
509
510 log_fini();
511
512 exit(0);
513}