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.
Even better, we win for free non-blocking write behavior in the tundev
when switching to the new API, since it already has its own internal
wqueue.

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.

Depends: libosmocore.git Change-Id Ia8a7e7ec6d96c7aebc80528236a0e0d035e7f38d
Change-Id: I4d99ba147ac0f3b414d2efef0068b6b8d6cf0014
This commit is contained in:
Pau Espin Pedrol
2024-10-21 13:37:40 +02:00
parent 41bec9529f
commit 38b607ece3
6 changed files with 90 additions and 148 deletions

View File

@@ -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()
libgtp new API gtp_set_cb_update_context_ind(), gtp_update_context_resp()
libosmocore >1.10.0 osmo_tundev_get_fd()

View File

@@ -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 @@ int apn_stop(struct apn_ctx *apn)
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 @@ int apn_start(struct apn_ctx *apn)
}
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 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
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)
{

View File

@@ -97,7 +97,6 @@ struct apn_ctx {
char *ipdown_script;
} cfg;
struct tun_t *tun;
struct osmo_fd fd;
} tun;
/* ipv6 link-local address */

207
lib/tun.c
View File

@@ -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 @@ int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *ds
}
}
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 @@ int tun_free(struct tun_t *tun)
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 @@ int tun_set_cb_ind(struct tun_t *this,
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;
}

View File

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

View File

@@ -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 @@ int main(int argc, char **argv)
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))