sgsn: Create testcase that verifies that llmes get deleted

On an "unassignment" this code verifies that the LLME will vanish
from the list of LLMEs. We assume that this doesn't create a
memory leak.
diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
index 854b968..9689a37 100644
--- a/openbsc/include/openbsc/gprs_llc.h
+++ b/openbsc/include/openbsc/gprs_llc.h
@@ -240,4 +240,10 @@
 void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph);
 int gprs_llc_fcs(uint8_t *data, unsigned int len);
 
+
+/* LLME handling routines */
+struct llist_head *gprs_llme_list(void);
+struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi);
+
+
 #endif
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
index b157bfd..cb810c4 100644
--- a/openbsc/src/gprs/gprs_llc.c
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -166,6 +166,31 @@
 	return NULL;
 }
 
+struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi)
+{
+	struct gprs_llc_llme *llme;
+	struct gprs_llc_lle *lle;
+
+	lle = lle_by_tlli_sapi(tlli, sapi);
+	if (lle)
+		return lle;
+
+	lle = lle_by_tlli_sapi(tlli_foreign2local(tlli), sapi);
+	if (lle)
+		return lle;
+
+	LOGP(DLLC, LOGL_NOTICE, "LLC: unknown TLLI 0x%08x, "
+		"creating LLME on the fly\n", tlli);
+	llme = llme_alloc(tlli);
+	lle = &llme->lle[sapi];
+	return lle;
+}
+
+struct llist_head *gprs_llme_list(void)
+{
+	return &gprs_llc_llmes;
+}
+
 /* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */
 static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli,
 					uint8_t sapi, enum gprs_llc_cmd cmd)
@@ -352,16 +377,7 @@
 	/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
 
 	/* look-up or create the LL Entity for this (TLLI, SAPI) tuple */
-	lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi);
-	if (!lle) 
-		lle = lle_by_tlli_sapi(tlli_foreign2local(msgb_tlli(msg)), sapi);
-	if (!lle) {
-		struct gprs_llc_llme *llme;
-		LOGP(DLLC, LOGL_NOTICE, "LLC TX: unknown TLLI 0x%08x, "
-			"creating LLME on the fly\n", msgb_tlli(msg));
-		llme = llme_alloc(msgb_tlli(msg));
-		lle = &llme->lle[sapi];
-	}
+	lle = gprs_lle_get_or_create(msgb_tlli(msg), sapi);
 
 	if (msg->len > lle->params.n201_u) {
 		LOGP(DLLC, LOGL_ERROR, "Cannot Tx %u bytes (N201-U=%u)\n",
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 9753acf..af00a34 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -6,5 +6,17 @@
 noinst_PROGRAMS = sgsn_test
 
 sgsn_test_SOURCES = sgsn_test.c
-sgsn_test_LDADD = $(LIBOSMOCORE_LIBS)
+sgsn_test_LDADD = \
+	$(top_builddir)/src/gprs/gprs_llc_parse.o \
+	$(top_builddir)/src/gprs/gprs_llc.o \
+	$(top_builddir)/src/gprs/crc24.o \
+	$(top_builddir)/src/gprs/gprs_sndcp.o \
+	$(top_builddir)/src/gprs/gprs_gmm.o \
+	$(top_builddir)/src/gprs/gprs_sgsn.o \
+	$(top_builddir)/src/gprs/sgsn_vty.o \
+	$(top_builddir)/src/gprs/sgsn_libgtp.o \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOGSM_LIBS) \
+	$(LIBOSMOGB_LIBS) \
+	-lgtp
 
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 729cecd..6224fd5 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -19,9 +19,144 @@
  *
  */
 
+#include <openbsc/gprs_llc.h>
+#include <openbsc/sgsn.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/msgb.h>
+
 #include <stdio.h>
 
+extern void *tall_msgb_ctx;
+
+void *tall_bsc_ctx;
+static struct sgsn_instance sgsn_inst = {
+	.config_file = "osmo_sgsn.cfg",
+	.cfg = {
+		.gtp_statedir = "./",
+		.acl_enabled = 1,
+	},
+};
+struct sgsn_instance *sgsn = &sgsn_inst;
+
+static int count(struct llist_head *head)
+{
+	struct llist_head *cur;
+	int count = 0;
+
+	llist_for_each(cur, head)
+		count += 1;
+
+	return count;		
+}
+
+static void test_llme(void)
+{
+	struct gprs_llc_lle *lle, *lle_copy;
+	uint32_t local_tlli;
+	uint32_t foreign_tlli;
+
+	printf("Testing LLME allocations\n");
+	local_tlli = gprs_tmsi2tlli(0x234, TLLI_LOCAL);
+	foreign_tlli = gprs_tmsi2tlli(0x234, TLLI_FOREIGN);
+
+	/* initial state */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+
+	/* Create a new entry */
+	lle = gprs_lle_get_or_create(local_tlli, 3);
+	OSMO_ASSERT(lle);
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+	/* No new entry is created */
+	lle_copy = gprs_lle_get_or_create(local_tlli, 3);
+	OSMO_ASSERT(lle == lle_copy);
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+	lle_copy = gprs_lle_get_or_create(foreign_tlli, 3);
+	OSMO_ASSERT(lle == lle_copy);
+	OSMO_ASSERT(count(gprs_llme_list()) == 1);
+
+	/* unassign which should delete it*/
+	gprs_llgmm_assign(lle->llme, lle->llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
+
+	/* Check that everything was cleaned up */
+	OSMO_ASSERT(count(gprs_llme_list()) == 0);
+}
+
+
+static struct log_info_cat gprs_categories[] = {
+	[DMM] = {
+		.name = "DMM",
+		.description = "Layer3 Mobility Management (MM)",
+		.color = "\033[1;33m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DPAG]	= {
+		.name = "DPAG",
+		.description = "Paging Subsystem",
+		.color = "\033[1;38m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DMEAS] = {
+		.name = "DMEAS",
+		.description = "Radio Measurement Processing",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
+	[DREF] = {
+		.name = "DREF",
+		.description = "Reference Counting",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
+	[DGPRS] = {
+		.name = "DGPRS",
+		.description = "GPRS Packet Service",
+		.enabled = 1, .loglevel = LOGL_DEBUG,
+	},
+	[DNS] = {
+		.name = "DNS",
+		.description = "GPRS Network Service (NS)",
+		.enabled = 1, .loglevel = LOGL_INFO,
+	},
+	[DBSSGP] = {
+		.name = "DBSSGP",
+		.description = "GPRS BSS Gateway Protocol (BSSGP)",
+		.enabled = 1, .loglevel = LOGL_DEBUG,
+	},
+	[DLLC] = {
+		.name = "DLLC",
+		.description = "GPRS Logical Link Control Protocol (LLC)",
+		.enabled = 1, .loglevel = LOGL_DEBUG,
+	},
+	[DSNDCP] = {
+		.name = "DSNDCP",
+		.description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
+		.enabled = 1, .loglevel = LOGL_DEBUG,
+	},
+};
+
+static struct log_info info = {
+	.cat = gprs_categories,
+	.num_cat = ARRAY_SIZE(gprs_categories),
+};
+
 int main(int argc, char **argv)
 {
+	osmo_init_logging(&info);
+	tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
+	tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
+
+	test_llme();
+	printf("Done\n");
 	return 0;
 }
+
+
+/* stubs */
+struct osmo_prim_hdr;
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+	abort();
+}
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index e69de29..045b9e5 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -0,0 +1,2 @@
+Testing LLME allocations
+Done