gprs: Retry PURGE_MS procedure after timeout

Currently, when the PURGE_MS_REQ to the HLR gets lost (e.g. by a
connection or peer failure), the expired subscriber entry will not get
deleted.

This commit adds a retry mechanism then restarts the procedure after
a timeout (currently 10s). The maximum number of retries is limited
(currently to 3 PURGE_MS messages). If none of these procedures is
completed (either with success or error), the subscriber data is
deleted.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 2ed46ff..2b94096 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -277,6 +277,7 @@
 	int			auth_triplets_updated;
 	int			error_cause;
 	struct osmo_timer_list	timer;
+	int			retries;
 };
 
 #define LOGGSUBSCRP(level, subscr, fmt, args...) \
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 58203ba..8399ea1 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -32,6 +32,9 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#define SGSN_SUBSCR_MAX_RETRIES 3
+#define SGSN_SUBSCR_RETRY_INTERVAL 10
+
 extern void *tall_bsc_ctx;
 
 static int gsup_read_cb(struct gprs_gsup_client *gsupc, struct msgb *msg);
@@ -92,6 +95,7 @@
 	if (!subscr->sgsn_data->timer.data) {
 		subscr->sgsn_data->timer.cb = sgsn_subscriber_timeout_cb;
 		subscr->sgsn_data->timer.data = subscr_get(subscr);
+		subscr->sgsn_data->retries = 0;
 	}
 
 	osmo_timer_schedule(&subscr->sgsn_data->timer, seconds, 0);
@@ -107,7 +111,8 @@
 	subscr_get(subscr);
 
 	/* Check, whether to cleanup immediately */
-	if (!(subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE))
+	if (!(subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) ||
+	    subscr->sgsn_data->retries >= SGSN_SUBSCR_MAX_RETRIES)
 		goto force_cleanup;
 
 	/* Send a 'purge MS' message to the HLR */
@@ -116,6 +121,13 @@
 
 	/* Purge request has been sent */
 
+	/* Check, whether purge is still enabled */
+	if (!(subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE))
+		goto force_cleanup;
+
+	/* Make sure this will be tried again if there is no response in time */
+	subscr->sgsn_data->retries += 1;
+	gprs_subscr_start_timer(subscr, SGSN_SUBSCR_RETRY_INTERVAL);
 	subscr_put(subscr);
 	return;