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/lib/tun.c b/lib/tun.c
index 6498945..fa4c37d 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -19,6 +19,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -57,6 +58,7 @@
 
 #include "tun.h"
 #include "syserr.h"
+#include "gtp-kernel.h"
 
 static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
 			struct in_addr *dstaddr, struct in_addr *netmask)
@@ -147,7 +149,7 @@
 	}
 }
 
-int tun_new(struct tun_t **tun, const char *dev_name)
+int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
 {
 
 #if defined(__linux__)
@@ -170,31 +172,50 @@
 	(*tun)->routes = 0;
 
 #if defined(__linux__)
-	/* Open the actual tun device */
-	if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
-		SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
-		goto err_free;
+	if (!use_kernel) {
+		/* Open the actual tun device */
+		if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
+			SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
+			goto err_free;
+		}
+
+		/* Set device flags. For some weird reason this is also the method
+		   used to obtain the network interface name */
+		memset(&ifr, 0, sizeof(ifr));
+		if (dev_name)
+			strcpy(ifr.ifr_name, dev_name);
+		ifr.ifr_flags = IFF_TUN | IFF_NO_PI;	/* Tun device, no packet info */
+		if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
+			SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
+			goto err_close;
+		}
+
+		strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
+		(*tun)->devname[IFNAMSIZ - 1] = 0;
+
+		ioctl((*tun)->fd, TUNSETNOCSUM, 1);	/* Disable checksums */
+		return 0;
+	} else {
+		strncpy((*tun)->devname, dev_name, IFNAMSIZ);
+		(*tun)->devname[IFNAMSIZ - 1] = 0;
+		(*tun)->fd = -1;
+
+		if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
+			LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
+				strerror(errno));
+			return -1;
+		}
+		LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
+		return 0;
 	}
 
-	/* Set device flags. For some weird reason this is also the method
-	   used to obtain the network interface name */
-	memset(&ifr, 0, sizeof(ifr));
-	if (dev_name)
-		strcpy(ifr.ifr_name, dev_name);
-	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;	/* Tun device, no packet info */
-	if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
-		SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
-		goto err_close;
-	}
-
-	strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
-	(*tun)->devname[IFNAMSIZ - 1] = 0;
-
-	ioctl((*tun)->fd, TUNSETNOCSUM, 1);	/* Disable checksums */
-	return 0;
-
 #elif defined(__FreeBSD__) || defined (__APPLE__)
 
+	if (use_kernel) {
+		LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
+		return -1;
+	}
+
 	/* Find suitable device */
 	for (devnum = 0; devnum < 255; devnum++) {	/* TODO 255 */
 		snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
@@ -249,10 +270,14 @@
 		netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
 	}
 
-	if (close(tun->fd)) {
-		SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
+	if (tun->fd >= 0) {
+		if (close(tun->fd)) {
+			SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
+		}
 	}
 
+	gtp_kernel_stop(tun->devname);
+
 	/* TODO: For solaris we need to unlink streams */
 
 	free(tun);