sgsn: Cleanup after RA Update Reject / Attach Reject

Currently, the LLME is not cleaned up after sending an RA Update
Reject. This happens after entering a routing area from outside,
since in that case the SGSN sends an RA Update Reject (implicitly
detached) which causes the MS to restart the attach procedure.
The LLME is also not updated if an Attach Request with message errors
(encoding, invalid MI type) is received or if an MM context cannot be
allocated.

This patch changes gsm48_rx_gmm_ra_upd_req and gsm48_rx_gmm_att_req
to unassign the LLME or free the MM context (if available) after a
Reject message has been sent.

Ticket: OW#1324
Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 00259a5..1756888 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -404,6 +404,148 @@
 	sgsn_acl_del("123456789012345", &sgsn->cfg);
 }
 
+/*
+ * Test the GMM Rejects
+ */
+static void test_gmm_reject(void)
+{
+	struct gprs_ra_id raid = { 0, };
+	struct sgsn_mm_ctx *ctx = NULL;
+	uint32_t foreign_tlli;
+	struct gprs_llc_lle *lle;
+	int idx;
+
+	/* DTAP - Attach Request */
+	/* Invalid MI length */
+	static const unsigned char attach_req_inv_mi_len[] = {
+		0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x09, 0xf4,
+		0xfb, 0xc5, 0x46, 0x79, 0xff, 0xff, 0xff, 0xff, 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 - Attach Request */
+	/* Invalid MI type (IMEI) */
+	static const unsigned char attach_req_inv_mi_type[] = {
+		0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf2,
+		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 - Routing Area Update Request */
+	static const unsigned char dtap_ra_upd_req[] = {
+		0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50,
+		0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b,
+		0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8,
+		0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02,
+		0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19,
+		0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04,
+		0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00
+	};
+
+	/* DTAP - Routing Area Update Request */
+	/* Invalid type: GPRS_UPD_T_RA_LA_IMSI_ATT */
+	static const unsigned char dtap_ra_upd_req_inv_type[] = {
+		0x08, 0x08, 0x12, 0x11, 0x22, 0x33, 0x40, 0x50,
+		0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b,
+		0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8,
+		0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02,
+		0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19,
+		0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04,
+		0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00
+	};
+
+	/* DTAP - Routing Area Update Request */
+	/* Invalid cap length */
+	static const unsigned char dtap_ra_upd_req_inv_cap_len[] = {
+		0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50,
+		0x60, 0x3d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b,
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8,
+		0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02,
+		0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19,
+		0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04,
+		0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00
+	};
+
+	struct test {
+		const char *title;
+		const unsigned char *msg;
+		unsigned msg_len;
+		unsigned num_resp;
+
+	};
+	static struct test tests[] = {
+		{
+			.title    = "Attach Request (invalid MI length)",
+			.msg      = attach_req_inv_mi_len,
+			.msg_len  = sizeof(attach_req_inv_mi_len),
+			.num_resp = 1 /* Reject */
+
+		},
+		{
+			.title   = "Attach Request (invalid MI type)",
+			.msg     = attach_req_inv_mi_type,
+			.msg_len = sizeof(attach_req_inv_mi_type),
+			.num_resp = 1 /* Reject */
+		},
+		{
+			.title   = "Routing Area Update Request (valid)",
+			.msg     = dtap_ra_upd_req,
+			.msg_len = sizeof(dtap_ra_upd_req),
+			.num_resp = 2 /* XID Reset + Reject */
+		},
+		{
+			.title   = "Routing Area Update Request (invalid type)",
+			.msg     = dtap_ra_upd_req_inv_type,
+			.msg_len = sizeof(dtap_ra_upd_req_inv_type),
+			.num_resp = 1 /* Reject */
+		},
+		{
+			.title   = "Routing Area Update Request (invalid CAP length)",
+			.msg     = dtap_ra_upd_req_inv_cap_len,
+			.msg_len = sizeof(dtap_ra_upd_req_inv_cap_len),
+			.num_resp = 1 /* Reject */
+		},
+	};
+
+	printf("Testing GMM reject\n");
+
+	/* reset the PRNG used by sgsn_alloc_ptmsi */
+	srand(1);
+
+	foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN);
+
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+
+	for (idx = 0; idx < ARRAY_SIZE(tests); idx++) {
+		const struct test *test = &tests[idx];
+		printf("  - %s\n", test->title);
+
+		/* Create a LLE/LLME */
+		lle = gprs_lle_get_or_create(foreign_tlli, 3);
+		OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+		/* Inject the Request message */
+		send_0408_message(lle->llme, foreign_tlli,
+				  test->msg, test->msg_len);
+
+		/* We expect a Reject message */
+		fprintf(stderr, "sgsn_tx_counter = %d (expected %d)\n",
+			sgsn_tx_counter, test->num_resp);
+		OSMO_ASSERT(sgsn_tx_counter == test->num_resp);
+
+		/* verify that LLME/MM are removed */
+		ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid);
+		OSMO_ASSERT(ctx == NULL);
+		OSMO_ASSERT(count(gprs_llme_list()) == 0);
+	}
+}
+
 static struct log_info_cat gprs_categories[] = {
 	[DMM] = {
 		.name = "DMM",
@@ -473,6 +615,7 @@
 	test_gmm_detach_no_mmctx();
 	test_gmm_status_no_mmctx();
 	test_gmm_attach();
+	test_gmm_reject();
 	printf("Done\n");
 	return 0;
 }
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index c03bb1e..d3b333f 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -4,4 +4,10 @@
 Testing GMM detach (no MMCTX)
 Testing GMM Status (no MMCTX)
 Testing GMM attach
+Testing GMM reject
+  - Attach Request (invalid MI length)
+  - Attach Request (invalid MI type)
+  - Routing Area Update Request (valid)
+  - Routing Area Update Request (invalid type)
+  - Routing Area Update Request (invalid CAP length)
 Done