Split gsn_t related APIs out of gtp.{c,h}

This way we split the gsn_t object API/logic from the protocol (message
handling) code.

Change-Id: I47cebb51bf08b9fcf7f115fc8dbea5f3493d4388
diff --git a/gtp/Makefile.am b/gtp/Makefile.am
index 50c582c..77f81cf 100644
--- a/gtp/Makefile.am
+++ b/gtp/Makefile.am
@@ -6,10 +6,10 @@
 
 lib_LTLIBRARIES = libgtp.la
 
-include_HEADERS = gtp.h pdp.h gtpie.h
+include_HEADERS = gtp.h gsn.h pdp.h gtpie.h
 
 AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
 
-libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
+libgtp_la_SOURCES = gtp.c gtp.h gsn.c gsn.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
 libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
 libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
diff --git a/gtp/gsn.c b/gtp/gsn.c
new file mode 100644
index 0000000..8fb0fdb
--- /dev/null
+++ b/gtp/gsn.c
@@ -0,0 +1,536 @@
+/*
+ *  OsmoGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002, 2003, 2004 Mondru AB.
+ *  Copyright (C) 2010-2011, 2016-2017 Harald Welte <laforge@gnumonks.org>
+ *  Copyright (C) 2015-2017 sysmocom - s.f.m.c. GmbH
+ *
+ *  The contents of this file may be used under the terms of the GNU
+ *  General Public License Version 2, provided that the above copyright
+ *  notice and this permission notice is included in all copies or
+ *  substantial portions of the software.
+ *
+ */
+
+/*
+ * gtp.c: Contains all GTP functionality. Should be able to handle multiple
+ * tunnels in the same program.
+ *
+ * TODO:
+ *  - Do we need to handle fragmentation?
+ */
+
+#ifdef __linux__
+#define _GNU_SOURCE 1
+#endif
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#if defined(__FreeBSD__)
+#include <sys/endian.h>
+#endif
+
+#include "../config.h"
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <arpa/inet.h>
+
+/* #include <stdint.h>  ISO C99 types */
+
+#include "pdp.h"
+#include "gtp.h"
+#include "gtpie.h"
+#include "queue.h"
+
+/* According to section 14.2 of 3GPP TS 29.006 version 6.9.0 */
+#define N3_REQUESTS	5
+
+#define T3_REQUEST	3
+
+/* Error reporting functions */
+
+#define LOGP_WITH_ADDR(ss, level, addr, fmt, args...)                    \
+		LOGP(ss, level, "addr(%s:%d) " fmt,                      \
+		     inet_ntoa((addr).sin_addr), htons((addr).sin_port), \
+		     ##args);
+
+/* API Functions */
+
+/* Deprecated, use gtp_pdp_newpdp() instead */
+int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
+	       uint64_t imsi, uint8_t nsapi)
+{
+	int rc;
+	rc = gtp_pdp_newpdp(gsn, pdp, imsi, nsapi, NULL);
+	return rc;
+}
+
+int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp)
+{
+	if (gsn->cb_delete_context)
+		gsn->cb_delete_context(pdp);
+	return pdp_freepdp(pdp);
+}
+
+/* Free pdp and all its secondary PDP contexts. Must be called on the primary PDP context. */
+int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp)
+{
+	int n;
+	struct pdp_t *secondary_pdp;
+	OSMO_ASSERT(!pdp->secondary);
+
+	for (n = 0; n < PDP_MAXNSAPI; n++) {
+		if (pdp->secondary_tei[n]) {
+			if (gtp_pdp_getgtp1(gsn, &secondary_pdp,
+					     pdp->secondary_tei[n])) {
+				LOGP(DLGTP, LOGL_ERROR,
+					"Unknown secondary PDP context\n");
+				continue;
+			}
+			if (pdp != secondary_pdp) {
+				gtp_freepdp(gsn, secondary_pdp);
+			}
+		}
+	}
+
+	return gtp_freepdp(gsn, pdp);
+}
+
+/* gtp_gpdu */
+
+extern int gtp_fd(struct gsn_t *gsn)
+{
+	return gsn->fd0;
+}
+
+int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
+			 int (*cb) (struct sockaddr_in * peer))
+{
+	gsn->cb_unsup_ind = cb;
+	return 0;
+}
+
+int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
+			     int (*cb) (struct sockaddr_in * peer))
+{
+	gsn->cb_extheader_ind = cb;
+	return 0;
+}
+
+int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
+			     int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie))
+{
+	gsn->cb_ran_info_relay_ind = cb;
+	return 0;
+}
+
+/* API: Initialise delete context callback */
+/* Called whenever a pdp context is deleted for any reason */
+int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb) (struct pdp_t * pdp))
+{
+	gsn->cb_delete_context = cb;
+	return 0;
+}
+
+int gtp_set_cb_conf(struct gsn_t *gsn,
+		    int (*cb) (int type, int cause,
+			       struct pdp_t * pdp, void *cbp))
+{
+	gsn->cb_conf = cb;
+	return 0;
+}
+
+int gtp_set_cb_recovery(struct gsn_t *gsn,
+			int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
+{
+	gsn->cb_recovery = cb;
+	return 0;
+}
+
+/* cb_recovery()
+ * pdp may be NULL if Recovery IE was received from a message independent
+ * of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
+ * local setup. In case pdp is known, caller may want to keep that pdp alive to
+ * handle subsequent msg cb as this specific pdp ctx is still valid according to
+ * specs.
+ */
+int gtp_set_cb_recovery2(struct gsn_t *gsn,
+			int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery))
+{
+	gsn->cb_recovery2 = cb_recovery2;
+	return 0;
+}
+
+/* cb_recovery()
+ * pdp may be NULL if Recovery IE was received from a message independent
+ * of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
+ * local setup. In case pdp is known, caller may want to keep that pdp alive to
+ * handle subsequent msg cb as this specific pdp ctx is still valid according to
+ * specs.
+ */
+int gtp_set_cb_recovery3(struct gsn_t *gsn,
+			 int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer,
+					      struct pdp_t *pdp, uint8_t recovery))
+{
+	gsn->cb_recovery3 = cb_recovery3;
+	return 0;
+}
+
+int gtp_set_cb_data_ind(struct gsn_t *gsn,
+			       int (*cb_data_ind) (struct pdp_t * pdp,
+						   void *pack, unsigned len))
+{
+	gsn->cb_data_ind = cb_data_ind;
+	return 0;
+}
+
+static int queue_timer_retrans(struct gsn_t *gsn)
+{
+	/* Retransmit any outstanding packets */
+	/* Remove from queue if maxretrans exceeded */
+	time_t now;
+	struct qmsg_t *qmsg;
+	now = time(NULL);
+
+	/* get first element in queue, as long as the timeout of that
+	 * element has expired */
+	while ((!queue_getfirst(gsn->queue_req, &qmsg)) &&
+	       (qmsg->timeout <= now)) {
+		if (qmsg->retrans > N3_REQUESTS) {	/* Too many retrans */
+			LOGP(DLGTP, LOGL_NOTICE, "Retransmit req queue timeout of seq %" PRIu16 "\n",
+			     qmsg->seq);
+			if (gsn->cb_conf)
+				gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
+			queue_freemsg(gsn->queue_req, qmsg);
+		} else {
+			LOGP(DLGTP, LOGL_INFO, "Retransmit (%d) of seq %" PRIu16 "\n",
+			     qmsg->retrans, qmsg->seq);
+			if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
+				   (struct sockaddr *)&qmsg->peer,
+				   sizeof(struct sockaddr_in)) < 0) {
+				gsn->err_sendto++;
+				LOGP(DLGTP, LOGL_ERROR,
+					"Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s\n",
+					gsn->fd0, (unsigned long)&qmsg->p,
+					qmsg->l, strerror(errno));
+			}
+			queue_back(gsn->queue_req, qmsg);
+			qmsg->timeout = now + T3_REQUEST;
+			qmsg->retrans++;
+		}
+	}
+
+	/* Also clean up reply timeouts */
+	while ((!queue_getfirst(gsn->queue_resp, &qmsg)) &&
+	       (qmsg->timeout < now)) {
+		LOGP(DLGTP, LOGL_DEBUG, "Retransmit resp queue seq %"
+		     PRIu16 " expired, removing from queue\n", qmsg->seq);
+		queue_freemsg(gsn->queue_resp, qmsg);
+	}
+
+	return 0;
+}
+
+static int queue_timer_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
+{
+	time_t now, later, diff;
+	struct qmsg_t *qmsg;
+	timeout->tv_usec = 0;
+
+	if (queue_getfirst(gsn->queue_req, &qmsg)) {
+		timeout->tv_sec = 10;
+	} else {
+		now = time(NULL);
+		later = qmsg->timeout;
+		timeout->tv_sec = later - now;
+		if (timeout->tv_sec < 0)
+			timeout->tv_sec = 0;	/* No negative allowed */
+		if (timeout->tv_sec > 10)
+			timeout->tv_sec = 10;	/* Max sleep for 10 sec */
+	}
+
+	if (queue_getfirst(gsn->queue_resp, &qmsg)) {
+		/* already set by queue_req, do nothing */
+	} else { /* trigger faster if earlier timeout exists in queue_resp */
+		now = time(NULL);
+		later = qmsg->timeout;
+		diff = later - now;
+		if (diff < 0)
+			diff = 0;
+		if (diff < timeout->tv_sec)
+			timeout->tv_sec = diff;
+	}
+
+	return 0;
+}
+
+void gtp_queue_timer_start(struct gsn_t *gsn)
+{
+	struct timeval next;
+
+	/* Retrieve next retransmission as timeval */
+	queue_timer_retranstimeout(gsn, &next);
+
+	/* re-schedule the timer */
+	osmo_timer_schedule(&gsn->queue_timer, next.tv_sec, next.tv_usec/1000);
+}
+
+/* timer callback for libgtp retransmission and ping */
+static void queue_timer_cb(void *data)
+{
+	struct gsn_t *gsn = data;
+
+	/* do all the retransmissions as needed */
+	queue_timer_retrans(gsn);
+
+	gtp_queue_timer_start(gsn);
+}
+
+
+/**
+ * @brief clear the request and response queue. Useful for debugging to reset "some" state.
+ * @param gsn The GGSN instance
+ */
+void gtp_clear_queues(struct gsn_t *gsn)
+{
+	struct qmsg_t *qmsg;
+
+	LOGP(DLGTP, LOGL_INFO, "Clearing req & resp retransmit queues\n");
+	while (!queue_getfirst(gsn->queue_req, &qmsg)) {
+		queue_freemsg(gsn->queue_req, qmsg);
+	}
+
+	while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
+		queue_freemsg(gsn->queue_resp, qmsg);
+	}
+}
+
+/* Perform restoration and recovery error handling as described in 29.060 */
+static void log_restart(struct gsn_t *gsn)
+{
+	FILE *f;
+	int i, rc;
+	int counter = 0;
+	char *filename;
+
+	filename = talloc_asprintf(NULL, "%s/%s", gsn->statedir, RESTART_FILE);
+	OSMO_ASSERT(filename);
+
+	/* We try to open file. On failure we will later try to create file */
+	if (!(f = fopen(filename, "r"))) {
+		LOGP(DLGTP, LOGL_NOTICE,
+			"State information file (%s) not found. Creating new file.\n",
+			filename);
+	} else {
+		rc = fscanf(f, "%d", &counter);
+		if (rc != 1) {
+			LOGP(DLGTP, LOGL_ERROR,
+				"fscanf failed to read counter value\n");
+			goto close_file;
+		}
+		if (fclose(f)) {
+			LOGP(DLGTP, LOGL_ERROR,
+				"fclose failed: Error = %s\n", strerror(errno));
+		}
+	}
+
+	gsn->restart_counter = (unsigned char)counter;
+	gsn->restart_counter++;
+
+	/* Keep the umask closely wrapped around our fopen() call in case the
+	 * log outputs cause file creation. */
+	i = umask(022);
+	f = fopen(filename, "w");
+	umask(i);
+	if (!f) {
+		LOGP(DLGTP, LOGL_ERROR,
+			"fopen(path=%s, mode=%s) failed: Error = %s\n", filename,
+			"w", strerror(errno));
+		goto free_filename;
+	}
+
+	fprintf(f, "%d\n", gsn->restart_counter);
+close_file:
+	if (fclose(f))
+		LOGP(DLGTP, LOGL_ERROR,
+			"fclose failed: Error = %s\n", strerror(errno));
+free_filename:
+	talloc_free(filename);
+}
+
+int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+	    int mode)
+{
+	struct sockaddr_in addr;
+
+	LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen));
+
+	*gsn = calloc(sizeof(struct gsn_t), 1);	/* TODO */
+
+	(*gsn)->statedir = statedir;
+	log_restart(*gsn);
+
+	/* Initialise sequence number */
+	(*gsn)->seq_next = (*gsn)->restart_counter * 1024;
+
+	/* Initialise request retransmit queue */
+	queue_new(&(*gsn)->queue_req);
+	queue_new(&(*gsn)->queue_resp);
+
+	/* Initialise pdp table */
+	pdp_init(*gsn);
+
+	/* Initialize internal queue timer */
+	osmo_timer_setup(&(*gsn)->queue_timer, queue_timer_cb, *gsn);
+
+	/* Initialise call back functions */
+	(*gsn)->cb_create_context_ind = 0;
+	(*gsn)->cb_delete_context = 0;
+	(*gsn)->cb_unsup_ind = 0;
+	(*gsn)->cb_conf = 0;
+	(*gsn)->cb_data_ind = 0;
+
+	/* Store function parameters */
+	(*gsn)->gsnc = *listen;
+	(*gsn)->gsnu = *listen;
+	(*gsn)->mode = mode;
+
+	/* Create GTP version 0 socket */
+	if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP(DLGTP, LOGL_ERROR,
+		     "GTPv0 socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
+			AF_INET, SOCK_DGRAM, 0, strerror(errno));
+		return -errno;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
+	addr.sin_port = htons(GTP0_PORT);
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	addr.sin_len = sizeof(addr);
+#endif
+
+	if (bind((*gsn)->fd0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
+			       "bind(fd0=%d) failed: Error = %s\n",
+			       (*gsn)->fd0, strerror(errno));
+		return -errno;
+	}
+
+	/* Create GTP version 1 control plane socket */
+	if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP(DLGTP, LOGL_ERROR,
+		     "GTPv1 control plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
+			AF_INET, SOCK_DGRAM, 0, strerror(errno));
+		return -errno;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
+	addr.sin_port = htons(GTP1C_PORT);
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	addr.sin_len = sizeof(addr);
+#endif
+
+	if (bind((*gsn)->fd1c, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
+				"bind(fd1c=%d) failed: Error = %s\n",
+				(*gsn)->fd1c, strerror(errno));
+		return -errno;
+	}
+
+	/* Create GTP version 1 user plane socket */
+	if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP(DLGTP, LOGL_ERROR,
+		     "GTPv1 user plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
+			AF_INET, SOCK_DGRAM, 0, strerror(errno));
+		return -errno;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
+	addr.sin_port = htons(GTP1U_PORT);
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	addr.sin_len = sizeof(addr);
+#endif
+
+	if (bind((*gsn)->fd1u, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		(*gsn)->err_socket++;
+		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
+			"bind(fd1u=%d) failed: Error = %s\n",
+			(*gsn)->fd1u, strerror(errno));
+		return -errno;
+	}
+
+	/* Start internal queue timer */
+	gtp_queue_timer_start(*gsn);
+
+	return 0;
+}
+
+int gtp_free(struct gsn_t *gsn)
+{
+
+	/* Cleanup internal queue timer */
+	osmo_timer_del(&gsn->queue_timer);
+
+	/* Clean up retransmit queues */
+	queue_free(gsn->queue_req);
+	queue_free(gsn->queue_resp);
+
+	close(gsn->fd0);
+	close(gsn->fd1c);
+	close(gsn->fd1u);
+
+	free(gsn);
+	return 0;
+}
+
+/* API: Register create context indication callback */
+int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
+				  int (*cb_create_context_ind) (struct pdp_t *
+								pdp))
+{
+	gsn->cb_create_context_ind = cb_create_context_ind;
+	return 0;
+}
+
+int gtp_retrans(struct gsn_t *gsn)
+{
+	/* dummy API, deprecated. */
+	return 0;
+}
+
+int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
+{
+	timeout->tv_sec = 24*60*60;
+	timeout->tv_usec = 0;
+	/* dummy API, deprecated. Return a huge timer to do nothing */
+	return 0;
+}
\ No newline at end of file
diff --git a/gtp/gsn.h b/gtp/gsn.h
new file mode 100644
index 0000000..18a6d58
--- /dev/null
+++ b/gtp/gsn.h
@@ -0,0 +1,167 @@
+/*
+ *  OsmoGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002, 2003, 2004 Mondru AB.
+ *
+ *  The contents of this file may be used under the terms of the GNU
+ *  General Public License Version 2, provided that the above copyright
+ *  notice and this permission notice is included in all copies or
+ *  substantial portions of the software.
+ *
+ */
+
+#ifndef _GSN_H
+#define _GSN_H
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/defs.h>
+#include <osmocom/core/timer.h>
+
+#include "pdp.h"
+
+#define GTP_MODE_GGSN 1
+#define GTP_MODE_SGSN 2
+
+#define RESTART_FILE "gsn_restart"
+
+/* ***********************************************************
+ * Information storage for each gsn instance
+ *
+ * Normally each instance of the application corresponds to
+ * one instance of a gsn.
+ *
+ * In order to avoid global variables in the application, and
+ * also in order to allow several instances of a gsn in the same
+ * application this struct is provided in order to store all
+ * relevant information related to the gsn.
+ *
+ * Note that this does not include information storage for '
+ * each pdp context. This is stored in another struct.
+ *************************************************************/
+
+struct gsn_t {
+	/* Parameters related to the network interface */
+
+	int fd0;		/* GTP0 file descriptor */
+	int fd1c;		/* GTP1 control plane file descriptor */
+	int fd1u;		/* GTP0 user plane file descriptor */
+	int mode;		/* Mode of operation: GGSN or SGSN */
+	struct in_addr gsnc;	/* IP address of this gsn for signalling */
+	struct in_addr gsnu;	/* IP address of this gsn for user traffic */
+
+	/* Parameters related to signalling messages */
+	uint16_t seq_next;	/* Next sequence number to use */
+	int seq_first;		/* First packet in queue (oldest timeout) */
+	int seq_last;		/* Last packet in queue (youngest timeout) */
+
+	unsigned char restart_counter;	/* Increment on restart. Stored on disk */
+	char *statedir;		/* Disk location for permanent storage */
+	void *priv;		/* used by libgtp users to attach their own state) */
+	struct queue_t *queue_req;	/* Request queue */
+	struct queue_t *queue_resp;	/* Response queue */
+
+	struct pdp_t pdpa[PDP_MAX];	/* PDP storage */
+	struct pdp_t *hashtid[PDP_MAX];	/* Hash table for IMSI + NSAPI */
+
+	struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
+
+	/* Call back functions */
+	int (*cb_delete_context) (struct pdp_t *);
+	int (*cb_create_context_ind) (struct pdp_t *);
+	int (*cb_unsup_ind) (struct sockaddr_in * peer);
+	int (*cb_extheader_ind) (struct sockaddr_in * peer);
+	int (*cb_ran_info_relay_ind) (struct sockaddr_in *peer, union gtpie_member **ie);
+	int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
+	int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
+	int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
+	int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
+	int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
+
+	/* Counters */
+
+	uint64_t err_socket;	/* Number of socket errors */
+	uint64_t err_readfrom;	/* Number of readfrom errors */
+	uint64_t err_sendto;	/* Number of sendto errors */
+	uint64_t err_memcpy;	/* Number of memcpy */
+	uint64_t err_queuefull;	/* Number of times queue was full */
+	uint64_t err_seq;	/* Number of seq out of range */
+	uint64_t err_address;	/* GSN address conversion failed */
+	uint64_t err_unknownpdp;	/* GSN address conversion failed */
+	uint64_t err_unknowntid;	/* Application supplied unknown imsi+nsapi */
+	uint64_t err_cause;	/* Unexpected cause value received */
+	uint64_t err_outofpdp;	/* Out of storage for PDP contexts */
+
+	uint64_t empty;		/* Number of empty packets */
+	uint64_t unsup;		/* Number of unsupported version 29.60 11.1.1 */
+	uint64_t tooshort;	/* Number of too short headers 29.60 11.1.2 */
+	uint64_t unknown;	/* Number of unknown messages 29.60 11.1.3 */
+	uint64_t unexpect;	/* Number of unexpected messages 29.60 11.1.4 */
+	uint64_t duplicate;	/* Number of duplicate or unsolicited replies */
+	uint64_t missing;	/* Number of missing information field messages */
+	uint64_t incorrect;	/* Number of incorrect information field messages */
+	uint64_t invalid;	/* Number of invalid message format messages */
+};
+
+/* External API functions */
+
+extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+		   int mode);
+
+extern int gtp_free(struct gsn_t *gsn);
+
+extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
+		      uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
+extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
+extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
+
+extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
+				  void *cbp);
+
+extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
+					 int (*cb_create_context_ind) (struct
+								       pdp_t *
+								       pdp));
+extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
+			       int (*cb_data_ind) (struct pdp_t * pdp,
+						   void *pack, unsigned len));
+extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
+				     int (*cb_delete_context) (struct pdp_t *
+							       pdp));
+/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
+  int (*cb_create_context) (struct pdp_t* pdp)); */
+
+extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
+				int (*cb) (struct sockaddr_in * peer));
+
+extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
+				    int (*cb) (struct sockaddr_in * peer));
+
+extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
+				    int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie));
+
+extern int gtp_set_cb_conf(struct gsn_t *gsn,
+			   int (*cb) (int type, int cause, struct pdp_t * pdp,
+				      void *cbp));
+
+int gtp_set_cb_recovery(struct gsn_t *gsn,
+			int (*cb) (struct sockaddr_in * peer,
+				   uint8_t recovery))
+	OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
+int gtp_set_cb_recovery2(struct gsn_t *gsn,
+			int (*cb) (struct sockaddr_in * peer,
+				   struct pdp_t * pdp,
+				   uint8_t recovery))
+	OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");
+int gtp_set_cb_recovery3(struct gsn_t *gsn,
+			int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
+				   struct pdp_t * pdp,
+				   uint8_t recovery));
+void gtp_clear_queues(struct gsn_t *gsn);
+extern int gtp_fd(struct gsn_t *gsn);
+
+extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
+extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
+
+/* Internal APIs: */
+void gtp_queue_timer_start(struct gsn_t *gsn);
+
+#endif /* !_GSN_H */
diff --git a/gtp/gtp.c b/gtp/gtp.c
index d2f2219..5585256 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -131,93 +131,7 @@
 	{ 0, NULL }
 };
 
