pespin has uploaded this change for review.

View Change

ggsn: use libosmocore tundev API to create apn tun device

This way we can start dropping old osmo-ggsn specific API, avoiding
duplication of code.
Moreover, the osmo-ggsn code is using older ioctl APIs, which are
discouraged nowadays in favour of netlink, which osmo_tundev/osmo_netdev
from libosmocore is used.

While doing this, BSD code is dropped since anyway it's not been
maintained for a long time.
If needed, the BSD support can be added to libosmocore
osmo_tundev/osmo_netdev API.

This is a first step (already working). Follow-up commits will replace
the APIs to set up routes and addresses, and later on osmo-ggsn will win
support to set MTU on the interface.

Furthermore, this will allow easily adding netns support to osmo-ggsn
later on if ever needed.

Change-Id: I4d99ba147ac0f3b414d2efef0068b6b8d6cf0014
---
M TODO-RELEASE
M ggsn/ggsn.c
M ggsn/ggsn.h
M lib/tun.c
M lib/tun.h
M sgsnemu/sgsnemu.c
6 files changed, 90 insertions(+), 148 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/80/38480/1
diff --git a/TODO-RELEASE b/TODO-RELEASE
index 905c234..194885d 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -9,4 +9,5 @@
#library what description / commit summary line
libgtp append new field dir_tun_flags in struct pdp_t (older users not using the field should be fine since struct pdp_t is allocated internally)
libgtp ABI break new field cb_create_context_ind in struct gsn_t
-libgtp new API gtp_set_cb_update_context_ind(), gtp_update_context_resp()
\ No newline at end of file
+libgtp new API gtp_set_cb_update_context_ind(), gtp_update_context_resp()
+libosmocore >1.10.0 osmo_tundev_get_fd()
\ No newline at end of file
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 19b0132..7967f42 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -62,7 +62,6 @@

LLIST_HEAD(g_ggsn_list);

-static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);

void ggsn_close_one_pdp(struct pdp_t *pdp)
@@ -150,7 +149,6 @@
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;
@@ -234,10 +232,6 @@
}
LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", apn->tun.tun->devname);

- /* Register with libosmocore */
- osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, OSMO_FD_READ, ggsn_tun_fd_cb, apn, 0);
- osmo_fd_register(&apn->tun.fd);
-
/* Set TUN library callback */
tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
break;
@@ -799,16 +793,6 @@
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
}

-/* callback for tun device osmocom select loop integration */
-static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what)
-{
- struct apn_ctx *apn = fd->data;
-
- OSMO_ASSERT(what & OSMO_FD_READ);
-
- return tun_decaps(apn->tun.tun);
-}
-
/* callback for libgtp osmocom select loop integration */
static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
{
diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h
index a212a06..28f9d04 100644
--- a/ggsn/ggsn.h
+++ b/ggsn/ggsn.h
@@ -103,7 +103,6 @@
char *ipdown_script;
} cfg;
struct tun_t *tun;
- struct osmo_fd fd;
} tun;

/* ipv6 link-local address */
diff --git a/lib/tun.c b/lib/tun.c
index cb66fef..acc3927 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -41,20 +41,9 @@
#include <net/route.h>
#include <net/if.h>

-#if defined(__linux__)
#include <linux/if_tun.h>

-#elif defined (__FreeBSD__)
-#include <net/if_tun.h>
-#include <net/if_var.h>
-#include <netinet/in_var.h>
-
-#elif defined (__APPLE__)
-#include <net/if.h>
-
-#else
-#error "Unknown platform!"
-#endif
+#include <osmocom/core/msgb.h>

#include "tun.h"
#include "syserr.h"
@@ -155,121 +144,93 @@
}
}

+static int tun_tundev_data_ind_cb(struct osmo_tundev *tundev, struct msgb *msg)
+{
+ struct tun_t *tun = osmo_tundev_get_priv_data(tundev);
+ int rc = 0;
+ if (tun->cb_ind)
+ rc = tun->cb_ind(tun, msgb_data(msg), msgb_length(msg));
+ msgb_free(msg);
+ return rc;
+}
+
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
{
+ struct tun_t *t;
+ int rc;

-#if defined(__linux__)
- struct ifreq ifr;
-
-#elif defined(__FreeBSD__) || defined (__APPLE__)
- char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
- int devnum;
- struct ifaliasreq areq;
- int fd;
-#endif
-
- if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
+ t = talloc_zero(NULL, struct tun_t);
+ if (!t) {
+ SYS_ERR(DTUN, LOGL_ERROR, errno, "talloc_zero() failed");
return EOF;
}
+ *tun = t;

- (*tun)->cb_ind = NULL;
- (*tun)->addrs = 0;
- (*tun)->routes = 0;
+ t->cb_ind = NULL;
+ t->addrs = 0;
+ t->routes = 0;
+ t->fd = -1;

-#if defined(__linux__)
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");
+ osmo_strlcpy(t->devname, dev_name, IFNAMSIZ);
+ t->devname[IFNAMSIZ - 1] = 0;
+
+ t->tundev = osmo_tundev_alloc(t, dev_name);
+ if (!t->tundev)
goto err_free;
- }
+ osmo_tundev_set_priv_data(t->tundev, t);
+ osmo_tundev_set_data_ind_cb(t->tundev, tun_tundev_data_ind_cb);
+ rc = osmo_tundev_set_dev_name(t->tundev, dev_name);
+ if (rc < 0)
+ goto err_free_tundev;

