sgsn: Add functions to handle APN contexts

This commit adds the exported functions apn_ctx_find_alloc,
apn_ctx_free, apn_ctx_by_name, and apn_ctx_match to manage and
retrieve APN to GGSN mappings.

The following VTY commands are added to 'config-sgsn':

 - apn APN ggsn <0-255>
 - apn APN imsi-prefix PREFIX ggsn <0-255>

which maps an APN gateway string to an SGSN id. The SGSN must be
configured in advance. When matching an APN string, entries with a
leading '*' are used for suffix matching, otherwise an exact match is
done.  When a prefix is given, it is matched against the IMSI. If
several entries match, a longer matching IMSI prefix has precedence.
If there are several matching entries with the same PREFIX, the entry
with longest matching APN is returned.

Ticket: OW#1334
Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 7a14cde..5fa33a2 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -1784,6 +1784,98 @@
 	sgsn->cfg.auth_policy = saved_auth_policy;
 }
 
+static void test_apn_matching(void)
+{
+	struct apn_ctx *actx, *actxs[9];
+
+	printf("Testing APN matching\n");
+
+	actxs[0] = sgsn_apn_ctx_find_alloc("*.test", "");
+	actxs[1] = sgsn_apn_ctx_find_alloc("*.def.test", "");
+	actxs[2] = sgsn_apn_ctx_find_alloc("abc.def.test", "");
+	actxs[3] = NULL;
+
+	actxs[4] = sgsn_apn_ctx_find_alloc("abc.def.test", "456");
+	actxs[5] = sgsn_apn_ctx_find_alloc("abc.def.test", "456123");
+	actxs[6] = sgsn_apn_ctx_find_alloc("*.def.test", "456");
+	actxs[7] = sgsn_apn_ctx_find_alloc("*.def.test", "456123");
+
+	actxs[8] = sgsn_apn_ctx_find_alloc("ghi.def.test", "456");
+
+	actx = sgsn_apn_ctx_match("abc.def.test", "12345678");
+	OSMO_ASSERT(actx == actxs[2]);
+	actx = sgsn_apn_ctx_match("aBc.dEf.test", "12345678");
+	OSMO_ASSERT(actx == actxs[2]);
+	actx = sgsn_apn_ctx_match("xyz.def.test", "12345678");
+	OSMO_ASSERT(actx == actxs[1]);
+	actx = sgsn_apn_ctx_match("xyz.dEf.test", "12345678");
+	OSMO_ASSERT(actx == actxs[1]);
+	actx = sgsn_apn_ctx_match("xyz.uvw.test", "12345678");
+	OSMO_ASSERT(actx == actxs[0]);
+	actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678");
+	OSMO_ASSERT(actx == NULL);
+
+	actxs[3] = sgsn_apn_ctx_find_alloc("*", "");
+	actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678");
+	OSMO_ASSERT(actx == actxs[3]);
+
+	actx = sgsn_apn_ctx_match("abc.def.test", "45699900");
+	OSMO_ASSERT(actx == actxs[4]);
+
+	actx = sgsn_apn_ctx_match("xyz.def.test", "45699900");
+	OSMO_ASSERT(actx == actxs[6]);
+
+	actx = sgsn_apn_ctx_match("abc.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[5]);
+
+	actx = sgsn_apn_ctx_match("xyz.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[7]);
+
+	actx = sgsn_apn_ctx_match("ghi.def.test", "45699900");
+	OSMO_ASSERT(actx == actxs[8]);
+
+	actx = sgsn_apn_ctx_match("ghi.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[7]);
+
+	/* Free APN contexts and check how the matching changes */
+
+	sgsn_apn_ctx_free(actxs[7]);
+	actx = sgsn_apn_ctx_match("ghi.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[8]);
+
+	sgsn_apn_ctx_free(actxs[8]);
+	actx = sgsn_apn_ctx_match("ghi.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[6]);
+
+	sgsn_apn_ctx_free(actxs[6]);
+	actx = sgsn_apn_ctx_match("ghi.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[1]);
+
+	sgsn_apn_ctx_free(actxs[5]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[4]);
+
+	sgsn_apn_ctx_free(actxs[4]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "45612300");
+	OSMO_ASSERT(actx == actxs[2]);
+
+	sgsn_apn_ctx_free(actxs[2]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "12345678");
+	OSMO_ASSERT(actx == actxs[1]);
+
+	sgsn_apn_ctx_free(actxs[1]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "12345678");
+	OSMO_ASSERT(actx == actxs[0]);
+
+	sgsn_apn_ctx_free(actxs[0]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "12345678");
+	OSMO_ASSERT(actx == actxs[3]);
+
+	sgsn_apn_ctx_free(actxs[3]);
+	actx = sgsn_apn_ctx_match("abc.def.test", "12345678");
+	OSMO_ASSERT(actx == NULL);
+}
+
 static struct log_info_cat gprs_categories[] = {
 	[DMM] = {
 		.name = "DMM",
@@ -1871,6 +1963,7 @@
 	test_gmm_reject();
 	test_gmm_cancel();
 	test_gmm_ptmsi_allocation();
+	test_apn_matching();
 	printf("Done\n");
 
 	talloc_report_full(osmo_sgsn_ctx, stderr);