-/* Deprecated, use gtp_pdp_newpdp() instead */
-int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
-	       uint64_t imsi, uint8_t nsapi)
-{
-	int rc;
-	rc = gtp_pdp_newpdp(gsn, pdp, imsi, nsapi, NULL);
-	return rc;
-}
 
-int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp)
-{
-	if (gsn->cb_delete_context)
-		gsn->cb_delete_context(pdp);
-	return pdp_freepdp(pdp);
-}
-
-/* Free pdp and all its secondary PDP contexts. Must be called on the primary PDP context. */
-int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp)
-{
-	int n;
-	struct pdp_t *secondary_pdp;
-	OSMO_ASSERT(!pdp->secondary);
-
-	for (n = 0; n < PDP_MAXNSAPI; n++) {
-		if (pdp->secondary_tei[n]) {
-			if (gtp_pdp_getgtp1(gsn, &secondary_pdp,
-					     pdp->secondary_tei[n])) {
-				LOGP(DLGTP, LOGL_ERROR,
-					"Unknown secondary PDP context\n");
-				continue;
-			}
-			if (pdp != secondary_pdp) {
-				gtp_freepdp(gsn, secondary_pdp);
-			}
-		}
-	}
-
-	return gtp_freepdp(gsn, pdp);
-}
-
-/* gtp_gpdu */
-
-extern int gtp_fd(struct gsn_t *gsn)
-{
-	return gsn->fd0;
-}
-
-/* gtp_decaps */
-/* gtp_retrans */
-/* gtp_retranstimeout */
-
-int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
-			 int (*cb) (struct sockaddr_in * peer))
-{
-	gsn->cb_unsup_ind = cb;
-	return 0;
-}
-
-int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
-			     int (*cb) (struct sockaddr_in * peer))
-{
-	gsn->cb_extheader_ind = cb;
-	return 0;
-}
-
-int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
-			     int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie))
-{
-	gsn->cb_ran_info_relay_ind = cb;
-	return 0;
-}
-
-/* API: Initialise delete context callback */
-/* Called whenever a pdp context is deleted for any reason */
-int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb) (struct pdp_t * pdp))
-{
-	gsn->cb_delete_context = cb;
-	return 0;
-}
-
-int gtp_set_cb_conf(struct gsn_t *gsn,
-		    int (*cb) (int type, int cause,
-			       struct pdp_t * pdp, void *cbp))
-{
-	gsn->cb_conf = cb;
-	return 0;
-}
 
 static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer,
 			     struct pdp_t * pdp, uint8_t recovery)
