diff options
author | Harald Welte <laforge@gnumonks.org> | 2016-05-03 18:49:27 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2016-05-03 18:49:27 +0200 |
commit | e687be5f2db2302c82ee2b0bffa1c6f69b5fe278 (patch) | |
tree | 4fb5956c84b565bc17860765dad148499be97eff /src/hlr.c | |
parent | 612291530491e9fc06ad0ad9d5704499fc43f554 (diff) |
Major update; Code now supports SAI, LU and ISD transactions
We also introduce a 'gsup_router' which enables us to route
a transaction to a given VLR. It works based on the SERIAL attribute
communicated at time of the IPA multiplex setup as part of the CCM
sub-protocol.
Diffstat (limited to 'src/hlr.c')
-rw-r--r-- | src/hlr.c | 357 |
1 files changed, 354 insertions, 3 deletions
@@ -1,4 +1,5 @@ #include <signal.h> +#include <errno.h> #include <osmocom/core/msgb.h> #include <osmocom/core/logging.h> @@ -8,10 +9,15 @@ #include "db.h" #include "logging.h" #include "gsup_server.h" +#include "gsup_router.h" #include "rand.h" static struct db_context *g_dbc; +/*********************************************************************** + * Send Auth Info handling + ***********************************************************************/ + /* process an incoming SAI request */ static int rx_send_auth_info(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup) @@ -32,17 +38,350 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn, gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR; } - msg_out = msgb_alloc(1024, "GSUP response"); + msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response"); osmo_gsup_encode(msg_out, &gsup_out); return osmo_gsup_conn_send(conn, msg_out); } +/*********************************************************************** + * LU Operation State / Structure + ***********************************************************************/ + +static LLIST_HEAD(g_lu_ops); + +#define CANCEL_TIMEOUT_SECS 30 +#define ISD_TIMEOUT_SECS 30 + +enum lu_state { + LU_S_NULL, + LU_S_LU_RECEIVED, + LU_S_CANCEL_SENT, + LU_S_CANCEL_ACK_RECEIVED, + LU_S_ISD_SENT, + LU_S_ISD_ACK_RECEIVED, + LU_S_COMPLETE, +}; + +static const struct value_string lu_state_names[] = { + { LU_S_NULL, "NULL" }, + { LU_S_LU_RECEIVED, "LU RECEIVED" }, + { LU_S_CANCEL_SENT, "CANCEL SENT" }, + { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" }, + { LU_S_ISD_SENT, "ISD SENT" }, + { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" }, + { LU_S_COMPLETE, "COMPLETE" }, + { 0, NULL } +}; + +struct lu_operation { + /*! entry in global list of location update operations */ + struct llist_head list; + /*! to which gsup_server do we belong */ + struct osmo_gsup_server *gsup_server; + /*! state of the location update */ + enum lu_state state; + /*! CS (false) or PS (true) Location Update? */ + bool is_ps; + /*! currently running timer */ + struct osmo_timer_list timer; + + /*! subscriber related to this operation */ + struct hlr_subscriber subscr; + /*! peer VLR/SGSN starting the request */ + uint8_t *peer; +}; + +void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state) +{ + enum lu_state old_state = luop->state; + + DEBUGP(DMAIN, "LU OP state change: %s -> ", + get_value_string(lu_state_names, old_state)); + DEBUGPC(DMAIN, "%s\n", + get_value_string(lu_state_names, new_state)); + + luop->state = new_state; +} + +struct lu_operation *lu_op_by_imsi(const char *imsi) +{ + struct lu_operation *luop; + + llist_for_each_entry(luop, &g_lu_ops, list) { + if (!strcmp(imsi, luop->subscr.imsi)) + return luop; + } + return NULL; +} + +/* Send a msgb to a given address using routing */ +int osmo_gsup_addr_send(struct osmo_gsup_server *gs, + const uint8_t *addr, size_t addrlen, + struct msgb *msg) +{ + struct osmo_gsup_conn *conn; + + conn = gsup_route_find(gs, addr, addrlen); + if (!conn) { + DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr); + msgb_free(msg); + return -ENODEV; + } + + return osmo_gsup_conn_send(conn, msg); +} + +static void _luop_tx_gsup(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + struct msgb *msg_out; + + msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP"); + osmo_gsup_encode(msg_out, gsup); + + osmo_gsup_addr_send(luop->gsup_server, luop->peer, + talloc_total_size(luop->peer), + msg_out); +} + +/*! Transmit UPD_LOC_ERROR and destroy lu_operation */ +void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause) +{ + struct osmo_gsup_message gsup; + + DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n", + luop->subscr.imsi, cause); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR; + strncpy(&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)); + gsup.imsi[sizeof(gsup.imsi)-1] = '\0'; + gsup.cause = cause; + + _luop_tx_gsup(luop, &gsup); + + llist_del(&luop->list); + talloc_free(luop); +} + +static void lu_op_timer_cb(void *data) +{ + struct lu_operation *luop = data; + + DEBUGP(DMAIN, "LU OP timer expired in state %s\n", + get_value_string(lu_state_names, luop->state)); + + switch (luop->state) { + case LU_S_CANCEL_SENT: + break; + case LU_S_ISD_SENT: + break; + default: + break; + } + + lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL); +} + +/*! Transmit UPD_LOC_RESULT and destroy lu_operation */ +void lu_op_tx_ack(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT; + strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1); + //FIXME gsup.hlr_enc; + + _luop_tx_gsup(luop, &gsup); + + llist_del(&luop->list); + talloc_free(luop); +} + +/*! Send Cancel Location to old VLR/SGSN */ +void lu_op_tx_cancel_old(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST; + //gsup.cause = FIXME; + //gsup.cancel_type = FIXME; + + _luop_tx_gsup(luop, &gsup); + + lu_op_statechg(luop, LU_S_CANCEL_SENT); + osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0); +} + +/*! Receive Cancel Location Result from old VLR/SGSN */ +void lu_op_rx_cancel_old_ack(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT); + /* FIXME: Check for spoofing */ + + osmo_timer_del(&luop->timer); + + /* FIXME */ + + lu_op_tx_insert_subscr_data(luop); +} + +/*! Transmit Insert Subscriber Data to new VLR/SGSN */ +void lu_op_tx_insert_subscr_data(struct lu_operation *luop) +{ + struct osmo_gsup_message gsup; + + OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED || + luop->state == LU_S_CANCEL_ACK_RECEIVED); + + memset(&gsup, 0, sizeof(gsup)); + gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST; + strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1); + gsup.msisdn_enc; + gsup.hlr_enc; + + if (luop->is_ps) { + /* FIXME: PDP infos */ + } + + /* Send ISD to new VLR/SGSN */ + _luop_tx_gsup(luop, &gsup); + + lu_op_statechg(luop, LU_S_ISD_SENT); + osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0); +} + +/*! Receive Insert Subscriber Data Result from new VLR/SGSN */ +static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + OSMO_ASSERT(luop->state == LU_S_ISD_SENT); + /* FIXME: Check for spoofing */ + + osmo_timer_del(&luop->timer); + + /* Subscriber_Present_HLR */ + /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */ + + /* Send final ACK towards inquiring VLR/SGSN */ + lu_op_tx_ack(luop); +} + +/*! Receive GSUP message for given \ref lu_operation */ +void lu_op_rx_gsup(struct lu_operation *luop, + const struct osmo_gsup_message *gsup) +{ + switch (gsup->message_type) { + case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: + /* FIXME */ + break; + case OSMO_GSUP_MSGT_INSERT_DATA_RESULT: + lu_op_rx_insert_subscr_data_ack(luop, gsup); + break; + case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: + /* FIXME */ + break; + case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT: + lu_op_rx_cancel_old_ack(luop, gsup); + break; + default: + LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n", + gsup->message_type); + break; + } +} + +static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv) +{ + struct lu_operation *luop; + + luop = talloc_zero(srv, struct lu_operation); + OSMO_ASSERT(luop); + luop->gsup_server = srv; + luop->timer.cb = lu_op_timer_cb; + luop->timer.data = luop; + + return luop; +} + +/*! Receive Update Location Request, creates new \ref lu_operation */ +static int rx_upd_loc_req(struct osmo_gsup_conn *conn, + const struct osmo_gsup_message *gsup) +{ + int rc; + bool is_ps; + struct lu_operation *luop; + struct hlr_subscriber *subscr; + uint8_t *peer_addr; + + rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR); + if (rc < 0) { + LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n"); + return rc; + } + + luop = lu_op_alloc(conn->server); + luop->peer = talloc_memdup(luop, peer_addr, rc); + lu_op_statechg(luop, LU_S_LU_RECEIVED); + subscr = &luop->subscr; + if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) + luop->is_ps = true; + llist_add(&luop->list, &g_lu_ops); + + /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */ + + /* check if subscriber is known at all */ + rc = db_subscr_get(g_dbc, gsup->imsi, subscr); + if (rc < 0) { + /* Send Error back: Subscriber Unknown in HLR */ + strcpy(luop->subscr.imsi, gsup->imsi); + lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN); + return 0; + } + + if (!is_ps && !subscr->nam_cs) { + lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED); + return 0; + } else if (is_ps && !subscr->nam_ps) { + lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED); + return 0; + } + + /* TODO: Set subscriber tracing = deactive in VLR/SGSN */ + +#if 0 + /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */ + if (luop->is_ps == false && + strcmp(subscr->vlr_number, vlr_number)) { + /* FIXME: start location cancel towards old VLR */ + lu_op_tx_cancel_old(luop); + } else if (luop->is_ps == true && + strcmp(subscr->sgsn_number, sgsn_number)) { + /* FIXME: start location cancel towards old VLR */ + lu_op_tx_cancel_old(luop); + } else +#endif + { + /* TODO: Subscriber allowed to roam in PLMN? */ + /* TODO: Update RoutingInfo */ + /* TODO: Reset Flag MS Purged (cs/ps) */ + /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */ + lu_op_tx_insert_subscr_data(luop); + } + return 0; +} + static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) { static struct osmo_gsup_message gsup; int rc; - rc = osmo_gsup_decode(msgb_l3(msg), msgb_l3len(msg), &gsup); + rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup); if (rc < 0) { LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc); return rc; @@ -54,11 +393,23 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg) rx_send_auth_info(conn, &gsup); break; case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: + rx_upd_loc_req(conn, &gsup); break; /* responses to requests sent by us */ case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: - break; case OSMO_GSUP_MSGT_INSERT_DATA_RESULT: + case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: + case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT: + { + struct lu_operation *luop = lu_op_by_imsi(gsup.imsi); + if (!luop) { + LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for " + "unknown IMSI %s\n", gsup.message_type, + gsup.imsi); + break; + } + lu_op_rx_gsup(luop, &gsup); + } break; default: LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n", |