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);