@@ -230,50 +144,6 @@
 		gsn->cb_recovery3(gsn, peer, pdp, recovery);
 }
 
-int gtp_set_cb_recovery(struct gsn_t *gsn,
-			int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
-{
-	gsn->cb_recovery = cb;
-	return 0;
-}
-
-/* cb_recovery()
- * pdp may be NULL if Recovery IE was received from a message independent
- * of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
- * local setup. In case pdp is known, caller may want to keep that pdp alive to
- * handle subsequent msg cb as this specific pdp ctx is still valid according to
- * specs.
- */
-int gtp_set_cb_recovery2(struct gsn_t *gsn,
-			int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery))
-{
-	gsn->cb_recovery2 = cb_recovery2;
-	return 0;
-}
-
-/* cb_recovery()
- * pdp may be NULL if Recovery IE was received from a message independent
- * of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
- * local setup. In case pdp is known, caller may want to keep that pdp alive to
- * handle subsequent msg cb as this specific pdp ctx is still valid according to
- * specs.
- */
-int gtp_set_cb_recovery3(struct gsn_t *gsn,
-			 int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer,
-					      struct pdp_t *pdp, uint8_t recovery))
-{
-	gsn->cb_recovery3 = cb_recovery3;
-	return 0;
-}
-
-int gtp_set_cb_data_ind(struct gsn_t *gsn,
-			       int (*cb_data_ind) (struct pdp_t * pdp,
-						   void *pack, unsigned len))
-{
-	gsn->cb_data_ind = cb_data_ind;
-	return 0;
-}
-
 /**
  * get_default_gtp()
  * Generate a GPRS Tunneling Protocol signalling packet header, depending
@@ -393,108 +263,6 @@
 	}
 }
 
-static int queue_timer_retrans(struct gsn_t *gsn)
-{
-	/* Retransmit any outstanding packets */
-	/* Remove from queue if maxretrans exceeded */
-	time_t now;
-	struct qmsg_t *qmsg;
-	now = time(NULL);
-
-	/* get first element in queue, as long as the timeout of that
-	 * element has expired */
-	while ((!queue_getfirst(gsn->queue_req, &qmsg)) &&
-	       (qmsg->timeout <= now)) {
-		if (qmsg->retrans > N3_REQUESTS) {	/* Too many retrans */
-			LOGP(DLGTP, LOGL_NOTICE, "Retransmit req queue timeout of seq %" PRIu16 "\n",
-			     qmsg->seq);
-			if (gsn->cb_conf)
-				gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
-			queue_freemsg(gsn->queue_req, qmsg);
-		} else {
-			LOGP(DLGTP, LOGL_INFO, "Retransmit (%d) of seq %" PRIu16 "\n",
-			     qmsg->retrans, qmsg->seq);
-			if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
-				   (struct sockaddr *)&qmsg->peer,
-				   sizeof(struct sockaddr_in)) < 0) {
-				gsn->err_sendto++;
-				LOGP(DLGTP, LOGL_ERROR,
-					"Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s\n",
-					gsn->fd0, (unsigned long)&qmsg->p,
-					qmsg->l, strerror(errno));
-			}
-			queue_back(gsn->queue_req, qmsg);
-			qmsg->timeout = now + T3_REQUEST;
-			qmsg->retrans++;
-		}
-	}
-
-	/* Also clean up reply timeouts */
-	while ((!queue_getfirst(gsn->queue_resp, &qmsg)) &&
-	       (qmsg->timeout < now)) {
-		LOGP(DLGTP, LOGL_DEBUG, "Retransmit resp queue seq %"
-		     PRIu16 " expired, removing from queue\n", qmsg->seq);
-		queue_freemsg(gsn->queue_resp, qmsg);
-	}
-
-	return 0;
-}
-
-static int queue_timer_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
-{
-	time_t now, later, diff;
-	struct qmsg_t *qmsg;
-	timeout->tv_usec = 0;
-
-	if (queue_getfirst(gsn->queue_req, &qmsg)) {
-		timeout->tv_sec = 10;
-	} else {
-		now = time(NULL);
-		later = qmsg->timeout;
-		timeout->tv_sec = later - now;
-		if (timeout->tv_sec < 0)
-			timeout->tv_sec = 0;	/* No negative allowed */
-		if (timeout->tv_sec > 10)
-			timeout->tv_sec = 10;	/* Max sleep for 10 sec */
-	}
-
-	if (queue_getfirst(gsn->queue_resp, &qmsg)) {
-		/* already set by queue_req, do nothing */
-	} else { /* trigger faster if earlier timeout exists in queue_resp */
-		now = time(NULL);
-		later = qmsg->timeout;
-		diff = later - now;
-		if (diff < 0)
-			diff = 0;
-		if (diff < timeout->tv_sec)
-			timeout->tv_sec = diff;
-	}
-
-	return 0;
-}
-
-static void queue_timer_start(struct gsn_t *gsn)
-{
-	struct timeval next;
-
-	/* Retrieve next retransmission as timeval */
-	queue_timer_retranstimeout(gsn, &next);
-
-	/* re-schedule the timer */
-	osmo_timer_schedule(&gsn->queue_timer, next.tv_sec, next.tv_usec/1000);
-}
-
-/* timer callback for libgtp retransmission and ping */
-static void queue_timer_cb(void *data)
-{
-	struct gsn_t *gsn = data;
-
-	/* do all the retransmissions as needed */
-	queue_timer_retrans(gsn);
-
-	queue_timer_start(gsn);
-}
-
 /* ***********************************************************
  * Reliable delivery of signalling messages
  *
@@ -646,31 +414,12 @@
 
 		/* Rearm timer: Retrans time for qmsg just queued may be required
 		   before an existing one (for instance a gtp echo req) */
