blob: 237950def048b2e8bcc06d7e7df13f7c24c5e34c [file] [log] [blame]
Harald Welteaabae9e2016-04-28 12:48:14 +02001#include <signal.h>
Harald Weltee687be52016-05-03 18:49:27 +02002#include <errno.h>
Harald Welteaabae9e2016-04-28 12:48:14 +02003
Harald Weltee72cf552016-04-28 07:18:49 +02004#include <osmocom/core/msgb.h>
5#include <osmocom/core/logging.h>
6#include <osmocom/core/application.h>
7#include <osmocom/gsm/gsup.h>
8
9#include "db.h"
10#include "logging.h"
11#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020012#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020013#include "rand.h"
14
15static struct db_context *g_dbc;
16
Harald Weltee687be52016-05-03 18:49:27 +020017/***********************************************************************
18 * Send Auth Info handling
19 ***********************************************************************/
20
Harald Weltee72cf552016-04-28 07:18:49 +020021/* process an incoming SAI request */
22static int rx_send_auth_info(struct osmo_gsup_conn *conn,
23 const struct osmo_gsup_message *gsup)
24{
25 struct osmo_gsup_message gsup_out;
26 struct msgb *msg_out;
27 int rc;
28
29 /* initialize return message structure */
30 memset(&gsup_out, 0, sizeof(gsup_out));
31 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
32 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
33
34 rc = db_get_auc(g_dbc, gsup->imsi, gsup_out.auth_vectors,
35 ARRAY_SIZE(gsup_out.auth_vectors),
36 NULL /* gsup->rand_auts */, gsup->auts);
37 if (rc <= 0) {
38 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
39 }
40
Harald Weltee687be52016-05-03 18:49:27 +020041 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +020042 osmo_gsup_encode(msg_out, &gsup_out);
43 return osmo_gsup_conn_send(conn, msg_out);
44}
45
Harald Weltee687be52016-05-03 18:49:27 +020046/***********************************************************************
47 * LU Operation State / Structure
48 ***********************************************************************/
49
50static LLIST_HEAD(g_lu_ops);
51
52#define CANCEL_TIMEOUT_SECS 30
53#define ISD_TIMEOUT_SECS 30
54
55enum lu_state {
56 LU_S_NULL,
57 LU_S_LU_RECEIVED,
58 LU_S_CANCEL_SENT,
59 LU_S_CANCEL_ACK_RECEIVED,
60 LU_S_ISD_SENT,
61 LU_S_ISD_ACK_RECEIVED,
62 LU_S_COMPLETE,
63};
64
65static const struct value_string lu_state_names[] = {
66 { LU_S_NULL, "NULL" },
67 { LU_S_LU_RECEIVED, "LU RECEIVED" },
68 { LU_S_CANCEL_SENT, "CANCEL SENT" },
69 { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" },
70 { LU_S_ISD_SENT, "ISD SENT" },
71 { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" },
72 { LU_S_COMPLETE, "COMPLETE" },
73 { 0, NULL }
74};
75
76struct lu_operation {
77 /*! entry in global list of location update operations */
78 struct llist_head list;
79 /*! to which gsup_server do we belong */
80 struct osmo_gsup_server *gsup_server;
81 /*! state of the location update */
82 enum lu_state state;
83 /*! CS (false) or PS (true) Location Update? */
84 bool is_ps;
85 /*! currently running timer */
86 struct osmo_timer_list timer;
87
88 /*! subscriber related to this operation */
89 struct hlr_subscriber subscr;
90 /*! peer VLR/SGSN starting the request */
91 uint8_t *peer;
92};
93
94void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
95{
96 enum lu_state old_state = luop->state;
97
98 DEBUGP(DMAIN, "LU OP state change: %s -> ",
99 get_value_string(lu_state_names, old_state));
100 DEBUGPC(DMAIN, "%s\n",
101 get_value_string(lu_state_names, new_state));
102
103 luop->state = new_state;
104}
105
106struct lu_operation *lu_op_by_imsi(const char *imsi)
107{
108 struct lu_operation *luop;
109
110 llist_for_each_entry(luop, &g_lu_ops, list) {
111 if (!strcmp(imsi, luop->subscr.imsi))
112 return luop;
113 }
114 return NULL;
115}
116
117/* Send a msgb to a given address using routing */
118int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
119 const uint8_t *addr, size_t addrlen,
120 struct msgb *msg)
121{
122 struct osmo_gsup_conn *conn;
123
124 conn = gsup_route_find(gs, addr, addrlen);
125 if (!conn) {
126 DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr);
127 msgb_free(msg);
128 return -ENODEV;
129 }
130
131 return osmo_gsup_conn_send(conn, msg);
132}
133
134static void _luop_tx_gsup(struct lu_operation *luop,
135 const struct osmo_gsup_message *gsup)
136{
137 struct msgb *msg_out;
138
139 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
140 osmo_gsup_encode(msg_out, gsup);
141
142 osmo_gsup_addr_send(luop->gsup_server, luop->peer,
143 talloc_total_size(luop->peer),
144 msg_out);
145}
146
147/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
148void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
149{
150 struct osmo_gsup_message gsup;
151
152 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n",
153 luop->subscr.imsi, cause);
154
155 memset(&gsup, 0, sizeof(gsup));
156 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
157 strncpy(&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi));
158 gsup.imsi[sizeof(gsup.imsi)-1] = '\0';
159 gsup.cause = cause;
160
161 _luop_tx_gsup(luop, &gsup);
162
163 llist_del(&luop->list);
164 talloc_free(luop);
165}
166
167static void lu_op_timer_cb(void *data)
168{
169 struct lu_operation *luop = data;
170
171 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
172 get_value_string(lu_state_names, luop->state));
173
174 switch (luop->state) {
175 case LU_S_CANCEL_SENT:
176 break;
177 case LU_S_ISD_SENT:
178 break;
179 default:
180 break;
181 }
182
183 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
184}
185
186/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
187void lu_op_tx_ack(struct lu_operation *luop)
188{
189 struct osmo_gsup_message gsup;
190
191 memset(&gsup, 0, sizeof(gsup));
192 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT;
193 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
194 //FIXME gsup.hlr_enc;
195
196 _luop_tx_gsup(luop, &gsup);
197
198 llist_del(&luop->list);
199 talloc_free(luop);
200}
201
202/*! Send Cancel Location to old VLR/SGSN */
203void lu_op_tx_cancel_old(struct lu_operation *luop)
204{
205 struct osmo_gsup_message gsup;
206
207 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
208
209 memset(&gsup, 0, sizeof(gsup));
210 gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST;
211 //gsup.cause = FIXME;
212 //gsup.cancel_type = FIXME;
213
214 _luop_tx_gsup(luop, &gsup);
215
216 lu_op_statechg(luop, LU_S_CANCEL_SENT);
217 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
218}
219
220/*! Receive Cancel Location Result from old VLR/SGSN */
221void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
222 const struct osmo_gsup_message *gsup)
223{
224 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
225 /* FIXME: Check for spoofing */
226
227 osmo_timer_del(&luop->timer);
228
229 /* FIXME */
230
231 lu_op_tx_insert_subscr_data(luop);
232}
233
234/*! Transmit Insert Subscriber Data to new VLR/SGSN */
235void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
236{
237 struct osmo_gsup_message gsup;
238
239 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
240 luop->state == LU_S_CANCEL_ACK_RECEIVED);
241
242 memset(&gsup, 0, sizeof(gsup));
243 gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
244 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
245 gsup.msisdn_enc;
246 gsup.hlr_enc;
247
248 if (luop->is_ps) {
249 /* FIXME: PDP infos */
250 }
251
252 /* Send ISD to new VLR/SGSN */
253 _luop_tx_gsup(luop, &gsup);
254
255 lu_op_statechg(luop, LU_S_ISD_SENT);
256 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
257}
258
259/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
260static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
261 const struct osmo_gsup_message *gsup)
262{
263 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
264 /* FIXME: Check for spoofing */
265
266 osmo_timer_del(&luop->timer);
267
268 /* Subscriber_Present_HLR */
269 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
270
271 /* Send final ACK towards inquiring VLR/SGSN */
272 lu_op_tx_ack(luop);
273}
274
275/*! Receive GSUP message for given \ref lu_operation */
276void lu_op_rx_gsup(struct lu_operation *luop,
277 const struct osmo_gsup_message *gsup)
278{
279 switch (gsup->message_type) {
280 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
281 /* FIXME */
282 break;
283 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
284 lu_op_rx_insert_subscr_data_ack(luop, gsup);
285 break;
286 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
287 /* FIXME */
288 break;
289 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
290 lu_op_rx_cancel_old_ack(luop, gsup);
291 break;
292 default:
293 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
294 gsup->message_type);
295 break;
296 }
297}
298
299static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
300{
301 struct lu_operation *luop;
302
303 luop = talloc_zero(srv, struct lu_operation);
304 OSMO_ASSERT(luop);
305 luop->gsup_server = srv;
306 luop->timer.cb = lu_op_timer_cb;
307 luop->timer.data = luop;
308
309 return luop;
310}
311
312/*! Receive Update Location Request, creates new \ref lu_operation */
313static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
314 const struct osmo_gsup_message *gsup)
315{
316 int rc;
317 bool is_ps;
318 struct lu_operation *luop;
319 struct hlr_subscriber *subscr;
320 uint8_t *peer_addr;
321
322 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
323 if (rc < 0) {
324 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
325 return rc;
326 }
327
328 luop = lu_op_alloc(conn->server);
329 luop->peer = talloc_memdup(luop, peer_addr, rc);
330 lu_op_statechg(luop, LU_S_LU_RECEIVED);
331 subscr = &luop->subscr;
332 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
333 luop->is_ps = true;
334 llist_add(&luop->list, &g_lu_ops);
335
336 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
337
338 /* check if subscriber is known at all */
339 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
340 if (rc < 0) {
341 /* Send Error back: Subscriber Unknown in HLR */
342 strcpy(luop->subscr.imsi, gsup->imsi);
343 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
344 return 0;
345 }
346
347 if (!is_ps && !subscr->nam_cs) {
348 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
349 return 0;
350 } else if (is_ps && !subscr->nam_ps) {
351 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
352 return 0;
353 }
354
355 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
356
357#if 0
358 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
359 if (luop->is_ps == false &&
360 strcmp(subscr->vlr_number, vlr_number)) {
361 /* FIXME: start location cancel towards old VLR */
362 lu_op_tx_cancel_old(luop);
363 } else if (luop->is_ps == true &&
364 strcmp(subscr->sgsn_number, sgsn_number)) {
365 /* FIXME: start location cancel towards old VLR */
366 lu_op_tx_cancel_old(luop);
367 } else
368#endif
369 {
370 /* TODO: Subscriber allowed to roam in PLMN? */
371 /* TODO: Update RoutingInfo */
372 /* TODO: Reset Flag MS Purged (cs/ps) */
373 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
374 lu_op_tx_insert_subscr_data(luop);
375 }
376 return 0;
377}
378
Harald Weltee72cf552016-04-28 07:18:49 +0200379static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
380{
381 static struct osmo_gsup_message gsup;
382 int rc;
383
Harald Weltee687be52016-05-03 18:49:27 +0200384 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200385 if (rc < 0) {
386 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
387 return rc;
388 }
389
390 switch (gsup.message_type) {
391 /* requests sent to us */
392 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
393 rx_send_auth_info(conn, &gsup);
394 break;
395 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200396 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200397 break;
398 /* responses to requests sent by us */
399 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200400 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200401 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
402 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
403 {
404 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
405 if (!luop) {
406 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
407 "unknown IMSI %s\n", gsup.message_type,
408 gsup.imsi);
409 break;
410 }
411 lu_op_rx_gsup(luop, &gsup);
412 }
Harald Weltee72cf552016-04-28 07:18:49 +0200413 break;
414 default:
415 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
416 gsup.message_type);
417 break;
418 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200419 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200420 return 0;
421}
422
Harald Welteaabae9e2016-04-28 12:48:14 +0200423static struct osmo_gsup_server *gs;
424
425static void signal_hdlr(int signal)
426{
427 switch (signal) {
428 case SIGINT:
429 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
430 osmo_gsup_server_destroy(gs);
431 db_close(g_dbc);
432 log_fini();
433 exit(0);
434 break;
435 case SIGUSR1:
436 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
437 talloc_report_full(NULL, stderr);
438 break;
439 }
440}
Harald Weltee72cf552016-04-28 07:18:49 +0200441
442int main(int argc, char **argv)
443{
Harald Weltee72cf552016-04-28 07:18:49 +0200444 int rc;
445
Harald Welteaabae9e2016-04-28 12:48:14 +0200446 talloc_enable_leak_report_full();
447
Harald Weltee72cf552016-04-28 07:18:49 +0200448 rc = osmo_init_logging(&hlr_log_info);
449 if (rc < 0) {
450 fprintf(stderr, "Error initializing logging\n");
451 exit(1);
452 }
453 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
454
455 rc = rand_init();
456 if (rc < 0) {
457 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
458 exit(1);
459 }
460
461 g_dbc = db_open(NULL, "hlr.db");
462 if (!g_dbc) {
463 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
464 exit(1);
465 }
466
467 gs = osmo_gsup_server_create(NULL, NULL, 2222, read_cb);
468 if (!gs) {
469 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
470 exit(1);
471 }
472
Harald Welteaabae9e2016-04-28 12:48:14 +0200473 osmo_init_ignore_signals();
474 signal(SIGINT, &signal_hdlr);
475 signal(SIGUSR1, &signal_hdlr);
476
477 //osmo_daemonize();
478
Harald Weltee72cf552016-04-28 07:18:49 +0200479 while (1) {
480 osmo_select_main(0);
481 }
482
483 db_close(g_dbc);
484
485 log_fini();
486
487 exit(0);
488}