gtphub: add explicit cleanup handles.

Clean up functionality is added for the test suite only, to be able to clean
out all allocations and test against memory leaks.

So far, it was sufficient to expire everything to free a gtphub. In preparation
for the upcoming rate counters, which will need to be freed explicitly, add
gtphub functions to clean up everything.

As added bonus, also close the sockets explicitly -- not really needed upon
program exit, neither by the test suite, but *if* we have a cleanup function,
it should clean up everything properly.

Closing the sockets is however kept separate, for the test suite.
gtphub_start() and gtphub_stop() are for normal use (published in gtphub.h),
and gtphub_init() and gtphub_free() are for the test suite, without sockets.
(gtphub_stop() will probably never be called by anyone, but its existence
completes the picture.)

In gtphub_test.c, have a function to clean up the testing gtphub struct. First,
expire everything by timeout, assert emptiness, then call the cleanup function.
Call from each test in the end.

Sponsored-by: On-Waves ehi
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index 4507e6a..5315249 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -24,6 +24,7 @@
 #include <inttypes.h>
 #include <time.h>
 #include <limits.h>
+#include <unistd.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -555,6 +556,14 @@
 	return expired;
 }
 
+void expiry_clear(struct expiry *exq)
+{
+	struct expiring_item *m, *n;
+	llist_for_each_entry_safe(m, n, &exq->items, entry) {
+		expiring_item_del(m);
+	}
+}
+
 void expiring_item_init(struct expiring_item *item)
 {
 	ZERO_STRUCT(item);
@@ -769,6 +778,13 @@
 	return 0;
 }
 
+static void gtphub_sock_close(struct osmo_fd *ofd)
+{
+	close(ofd->fd);
+	osmo_fd_unregister(ofd);
+	ofd->cb = NULL;
+}
+
 static void gtphub_bind_init(struct gtphub_bind *b)
 {
 	ZERO_STRUCT(b);
@@ -788,6 +804,16 @@
 	return 0;
 }
 
+static void gtphub_bind_free(struct gtphub_bind *b)
+{
+	OSMO_ASSERT(llist_empty(&b->peers));
+}
+
+static void gtphub_bind_stop(struct gtphub_bind *b) {
+	gtphub_sock_close(&b->ofd);
+	gtphub_bind_free(b);
+}
+
 /* Recv datagram from from->fd, write sender's address to *from_addr.
  * Return the number of bytes read, zero on error. */
 static int gtphub_read(const struct osmo_fd *from,
@@ -1758,6 +1784,37 @@
 	hub->to_ggsns[GTPH_PLANE_USER].label = "GGSN User";
 }
 
+/* For the test suite, this is kept separate from gtphub_stop(), which also
+ * closes sockets. The test suite avoids using sockets and would cause
+ * segfaults when trying to close uninitialized ofds. */
+void gtphub_free(struct gtphub *hub)
+{
+	/* By expiring all mappings, a garbage collection should free
+	 * everything else. A gtphub_bind_free() will assert that everything is
+	 * indeed empty. */
+	expiry_clear(&hub->expire_seq_maps);
+	expiry_clear(&hub->expire_tei_maps);
+
+	int plane_idx;
+	for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
+		gtphub_gc_bind(&hub->to_ggsns[plane_idx]);
+		gtphub_bind_free(&hub->to_ggsns[plane_idx]);
+
+		gtphub_gc_bind(&hub->to_sgsns[plane_idx]);
+		gtphub_bind_free(&hub->to_sgsns[plane_idx]);
+	}
+}
+
+void gtphub_stop(struct gtphub *hub)
+{
+	int plane_idx;
+	for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
+		gtphub_bind_stop(&hub->to_ggsns[plane_idx]);
+		gtphub_bind_stop(&hub->to_sgsns[plane_idx]);
+	}
+	gtphub_free(hub);
+}
+
 static int gtphub_make_proxy(struct gtphub *hub,
 			     struct gtphub_peer_port **pp,
 			     struct gtphub_bind *bind,