From 65fa3f73a1151e896e4c74196680b7886a3b6be6 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Tue, 6 Jan 2015 16:32:41 +0100 Subject: gprs: Use PURGE MS messages When a subscriber entry is going to be deleted by SGSN and when the subscriber info has been obtained from a remote peer via GSUP, the peer should be informed before the entry is really deleted. For this purpose, MAP defines the PURGE MS procedure (see GSM 09.02, 19.1.4). This patch adds support for the PURGE_MS_REQ/_ERR/_RES messages and invokes the procedure when the subscriber entry is going to be removed. This only applies if GSUP is being used, the Update Location procedure has been completed successfully, and the subscriber has not been cancelled. The removal of the entry is delayed until a PURGE_MS_RES or PURGE_MS_ERR message is received. Note that GSM 09.02, 19.1.4.4 implies that the subscriber data is not to be removed when the procedure fails which is not the way the feature has been implemented. Note that handling 'P-TMSI freezing' is not implemented. Ticket: OW#1338 Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/gprs_sgsn.h | 1 + openbsc/src/gprs/gprs_subscriber.c | 82 +++++++++++++++++++++++++++++++++++++ openbsc/src/gprs/sgsn_vty.c | 4 +- openbsc/tests/sgsn/sgsn_test.c | 16 +++++--- 4 files changed, 96 insertions(+), 7 deletions(-) diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h index e707c77d7..2ed46ffbe 100644 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -304,6 +304,7 @@ struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, #define GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING (1 << 16) #define GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING (1 << 17) #define GPRS_SUBSCRIBER_CANCELLED (1 << 18) +#define GPRS_SUBSCRIBER_ENABLE_PURGE (1 << 19) #define GPRS_SUBSCRIBER_UPDATE_PENDING_MASK ( \ GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING | \ diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c index 9d79f0006..58203bab8 100644 --- a/openbsc/src/gprs/gprs_subscriber.c +++ b/openbsc/src/gprs/gprs_subscriber.c @@ -104,10 +104,27 @@ static void sgsn_subscriber_timeout_cb(void *subscr_) LOGGSUBSCRP(LOGL_INFO, subscr, "Expired, deleting subscriber entry\n"); + subscr_get(subscr); + + /* Check, whether to cleanup immediately */ + if (!(subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE)) + goto force_cleanup; + + /* Send a 'purge MS' message to the HLR */ + if (gprs_subscr_purge(subscr) < 0) + goto force_cleanup; + + /* Purge request has been sent */ + + subscr_put(subscr); + return; + +force_cleanup: /* Make sure, the timer is cleaned up */ subscr->keep_in_ram = 0; gprs_subscr_stop_timer(subscr); /* The subscr is freed now, if the timer was the last user */ + subscr_put(subscr); } static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx) @@ -169,6 +186,7 @@ void gprs_subscr_put_and_cancel(struct gsm_subscriber *subscr) { subscr->authorized = 0; subscr->flags |= GPRS_SUBSCRIBER_CANCELLED; + subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; gprs_subscr_update(subscr); @@ -258,6 +276,8 @@ static int gprs_subscr_handle_gsup_upd_loc_res(struct gsm_subscriber *subscr, subscr->authorized = 1; subscr->sgsn_data->error_cause = 0; + subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE; + gprs_subscr_update(subscr); return 0; } @@ -377,6 +397,51 @@ static int gprs_subscr_handle_gsup_upd_loc_err(struct gsm_subscriber *subscr, return -gsup_msg->cause; } +static int gprs_subscr_handle_gsup_purge_res(struct gsm_subscriber *subscr, + struct gprs_gsup_message *gsup_msg) +{ + LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n"); + + /* Force silent cancellation */ + subscr->sgsn_data->error_cause = 0; + gprs_subscr_put_and_cancel(subscr_get(subscr)); + + return 0; +} + +static int gprs_subscr_handle_gsup_purge_err(struct gsm_subscriber *subscr, + struct gprs_gsup_message *gsup_msg) +{ + LOGGSUBSCRP(LOGL_NOTICE, subscr, + "Purge MS has failed with cause '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), + gsup_msg->cause); + + /* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that + * the subscriber data is not removed if the request has failed. On the + * other hand, keeping the subscriber data in either error case + * (subscriber unknown, syntactical message error, connection error) + * doesn't seem to give any advantage, since the data will be restored + * on the next Attach Request anyway. + * This approach ensures, that the subscriber record will not stick if + * an error happens. + */ + + /* TODO: Check whether this behaviour is acceptable and either just + * remove this TODO-notice or change the implementation to not delete + * the subscriber data (eventually resetting the ENABLE_PURGE flag and + * restarting the expiry timer based on the cause). + * + * Subscriber Unknown: cancel subscr + * Temporary network problems: do nothing (handled by timer based retry) + * Message problems (syntax, nyi, ...): cancel subscr (retry won't help) + */ + + gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg); + + return -gsup_msg->cause; +} + int gprs_subscr_rx_gsup_message(struct msgb *msg) { uint8_t *data = msgb_l2(msg); @@ -435,7 +500,13 @@ int gprs_subscr_rx_gsup_message(struct msgb *msg) break; case GPRS_GSUP_MSGT_PURGE_MS_ERROR: + rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg); + break; + case GPRS_GSUP_MSGT_PURGE_MS_RESULT: + rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg); + break; + case GPRS_GSUP_MSGT_INSERT_DATA_REQUEST: case GPRS_GSUP_MSGT_DELETE_DATA_REQUEST: LOGGSUBSCRP(LOGL_ERROR, subscr, @@ -458,6 +529,16 @@ int gprs_subscr_rx_gsup_message(struct msgb *msg) return rc; } +int gprs_subscr_purge(struct gsm_subscriber *subscr) +{ + struct gprs_gsup_message gsup_msg = {0}; + + LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n"); + + gsup_msg.message_type = GPRS_GSUP_MSGT_PURGE_MS_REQUEST; + return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); +} + int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr) { struct gprs_gsup_message gsup_msg = {0}; @@ -514,6 +595,7 @@ struct gsm_subscriber *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mm if (!subscr) { subscr = gprs_subscr_get_or_create(mmctx->imsi); subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; + subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; } if (strcpy(subscr->equipment.imei, mmctx->imei) != 0) { diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index 1241c17e5..ef4c8d82e 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -463,7 +463,7 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, } if (subscr->flags) - vty_out(vty, " Flags: %s%s%s%s%s", + vty_out(vty, " Flags: %s%s%s%s%s%s", subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ? "FIRST_CONTACT " : "", subscr->flags & GPRS_SUBSCRIBER_CANCELLED ? @@ -472,6 +472,8 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, "UPDATE_LOCATION_PENDING " : "", subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ? "AUTH_INFO_PENDING " : "", + subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ? + "ENABLE_PURGE " : "", VTY_NEWLINE); vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c index d6126523e..818106190 100644 --- a/openbsc/tests/sgsn/sgsn_test.c +++ b/openbsc/tests/sgsn/sgsn_test.c @@ -1109,12 +1109,16 @@ static void test_gmm_attach_subscr_gsup_auth(int retry) int my_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) { - struct gprs_gsup_message to_peer; - struct gprs_gsup_message from_peer; + struct gprs_gsup_message to_peer = {0}; + struct gprs_gsup_message from_peer = {0}; struct msgb *reply_msg; + int rc; /* Simulate the GSUP peer */ - gprs_gsup_decode(msgb_data(msg), msgb_length(msg), &to_peer); + rc = gprs_gsup_decode(msgb_data(msg), msgb_length(msg), &to_peer); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(to_peer.imsi[0] != 0); + strncpy(from_peer.imsi, to_peer.imsi, sizeof(from_peer.imsi)); /* This invalidates the pointers in to_peer */ msgb_free(msg); @@ -1129,13 +1133,13 @@ int my_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) return my_subscr_request_auth_info_gsup_auth(NULL); case GPRS_GSUP_MSGT_PURGE_MS_REQUEST: - /* TODO: send RES/ERR */ - return 0; + from_peer.message_type = GPRS_GSUP_MSGT_PURGE_MS_RESULT; + break; default: if ((to_peer.message_type & 0b00000011) == 0) { /* Unhandled request */ - /* TODO: send error(NOT_IMPL) */ + /* Send error(NOT_IMPL) */ from_peer.message_type = to_peer.message_type + 1; from_peer.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; break; -- cgit v1.2.3