aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h1
-rw-r--r--openbsc/src/gprs/gprs_subscriber.c82
-rw-r--r--openbsc/src/gprs/sgsn_vty.c4
-rw-r--r--openbsc/tests/sgsn/sgsn_test.c16
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;