-		queue_timer_start(gsn);
+		gtp_queue_timer_start(gsn);
 	}
 	gsn->seq_next++;	/* Count up this time */
 	return 0;
 }
 
-
-/**
- * @brief clear the request and response queue. Useful for debugging to reset "some" state.
- * @param gsn The GGSN instance
- */
-void gtp_clear_queues(struct gsn_t *gsn)
-{
-	struct qmsg_t *qmsg;
-
-	LOGP(DLGTP, LOGL_INFO, "Clearing req & resp retransmit queues\n");
-	while (!queue_getfirst(gsn->queue_req, &qmsg)) {
-		queue_freemsg(gsn->queue_req, qmsg);
-	}
-
-	while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
-		queue_freemsg(gsn->queue_resp, qmsg);
-	}
-}
-
 /* gtp_conf
  * Remove signalling packet from retransmission queue.
  * return 0 on success, EOF if packet was not found */
@@ -705,20 +454,6 @@
 	return 0;
 }
 
-int gtp_retrans(struct gsn_t *gsn)
-{
-	/* dummy API, deprecated. */
-	return 0;
-}
-
-int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
-{
-	timeout->tv_sec = 24*60*60;
-	timeout->tv_usec = 0;
-	/* dummy API, deprecated. Return a huge timer to do nothing */
-	return 0;
-}
-
 static int gtp_resp(uint8_t version, struct gsn_t *gsn, struct pdp_t *pdp,
 	     union gtp_packet *packet, int len,
 	     struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid)
