Move kernel GTP support from ggsn/ to lib/

This way, the IP address / route handling between TUN devices and kernel
GTP can be shared, which will provide not only a unified codebase but
also a more consistent behavior.

This also paves the road for to use kernel GTP from sgsnemu in the future.

Related: OS#3214
Change-Id: Ic53a971136edd0d8871fbd6746d7b0090ce3a188
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 85a2211..4636c04 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -63,9 +63,9 @@
 #include "../lib/ippool.h"
 #include "../lib/syserr.h"
 #include "../lib/in46_addr.h"
+#include "../lib/gtp-kernel.h"
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
-#include "gtp-kernel.h"
 #include "icmpv6.h"
 #include "ggsn.h"
 
@@ -125,13 +125,14 @@
 			LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
 			tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
 		}
-		/* release tun device */
-		LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
-		osmo_fd_unregister(&apn->tun.fd);
+		if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
+			/* release tun device */
+			LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
+			osmo_fd_unregister(&apn->tun.fd);
+		}
 		tun_free(apn->tun.tun);
 		apn->tun.tun = NULL;
 	}
-	gtp_kernel_stop(apn->tun.cfg.dev_name);
 
 	if (apn->v4.pool) {
 		LOGPAPN(LOGL_INFO, apn, "Releasing IPv4 pool\n");
@@ -195,6 +196,7 @@
 	struct in46_prefix ipv6_tun_linklocal_ip;
 	struct in46_prefix *blacklist;
 	int blacklist_size;
+	struct gsn_t *gsn = apn->ggsn->gsn;
 	int rc;
 
 	if (apn->started)
@@ -204,7 +206,7 @@
 	switch (apn->cfg.gtpu_mode) {
 	case APN_GTPU_MODE_TUN:
 		LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
-		if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name)) {
+		if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
 			LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
 			return -1;
 		}
@@ -216,66 +218,6 @@
 
 		/* Set TUN library callback */
 		tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
-
-		if (apn->v4.cfg.ifconfig_prefix.addr.len) {
-			LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
-				in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
-			if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
-					apn->v4.cfg.ifconfig_prefix.prefixlen)) {
-				LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
-					in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
-				apn_stop(apn, false);
-				return -1;
-			}
-		}
-
-		if (apn->v6.cfg.ifconfig_prefix.addr.len) {
-			LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
-				in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
-			if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
-					apn->v6.cfg.ifconfig_prefix.prefixlen)) {
-				LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
-					"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
-					in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
-				apn_stop(apn, false);
-				return -1;
-			}
-		}
-
-		if (apn->v6.cfg.ll_prefix.addr.len) {
-			LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 link-local address %s\n",
-				in46p_ntoa(&apn->v6.cfg.ll_prefix));
-			if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr, NULL,
-					apn->v6.cfg.ll_prefix.prefixlen)) {
-				LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
-					"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
-					in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
-				apn_stop(apn, false);
-				return -1;
-			}
-			apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
-		}
-
-		if (apn->tun.cfg.ipup_script) {
-			LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
-				apn->tun.cfg.ipup_script);
-			tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
-		}
-
-		if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
-		    apn->v6.cfg.ll_prefix.addr.len == 0) {
-			rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
-			if (rc < 1) {
-				LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
-					rc ? strerror(errno) : "tun interface has no link-local IP assigned");
-				apn_stop(apn, false);
-				return -1;
-			}
-			apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
-		}
-
-		/* set back-pointer from TUN device to APN */
-		apn->tun.tun->priv = apn;
 		break;
 	case APN_GTPU_MODE_KERNEL_GTP:
 		LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
@@ -284,7 +226,7 @@
 			apn_stop(apn, false);
 			return -1;
 		}
-		if (apn->ggsn->gsn == NULL) {
+		if (gsn == NULL) {
 			/* skip bringing up the APN now if the GSN is not initialized yet.
 			 * This happens during initial load of the config file, as the
 			 * "no shutdown" in the ggsn node only happens after the "apn" nodes
@@ -293,8 +235,8 @@
 			return 0;
 		}
 		/* use GTP kernel module for data packet encapsulation */
-		if (gtp_kernel_init(apn->ggsn->gsn, apn->tun.cfg.dev_name,
-				    &apn->v4.cfg.ifconfig_prefix, apn->tun.cfg.ipup_script) < 0) {
+		if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
+			LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
 			return -1;
 		}
 		break;
@@ -303,6 +245,68 @@
 		return -1;
 	}
 
+	/* common initialization below */
+
+	/* set back-pointer from TUN device to APN */
+	apn->tun.tun->priv = apn;
+
+	if (apn->v4.cfg.ifconfig_prefix.addr.len) {
+		LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
+			in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
+		if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
+				apn->v4.cfg.ifconfig_prefix.prefixlen)) {
+			LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
+				in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
+			apn_stop(apn, false);
+			return -1;
+		}
+	}
+
+	if (apn->v6.cfg.ifconfig_prefix.addr.len) {
+		LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
+			in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
+		if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
+				apn->v6.cfg.ifconfig_prefix.prefixlen)) {
+			LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
+				"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
+				in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
+			apn_stop(apn, false);
+			return -1;
+		}
+	}
+
+	if (apn->v6.cfg.ll_prefix.addr.len) {
+		LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 link-local address %s\n",
+			in46p_ntoa(&apn->v6.cfg.ll_prefix));
+		if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr, NULL,
+				apn->v6.cfg.ll_prefix.prefixlen)) {
+			LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
+				"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
+				in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
+			apn_stop(apn, false);
+			return -1;
+		}
+		apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
+	}
+
+	if (apn->tun.cfg.ipup_script) {
+		LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
+			apn->tun.cfg.ipup_script);
+		tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
+	}
+
+	if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
+	    apn->v6.cfg.ll_prefix.addr.len == 0) {
+		rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
+		if (rc < 1) {
+			LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
+				rc ? strerror(errno) : "tun interface has no link-local IP assigned");
+			apn_stop(apn, false);
+			return -1;
+		}
+		apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
+	}
+
 	/* Create IPv4 pool */
 	if (apn->v4.cfg.dynamic_prefix.addr.len) {
 		LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",