diff options
-rw-r--r-- | openbsc/include/openbsc/gprs_sgsn.h | 1 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_subscriber.c | 82 | ||||
-rw-r--r-- | openbsc/src/gprs/sgsn_vty.c | 4 | ||||
-rw-r--r-- | 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; |