@@ -782,7 +517,7 @@
 
 		/* Rearm timer: Retrans time for qmsg just queued may be required
 		   before an existing one (for instance a gtp echo req) */
-		queue_timer_start(gsn);
+		gtp_queue_timer_start(gsn);
 	}
 	return 0;
 }
@@ -864,195 +599,6 @@
 	return 0;
 }
 
-/* Perform restoration and recovery error handling as described in 29.060 */
-static void log_restart(struct gsn_t *gsn)
-{
-	FILE *f;
-	int i, rc;
-	int counter = 0;
-	char *filename;
-
-	filename = talloc_asprintf(NULL, "%s/%s", gsn->statedir, RESTART_FILE);
-	OSMO_ASSERT(filename);
-
-	/* We try to open file. On failure we will later try to create file */
-	if (!(f = fopen(filename, "r"))) {
-		LOGP(DLGTP, LOGL_NOTICE,
-			"State information file (%s) not found. Creating new file.\n",
-			filename);
-	} else {
-		rc = fscanf(f, "%d", &counter);
-		if (rc != 1) {
-			LOGP(DLGTP, LOGL_ERROR,
-				"fscanf failed to read counter value\n");
-			goto close_file;
-		}
-		if (fclose(f)) {
-			LOGP(DLGTP, LOGL_ERROR,
-				"fclose failed: Error = %s\n", strerror(errno));
-		}
-	}
-
-	gsn->restart_counter = (unsigned char)counter;
-	gsn->restart_counter++;
-
-	/* Keep the umask closely wrapped around our fopen() call in case the
-	 * log outputs cause file creation. */
-	i = umask(022);
-	f = fopen(filename, "w");
-	umask(i);
-	if (!f) {
-		LOGP(DLGTP, LOGL_ERROR,
-			"fopen(path=%s, mode=%s) failed: Error = %s\n", filename,
-			"w", strerror(errno));
-		goto free_filename;
-	}
-
-	fprintf(f, "%d\n", gsn->restart_counter);
-close_file:
-	if (fclose(f))
-		LOGP(DLGTP, LOGL_ERROR,
-			"fclose failed: Error = %s\n", strerror(errno));
-free_filename:
-	talloc_free(filename);
-}
-
-int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
-	    int mode)
-{
-	struct sockaddr_in addr;
-
-	LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen));
-
-	*gsn = calloc(sizeof(struct gsn_t), 1);	/* TODO */
-
-	(*gsn)->statedir = statedir;
-	log_restart(*gsn);
-
-	/* Initialise sequence number */
-	(*gsn)->seq_next = (*gsn)->restart_counter * 1024;
-
-	/* Initialise request retransmit queue */
-	queue_new(&(*gsn)->queue_req);
-	queue_new(&(*gsn)->queue_resp);
-
-	/* Initialise pdp table */
-	pdp_init(*gsn);
-
-	/* Initialize internal queue timer */
-	osmo_timer_setup(&(*gsn)->queue_timer, queue_timer_cb, *gsn);
-
-	/* Initialise call back functions */
-	(*gsn)->cb_create_context_ind = 0;
-	(*gsn)->cb_delete_context = 0;
-	(*gsn)->cb_unsup_ind = 0;
-	(*gsn)->cb_conf = 0;
-	(*gsn)->cb_data_ind = 0;
-
-	/* Store function parameters */
-	(*gsn)->gsnc = *listen;
-	(*gsn)->gsnu = *listen;
-	(*gsn)->mode = mode;
-
-	/* Create GTP version 0 socket */
-	if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP(DLGTP, LOGL_ERROR,
-		     "GTPv0 socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
-			AF_INET, SOCK_DGRAM, 0, strerror(errno));
-		return -errno;
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
-	addr.sin_port = htons(GTP0_PORT);
-#if defined(__FreeBSD__) || defined(__APPLE__)
-	addr.sin_len = sizeof(addr);
-#endif
-
-	if (bind((*gsn)->fd0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
-			       "bind(fd0=%d) failed: Error = %s\n",
-			       (*gsn)->fd0, strerror(errno));
-		return -errno;
-	}
-
-	/* Create GTP version 1 control plane socket */
-	if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP(DLGTP, LOGL_ERROR,
-		     "GTPv1 control plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
-			AF_INET, SOCK_DGRAM, 0, strerror(errno));
-		return -errno;
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
-	addr.sin_port = htons(GTP1C_PORT);
-#if defined(__FreeBSD__) || defined(__APPLE__)
-	addr.sin_len = sizeof(addr);
-#endif
-
-	if (bind((*gsn)->fd1c, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
-				"bind(fd1c=%d) failed: Error = %s\n",
-				(*gsn)->fd1c, strerror(errno));
-		return -errno;
-	}
-
-	/* Create GTP version 1 user plane socket */
-	if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP(DLGTP, LOGL_ERROR,
-		     "GTPv1 user plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
-			AF_INET, SOCK_DGRAM, 0, strerror(errno));
-		return -errno;
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr = *listen;	/* Same IP for user traffic and signalling */
-	addr.sin_port = htons(GTP1U_PORT);
-#if defined(__FreeBSD__) || defined(__APPLE__)
-	addr.sin_len = sizeof(addr);
-#endif
-
-	if (bind((*gsn)->fd1u, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		(*gsn)->err_socket++;
-		LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
-			"bind(fd1u=%d) failed: Error = %s\n",
-			(*gsn)->fd1u, strerror(errno));
-		return -errno;
-	}
-
-	/* Start internal queue timer */
-	queue_timer_start(*gsn);
-
-	return 0;
-}
-
-int gtp_free(struct gsn_t *gsn)
-{
-
-	/* Cleanup internal queue timer */
-	osmo_timer_del(&gsn->queue_timer);
-
-	/* Clean up retransmit queues */
-	queue_free(gsn->queue_req);
-	queue_free(gsn->queue_resp);
-
-	close(gsn->fd0);
-	close(gsn->fd1c);
-	close(gsn->fd1u);
-
-	free(gsn);
-	return 0;
-}
-
 /* ***********************************************************
  * Path management messages
  * Messages: echo and version not supported.
@@ -1455,15 +1001,6 @@
 	return 0;
 }
 
-/* API: Register create context indication callback */
-int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
-				  int (*cb_create_context_ind) (struct pdp_t *
-								pdp))
-{
-	gsn->cb_create_context_ind = cb_create_context_ind;
-	return 0;
-}
-
 /* Send Create PDP Context Response */
 int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 			uint8_t cause)
