diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 7 | ||||
-rw-r--r-- | lib/gtp-kernel.c | 160 | ||||
-rw-r--r-- | lib/gtp-kernel.h | 38 | ||||
-rw-r--r-- | lib/tun.c | 73 | ||||
-rw-r--r-- | lib/tun.h | 3 |
5 files changed, 255 insertions, 26 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 55348ad..b6e7aba 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,12 @@ noinst_LIBRARIES = libmisc.a -noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h +noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c + +if ENABLE_GTP_KERNEL +AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS) +libmisc_a_SOURCES += gtp-kernel.c +endif diff --git a/lib/gtp-kernel.c b/lib/gtp-kernel.c new file mode 100644 index 0000000..48811bc --- /dev/null +++ b/lib/gtp-kernel.c @@ -0,0 +1,160 @@ +#ifdef __linux__ +#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ +#endif + +#include "../config.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <libgtpnl/gtp.h> +#include <libgtpnl/gtpnl.h> +#include <libmnl/libmnl.h> + +#include <errno.h> + +#include <time.h> + +#include "../lib/tun.h" +#include "../lib/syserr.h" +#include "../gtp/pdp.h" +#include "../gtp/gtp.h" + +#include <libgtpnl/gtp.h> +#include <libgtpnl/gtpnl.h> +#include <libmnl/libmnl.h> + +#include "gtp-kernel.h" + +static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp) +{ + struct in46_addr ia46; + struct in_addr ia; + + in46a_from_eua(&pdp->eua, &ia46); + gsna2in_addr(&ia, &pdp->gsnrc); + + LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix, + devname, pdp->version, + pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn, + in46a_ntoa(&ia46), inet_ntoa(ia)); +} + +static struct { + int genl_id; + struct mnl_socket *nl; +} gtp_nl; + +static int gtp_kernel_init_once(void) +{ + /* only initialize once */ + if (gtp_nl.nl) + return 0; + + gtp_nl.nl = genl_socket_open(); + if (gtp_nl.nl == NULL) { + LOGP(DGGSN, LOGL_ERROR, "cannot create genetlink socket\n"); + return -1; + } + gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp"); + if (gtp_nl.genl_id < 0) { + LOGP(DGGSN, LOGL_ERROR, "cannot lookup GTP genetlink ID\n"); + genl_socket_close(gtp_nl.nl); + gtp_nl.nl = NULL; + return -1; + } + LOGP(DGGSN, LOGL_NOTICE, "Initialized GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id); + + return 0; +} + +int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u) +{ + if (gtp_kernel_init_once() < 0) + return -1; + + return gtp_dev_create(dest_ns, devname, fd0, fd1u); +} + +int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u) +{ + if (gtp_kernel_init_once() < 0) + return -1; + + return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u); +} + +void gtp_kernel_stop(const char *devname) +{ + gtp_dev_destroy(devname); +} + +int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname) +{ + struct in_addr ms, sgsn; + struct gtp_tunnel *t; + int ret; + + pdp_debug(__func__, devname, pdp); + + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr)); + memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr)); + + gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); + gtp_tunnel_set_version(t, pdp->version); + gtp_tunnel_set_ms_ip4(t, &ms); + gtp_tunnel_set_sgsn_ip4(t, &sgsn); + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_i_tei(t, pdp->teid_own); + /* use the TEI advertised by SGSN when sending packets + * towards the SGSN */ + gtp_tunnel_set_o_tei(t, pdp->teid_gn); + } + + ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + return ret; +} + +int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname) +{ + struct gtp_tunnel *t; + int ret; + + pdp_debug(__func__, devname, pdp); + + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); + gtp_tunnel_set_version(t, pdp->version); + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_i_tei(t, pdp->teid_own); + } + + ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + return ret; +} diff --git a/lib/gtp-kernel.h b/lib/gtp-kernel.h new file mode 100644 index 0000000..464352c --- /dev/null +++ b/lib/gtp-kernel.h @@ -0,0 +1,38 @@ +#ifndef _GTP_KERNEL_H_ +#define _GTP_KERNEL_H_ + +struct gengetopt_args_info; + +extern int debug; +extern char *ipup; + +#ifdef GTP_KERNEL +int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u); +int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u); +void gtp_kernel_stop(const char *devname); + +int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname); +int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname); + +#else +static inline int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u) +{ + SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n"); + return -1; +} +#define gtp_kernel_create_sgsn gtp_kernel_create + +static inline void gtp_kernel_stop(const char *devname) {} + +static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname) +{ + return 0; +} + +static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname) +{ + return 0; +} + +#endif +#endif /* _GTP_KERNEL_H_ */ @@ -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_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *ds } } -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 @@ int tun_new(struct tun_t **tun, const char *dev_name) (*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; - } - - /* 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; + 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; } - 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 @@ int tun_free(struct tun_t *tun) 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); @@ -13,6 +13,7 @@ #ifndef _TUN_H #define _TUN_H +#include <stdbool.h> #include <net/if.h> #include "../lib/in46_addr.h" @@ -41,7 +42,7 @@ struct tun_t { void *priv; }; -extern int tun_new(struct tun_t **tun, const char *dev_name); +extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u); extern int tun_free(struct tun_t *tun); extern int tun_decaps(struct tun_t *this); extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len); |