sgsn: Add gsm0408_gprs_access_cancelled

This function is called to delete an established MM context
silently without invoking a detach procedure.

It is called when a subscriber is cancelled by the HLR. This
generally happens, when an MS has moved to another routing area and
has to use another SGSN.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 981a557..26d81a7 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -734,6 +734,122 @@
 }
 
 /*
+ * Test cancellation of attached MM contexts
+ */
+static void test_gmm_cancel(void)
+{
+	struct gprs_ra_id raid = { 0, };
+	struct sgsn_mm_ctx *ctx = NULL;
+	struct sgsn_mm_ctx *ictx;
+	uint32_t ptmsi1;
+	uint32_t foreign_tlli;
+	uint32_t local_tlli = 0;
+	struct gprs_llc_lle *lle;
+	const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy;
+
+	/* DTAP - Attach Request */
+	/* The P-TMSI is not known by the SGSN */
+	static const unsigned char attach_req[] = {
+		0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4,
+		0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60,
+		0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60,
+		0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8,
+		0xc6, 0x62, 0x00, 0x60, 0x80, 0x00
+	};
+
+	/* DTAP - Identity Response IMEI */
+	static const unsigned char ident_resp_imei[] = {
+		0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78,
+		0x56
+	};
+
+	/* DTAP - Identity Response IMSI */
+	static const unsigned char ident_resp_imsi[] = {
+		0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32,
+		0x54
+	};
+
+	/* DTAP - Attach Complete */
+	static const unsigned char attach_compl[] = {
+		0x08, 0x03
+	};
+
+	printf("Testing cancellation\n");
+
+	sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_OPEN;
+
+	/* reset the PRNG used by sgsn_alloc_ptmsi */
+	srand(1);
+
+	ptmsi1 = sgsn_alloc_ptmsi();
+	OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI);
+
+	/* reset the PRNG, so that the same P-TMSI sequence will be generated
+	 * again */
+	srand(1);
+
+	foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN);
+
+	/* Create a LLE/LLME */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	lle = gprs_lle_get_or_create(foreign_tlli, 3);
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+	/* inject the attach request */
+	send_0408_message(lle->llme, foreign_tlli,
+			  attach_req, ARRAY_SIZE(attach_req));
+
+	ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid);
+	OSMO_ASSERT(ctx != NULL);
+	OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT);
+
+	/* we expect an identity request (IMEI) */
+	OSMO_ASSERT(sgsn_tx_counter == 1);
+
+	/* inject the identity response (IMEI) */
+	send_0408_message(ctx->llme, foreign_tlli,
+			  ident_resp_imei, ARRAY_SIZE(ident_resp_imei));
+
+	/* we expect an identity request (IMSI) */
+	OSMO_ASSERT(sgsn_tx_counter == 1);
+
+	/* inject the identity response (IMSI) */
+	send_0408_message(ctx->llme, foreign_tlli,
+			  ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi));
+
+	/* check that the MM context has not been removed due to a failed
+	 * authorization */
+	OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid));
+
+	OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT);
+
+	/* we expect an attach accept/reject */
+	OSMO_ASSERT(sgsn_tx_counter == 1);
+
+	/* this has been randomly assigned by the SGSN */
+	local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL);
+
+	/* inject the attach complete */
+	send_0408_message(ctx->llme, local_tlli,
+			  attach_compl, ARRAY_SIZE(attach_compl));
+
+	OSMO_ASSERT(ctx->mm_state == GMM_REGISTERED_NORMAL);
+
+	/* we don't expect a response */
+	OSMO_ASSERT(sgsn_tx_counter == 0);
+
+	/* cancel */
+	gsm0408_gprs_access_cancelled(ctx);
+
+	/* verify that things are gone */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid);
+	OSMO_ASSERT(!ictx);
+
+	sgsn->cfg.auth_policy = saved_auth_policy;
+}
+
+/*
  * Test the dynamic allocation of P-TMSIs
  */
 static void test_gmm_ptmsi_allocation(void)
@@ -997,6 +1113,7 @@
 	test_gmm_attach_acl();
 	test_gmm_attach_subscr();
 	test_gmm_reject();
+	test_gmm_cancel();
 	test_gmm_ptmsi_allocation();
 	printf("Done\n");
 	return 0;