diff --git a/gtp/gtp.h b/gtp/gtp.h
index 0583025..ede6f73 100644
--- a/gtp/gtp.h
+++ b/gtp/gtp.h
@@ -13,14 +13,10 @@
 #define _GTP_H
 
 #include <osmocom/core/utils.h>
-#include <osmocom/core/defs.h>
-#include <osmocom/core/timer.h>
 
 #include "gtpie.h"
 #include "pdp.h"
-
-#define GTP_MODE_GGSN 1
-#define GTP_MODE_SGSN 2
+#include "gsn.h"
 
 #define GTP0_PORT	3386
 #define GTP1C_PORT	2123
@@ -32,12 +28,10 @@
 #define GTP1_HEADER_SIZE_SHORT  8
 #define GTP1_HEADER_SIZE_LONG  12
 
+#define NAMESIZE 1024
 #define SYSLOG_PRINTSIZE 255
 #define ERRMSG_SIZE 255
 
-#define RESTART_FILE "gsn_restart"
-#define NAMESIZE 1024
-
 /* GTP version 1 extension header type definitions. */
 #define GTP_EXT_PDCP_PDU    0xC0	/* PDCP PDU Number */
 
@@ -232,105 +226,13 @@
 	struct gtp1_packet_long gtp1l;
 } __attribute__ ((packed));
 