- /* 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;
+ /* Open the actual tun device */
+ rc = osmo_tundev_open(t->tundev);
+ if (rc < 0)
+ goto err_free;
+ t->fd = osmo_tundev_get_fd(t->tundev);
+ t->netdev = osmo_tundev_get_netdev(t->tundev);

/* Disable checksums */
- if (ioctl((*tun)->fd, TUNSETNOCSUM, 1) < 0) {
- SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", (*tun)->devname);
+ if (ioctl(t->fd, TUNSETNOCSUM, 1) < 0) {
+ SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", t->devname);
}
+
+ LOGP(DTUN, LOGL_NOTICE, "tun %s configured\n", t->devname);
return 0;
+err_free_tundev:
+ osmo_tundev_free(t->tundev);
+err_free:
+ talloc_free(t);
+ *tun = NULL;
+ return -1;
+
} else {
- strncpy((*tun)->devname, dev_name, IFNAMSIZ);
- (*tun)->devname[IFNAMSIZ - 1] = 0;
- (*tun)->fd = -1;
+ osmo_strlcpy(t->devname, dev_name, IFNAMSIZ);
+ t->devname[IFNAMSIZ - 1] = 0;

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;
}
+ t->netdev = osmo_netdev_alloc(t, dev_name);
+ if (!t->netdev)
+ goto err_kernel_create;
+ rc = osmo_netdev_set_ifindex(t->netdev, if_nametoindex(dev_name));
+ if (rc < 0)
+ goto err_netdev_free;
+ rc = osmo_netdev_register(t->netdev);
+ if (rc < 0)
+ goto err_netdev_free;
LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
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);
- if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
- break;
- if (errno != EBUSY)
- break;
- }
- if ((*tun)->fd < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno,
- "Can't find tunnel device");
- goto err_free;
- }
-
- snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
- (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
-
- /* The tun device we found might have "old" IP addresses allocated */
- /* We need to delete those. This problem is not present on Linux */
-
- memset(&areq, 0, sizeof(areq));
-
- /* Set up interface name */
- strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
- areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
-
- /* Create a channel to the NET kernel. */
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
- goto err_close;
- }
-
- /* Delete any IP addresses until SIOCDIFADDR fails */
- while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
-
- close(fd);
- return 0;
-#endif
-
-err_close:
- close((*tun)->fd);
-err_free:
- free(*tun);
- *tun = NULL;
+err_netdev_free:
+ osmo_netdev_free(t->netdev);
+err_kernel_create:
+ gtp_kernel_stop(t->devname);
return -1;
+ }
}

int tun_free(struct tun_t *tun)
@@ -279,17 +240,23 @@
netdev_delroute4(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
}

- if (tun->fd >= 0) {
- if (close(tun->fd)) {
+ if (tun->tundev) {
+ if (osmo_tundev_close(tun->tundev) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
}
+ osmo_tundev_free(tun->tundev);
+ tun->tundev = NULL;
+ /* netdev is owned by tundev: */
+ tun->netdev = NULL;
+ } else {
+ /* netdev was allocated directly, free it: */
+ osmo_netdev_free(tun->netdev);
+ tun->netdev = NULL;
}

gtp_kernel_stop(tun->devname);

- /* TODO: For solaris we need to unlink streams */
-
- free(tun);
+ talloc_free(tun);
return 0;
}

@@ -300,30 +267,16 @@
return 0;
}

-int tun_decaps(struct tun_t *this)
-{
- unsigned char buffer[PACKET_MAX];
- int status;
-
- if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
- SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
- return -1;
- }
-
- if (this->cb_ind)
- return this->cb_ind(this, buffer, status);
-
- return 0;
-}
-
int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
{
+ struct msgb *msg = msgb_alloc(PACKET_MAX, "tun_tx");
int rc;
- rc = write(tun->fd, pack, len);
+
+ OSMO_ASSERT(msg);
+ memcpy(msgb_put(msg, len), pack, len);
+ rc = osmo_tundev_send(tun->tundev, msg);
if (rc < 0) {
SYS_ERR(DTUN, LOGL_ERROR, errno, "TUN(%s): write() failed", tun->devname);
- } else if (rc < len) {
- LOGTUN(LOGL_ERROR, tun, "short write() %d < %u\n", rc, len);
}
return rc;
}
diff --git a/lib/tun.h b/lib/tun.h
index 36a4f7e..8a3a2d4 100644
--- a/lib/tun.h
+++ b/lib/tun.h
@@ -16,6 +16,9 @@
#include <stdbool.h>
#include <net/if.h>

+#include <osmocom/core/netdev.h>
+#include <osmocom/core/tun.h>
+
#include "../lib/in46_addr.h"

#define PACKET_MAX 8196 /* Maximum packet size we receive */
@@ -30,6 +33,8 @@
*************************************************************/

struct tun_t {
+ struct osmo_tundev *tundev;
+ struct osmo_netdev *netdev;
int fd; /* File descriptor to tun interface */
struct in46_addr addr;
struct in46_addr dstaddr;
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 152b05b..d6d87b7 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -23,6 +23,7 @@

#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>

#include <ctype.h>
#include <netdb.h>
@@ -2186,9 +2187,8 @@

if (!signal_received) {

- if ((tun) && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) {
- SYS_ERR(DSGSN, LOGL_ERROR, 0,
- "TUN decaps failed");
+ if ((tun) && FD_ISSET(tun->fd, &fds)) {
+ osmo_select_main(1);
}

if (FD_ISSET(gsn->fd0, &fds))

To view, visit change 38480. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: I4d99ba147ac0f3b414d2efef0068b6b8d6cf0014
Gerrit-Change-Number: 38480
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>