From 6da2af924aaf95b9f4898c06e493336610f54edc Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Wed, 6 May 2020 13:41:07 +0200 Subject: [PATCH] src/lu_fsm.c: dealloc and cancel previous IMSI FIXME: deallocation works, but the location cancel does not get sent yet. (LU runs into a timeout and ME tries again, then the LU is successful) Related: OS#4476 Change-Id: I3993cbec8c5e96d704a58b396a55362e938c9cd2 --- include/osmocom/hlr/gsup_server.h | 2 + src/gsup_server.c | 23 +++++++ src/lu_fsm.c | 104 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/include/osmocom/hlr/gsup_server.h b/include/osmocom/hlr/gsup_server.h index ce7556e3..e85c6d95 100644 --- a/include/osmocom/hlr/gsup_server.h +++ b/include/osmocom/hlr/gsup_server.h @@ -74,5 +74,7 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, uint8_t *msisdn_enc, size_t msisdn_enc_size, uint8_t *apn_buf, size_t apn_buf_size, enum osmo_gsup_cn_domain cn_domain); +void osmo_gsup_create_location_cancel_msg(struct osmo_gsup_message *gsup, const char *imsi, + enum osmo_gsup_cn_domain cn_domain, enum osmo_gsup_cancel_type cancel_type); int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer, struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup); diff --git a/src/gsup_server.c b/src/gsup_server.c index ca2ad5ff..9e2df4ac 100644 --- a/src/gsup_server.c +++ b/src/gsup_server.c @@ -520,6 +520,29 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, return 0; } +/** + * Populate a gsup message structure with a Location Cancel Message. + * All required memory buffers for data pointed to by pointers in struct omso_gsup_message + * must be allocated by the caller and should have the same lifetime as the gsup parameter. + * + * \param[out] gsup The gsup message to populate. + * \param[in] imsi The subscriber's IMSI. + * \param[in] cn_domain The CN Domain of the subscriber connection. + * \param[in] cancel_type The cancellation type. + */ +void osmo_gsup_create_location_cancel_msg(struct osmo_gsup_message *gsup, const char *imsi, + enum osmo_gsup_cn_domain cn_domain, enum osmo_gsup_cancel_type cancel_type) +{ + OSMO_ASSERT(gsup); + *gsup = (struct osmo_gsup_message){ + .message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST, + .cn_domain = cn_domain, + .cancel_type = cancel_type + }; + + osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi)); +} + int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer, struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup) { diff --git a/src/lu_fsm.c b/src/lu_fsm.c index b5493dbb..303441a3 100644 --- a/src/lu_fsm.c +++ b/src/lu_fsm.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -80,6 +81,48 @@ static struct osmo_tdef_state_timeout lu_fsm_timeouts[32] = { [LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { .T = -4222 }, }; +/*! Deallocate the previous pseudonymous IMSI, if the subscriber has two allocated ones and has initiated the LU with + * the newer pseudonymous IMSI. + * \param[in] subscr The subscriber. + * \param[in] imsi_pseudo The pseudonymous IMSI used in the location update. + * \param[out] imsi_pseudo_prev The pseudonymous IMSI that was deallocated if return == 0. Must be a buffer of size + * OSMO_IMSI_BUF. + * \returns 0: successful deallocation, >0: successful, no pseudo IMSI to deallocate, <0: error + */ +int imsi_pseudo_prev_dealloc(struct hlr_subscriber *subscr, const char *imsi_pseudo, char *imsi_pseudo_prev) +{ + struct imsi_pseudo_data data; + + if (db_get_imsi_pseudo_data(g_hlr->dbc, subscr->id, &data) != 0) { + LOGPSEUDO(subscr->id, LOGL_ERROR, "failed to get pseudo IMSI data from DB\n"); + return -EIO; + } + if (data.alloc_count == 0) { + LOGPSEUDO(subscr->id, LOGL_ERROR, "does not have any pseudo IMSI allocated\n"); + return -EIO; + } + if (data.alloc_count != 2) { + LOGPSEUDO(subscr->id, LOGL_DEBUG, "has only one pseudo IMSI allocated, not deallocating any\n"); + return 1; + } + if (strcmp(data.previous, imsi_pseudo) == 0) { + LOGPSEUDO(subscr->id, LOGL_NOTICE, "did LU with previous pseudo IMSI (next pseudo IMSI SMS did not" + " arrive?)\n"); + return 2; + } + + LOGPSEUDO(subscr->id, LOGL_DEBUG, "used current pseudo IMSI '%s' in LU, deallocating previous: '%s'\n", + imsi_pseudo, data.previous); + + if (db_dealloc_imsi_pseudo(g_hlr->dbc, data.previous) != 0) { + LOGPSEUDO(subscr->id, LOGL_ERROR, "failed to deallocate previous pseudo IMSI '%s'\n", data.previous); + return -EIO; + } + + strncpy(imsi_pseudo_prev, data.previous, OSMO_IMSI_BUF_SIZE); + return 0; +} + #define lu_state_chg(lu, state) \ osmo_tdef_fsm_inst_state_chg((lu)->fi, state, lu_fsm_timeouts, g_hlr_tdefs, 5) @@ -197,6 +240,32 @@ static void lu_start(struct osmo_gsup_req *update_location_req) /* TODO: Reset Flag MS Purged (cs/ps) */ /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */ + /* IMSI pseudonymization: deallocate and cancel previous pseudo IMSI */ + if (g_hlr->imsi_pseudo) { + char imsi_pseudo_prev[OSMO_IMSI_BUF_SIZE]; + int rc = imsi_pseudo_prev_dealloc(&lu->subscr, update_location_req->imsi_pseudo, imsi_pseudo_prev); + + if (rc < 0) { + lu_failure(lu, GMM_CAUSE_NET_FAIL, "Failed to deallocate previous pseudonymous IMSI"); + return; + } + if (rc == 0) { + struct osmo_gsup_message gsup; + enum osmo_gsup_cn_domain cn_domain = lu->is_ps + ? OSMO_GSUP_CN_DOMAIN_PS + : OSMO_GSUP_CN_DOMAIN_CS; + + osmo_gsup_create_location_cancel_msg(&gsup, imsi_pseudo_prev, cn_domain, + OSMO_GSUP_CANCEL_TYPE_WITHDRAW); + + /* FIXME: _osmo_gsup_req_respond() fails with 'Invalid response (rc=1)' */ + osmo_gsup_req_respond(update_location_req, &gsup, false, false); + + lu_state_chg(lu, LU_ST_WAIT_LOCATION_CANCEL_RESULT); + return; + } + } + lu_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT); } @@ -285,6 +354,33 @@ void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, vo } } +void lu_fsm_wait_location_cancel_result(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct lu *lu = fi->priv; + struct osmo_gsup_req *req; + + switch (event) { + case LU_EV_RX_GSUP: + req = data; + break; + default: + OSMO_ASSERT(false); + } + + switch (req->gsup.message_type) { + case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR: + LOG_LU(lu, LOGL_DEBUG, "got a location cancel error (but this is fine, remote didn't even know the" + " subscriber we tried to make it forget)\n"); + /* fall through */ + case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT: + lu_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT); + break; + default: + osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "unexpected message type in this state"); + break; + } +} + #define S(x) (1 << (x)) static const struct osmo_fsm_state lu_fsm_states[] = { @@ -292,6 +388,7 @@ static const struct osmo_fsm_state lu_fsm_states[] = { .name = "UNVALIDATED", .out_state_mask = 0 | S(LU_ST_WAIT_INSERT_DATA_RESULT) + | S(LU_ST_WAIT_LOCATION_CANCEL_RESULT) , }, [LU_ST_WAIT_INSERT_DATA_RESULT] = { @@ -302,6 +399,13 @@ static const struct osmo_fsm_state lu_fsm_states[] = { .onenter = lu_fsm_wait_insert_data_result_onenter, .action = lu_fsm_wait_insert_data_result, }, + [LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { + .name = "WAIT_LOCATION_CANCEL_RESULT", + .in_event_mask = 0 + | S(LU_EV_RX_GSUP) + , + .action = lu_fsm_wait_location_cancel_result, + }, }; static struct osmo_fsm lu_fsm = {