sgsn: Unassign the LLME after GMM Status without mmctx

Currently the LLME is not deleted when a GMM Status message is
received for which a mmctx cannot be found. This can fill the LLME
list with unneeded entries.

This patch adds code to unassign the LLME in that case.

Ticket: OW#1324
Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index c8ad98c..a5f9b78 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -1111,8 +1111,12 @@
 	    gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) {
 		LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n");
 		/* 4.7.10 */
-		if (gh->msg_type == GSM48_MT_GMM_STATUS)
+		if (gh->msg_type == GSM48_MT_GMM_STATUS) {
+			/* TLLI unassignment */
+			gprs_llgmm_assign(llme, llme->tlli, 0xffffffff,
+					  GPRS_ALGO_GEA0, NULL);
 			return 0;
+		}
 
 		gprs_llgmm_reset(llme);
 
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 05d1ee0..6762ef8 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -256,6 +256,44 @@
 	OSMO_ASSERT(count(gprs_llme_list()) == 0);
 }
 
+/*
+ * Test that a GMM Status will remove the associated LLME if there is no MMCTX.
+ */
+static void test_gmm_status_no_mmctx(void)
+{
+	struct gprs_llc_lle *lle;
+	uint32_t local_tlli;
+	struct msgb *msg;
+	int sgsn_tx_counter_old;
+
+	printf("Testing GMM Status (no MMCTX)\n");
+
+	/* DTAP - GMM Status, protocol error */
+	static const unsigned char gmm_status[] = {
+		0x08, 0x20, 0x6f
+	};
+
+	/* Create an LLME  */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL);
+	lle = gprs_lle_get_or_create(local_tlli, 3);
+
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+	/* inject the detach */
+	sgsn_tx_counter_old = sgsn_tx_counter;
+	msg = create_msg(gmm_status, ARRAY_SIZE(gmm_status));
+	msgb_tlli(msg) = local_tlli;
+	gsm0408_gprs_rcvmsg(msg, lle->llme);
+	msgb_free(msg);
+
+	/* verify that no message has been sent by the SGSN */
+	OSMO_ASSERT(sgsn_tx_counter_old == sgsn_tx_counter);
+
+	/* verify that the LLME is gone */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+}
+
 static struct log_info_cat gprs_categories[] = {
 	[DMM] = {
 		.name = "DMM",
@@ -321,6 +359,7 @@
 	test_gmm_detach();
 	test_gmm_detach_power_off();
 	test_gmm_detach_no_mmctx();
+	test_gmm_status_no_mmctx();
 	printf("Done\n");
 	return 0;
 }
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index bca2b4b..ea5b0fa 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -2,4 +2,5 @@
 Testing GMM detach
 Testing GMM detach (power off)
 Testing GMM detach (no MMCTX)
+Testing GMM Status (no MMCTX)
 Done