-/* ***********************************************************
- * Information storage for each gsn instance
- *
- * Normally each instance of the application corresponds to
- * one instance of a gsn.
- *
- * In order to avoid global variables in the application, and
- * also in order to allow several instances of a gsn in the same
- * application this struct is provided in order to store all
- * relevant information related to the gsn.
- *
- * Note that this does not include information storage for '
- * each pdp context. This is stored in another struct.
- *************************************************************/
-
-struct gsn_t {
-	/* Parameters related to the network interface */
-
-	int fd0;		/* GTP0 file descriptor */
-	int fd1c;		/* GTP1 control plane file descriptor */
-	int fd1u;		/* GTP0 user plane file descriptor */
-	int mode;		/* Mode of operation: GGSN or SGSN */
-	struct in_addr gsnc;	/* IP address of this gsn for signalling */
-	struct in_addr gsnu;	/* IP address of this gsn for user traffic */
-
-	/* Parameters related to signalling messages */
-	uint16_t seq_next;	/* Next sequence number to use */
-	int seq_first;		/* First packet in queue (oldest timeout) */
-	int seq_last;		/* Last packet in queue (youngest timeout) */
-
-	unsigned char restart_counter;	/* Increment on restart. Stored on disk */
-	char *statedir;		/* Disk location for permanent storage */
-	void *priv;		/* used by libgtp users to attach their own state) */
-	struct queue_t *queue_req;	/* Request queue */
-	struct queue_t *queue_resp;	/* Response queue */
-
-	struct pdp_t pdpa[PDP_MAX];	/* PDP storage */
-	struct pdp_t *hashtid[PDP_MAX];	/* Hash table for IMSI + NSAPI */
-
-	struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
-
-	/* Call back functions */
-	int (*cb_delete_context) (struct pdp_t *);
-	int (*cb_create_context_ind) (struct pdp_t *);
-	int (*cb_unsup_ind) (struct sockaddr_in * peer);
-	int (*cb_extheader_ind) (struct sockaddr_in * peer);
-	int (*cb_ran_info_relay_ind) (struct sockaddr_in *peer, union gtpie_member **ie);
-	int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
-	int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
-	int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
-	int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
-	int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
-
-	/* Counters */
-
-	uint64_t err_socket;	/* Number of socket errors */
-	uint64_t err_readfrom;	/* Number of readfrom errors */
-	uint64_t err_sendto;	/* Number of sendto errors */
-	uint64_t err_memcpy;	/* Number of memcpy */
-	uint64_t err_queuefull;	/* Number of times queue was full */
-	uint64_t err_seq;	/* Number of seq out of range */
-	uint64_t err_address;	/* GSN address conversion failed */
-	uint64_t err_unknownpdp;	/* GSN address conversion failed */
-	uint64_t err_unknowntid;	/* Application supplied unknown imsi+nsapi */
-	uint64_t err_cause;	/* Unexpected cause value received */
-	uint64_t err_outofpdp;	/* Out of storage for PDP contexts */
-
-	uint64_t empty;		/* Number of empty packets */
-	uint64_t unsup;		/* Number of unsupported version 29.60 11.1.1 */
-	uint64_t tooshort;	/* Number of too short headers 29.60 11.1.2 */
-	uint64_t unknown;	/* Number of unknown messages 29.60 11.1.3 */
-	uint64_t unexpect;	/* Number of unexpected messages 29.60 11.1.4 */
-	uint64_t duplicate;	/* Number of duplicate or unsolicited replies */
-	uint64_t missing;	/* Number of missing information field messages */
-	uint64_t incorrect;	/* Number of incorrect information field messages */
-	uint64_t invalid;	/* Number of invalid message format messages */
-};
-
 /* External API functions */
 
 extern const char *gtp_version();
-extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
-		   int mode);
-
-extern int gtp_free(struct gsn_t *gsn);
-
-extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
-		      uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
-extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
-extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
 
 extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
 				  void *cbp);
 
-extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
-					 int (*cb_create_context_ind) (struct
-								       pdp_t *
-								       pdp));
-
 extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
 				   int cause);
 
@@ -351,53 +253,10 @@
 				  const uint8_t *rim_route_addr, size_t rim_route_addr_len,
 				  uint8_t rim_route_addr_discr);
 
-extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
-			       int (*cb_data_ind) (struct pdp_t * pdp,
-						   void *pack, unsigned len));
-
-extern int gtp_fd(struct gsn_t *gsn);
 extern int gtp_decaps0(struct gsn_t *gsn);
 extern int gtp_decaps1c(struct gsn_t *gsn);
 extern int gtp_decaps1u(struct gsn_t *gsn);
-extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
-extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
-
-extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
-				     int (*cb_delete_context) (struct pdp_t *
-							       pdp));
-/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
-  int (*cb_create_context) (struct pdp_t* pdp)); */
-
-extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
-				int (*cb) (struct sockaddr_in * peer));
-
-extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
-				    int (*cb) (struct sockaddr_in * peer));
-
-extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
-				    int (*cb) (struct sockaddr_in * peer, union gtpie_member **ie));
-
-extern int gtp_set_cb_conf(struct gsn_t *gsn,
-			   int (*cb) (int type, int cause, struct pdp_t * pdp,
-				      void *cbp));
-
-int gtp_set_cb_recovery(struct gsn_t *gsn,
-			int (*cb) (struct sockaddr_in * peer,
-				   uint8_t recovery))
-	OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
-int gtp_set_cb_recovery2(struct gsn_t *gsn,
-			int (*cb) (struct sockaddr_in * peer,
-				   struct pdp_t * pdp,
-				   uint8_t recovery))
-	OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");;
-int gtp_set_cb_recovery3(struct gsn_t *gsn,
-			int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
-				   struct pdp_t * pdp,
-				   uint8_t recovery));
-
-void gtp_clear_queues(struct gsn_t *gsn);
-
-/* Internal functions (not part of the API */
+/* Internal functions (not part of the API) */
 
 extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
 			struct in_addr *inetaddrs);