aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tun.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-08-03 00:47:03 +0200
committerHarald Welte <laforge@gnumonks.org>2017-08-11 13:12:04 +0200
commit2e48a44952fdc68eb3e5d3680f944507a4a129c1 (patch)
tree70ea7d0783c3a5ade0083442a08b7cafea087d04 /lib/tun.c
parent72a38b55e38407aa6c6b1cd32f848198ceee1287 (diff)
Support setting TUN device IPv6 address + prefix
As we can now have PDP contexts with IPv6 user IP payload, it is useful to extend the TUN related code to be able to configure the tun device IPv6 address + prefix length Change-Id: I899d21e52d02e0b8384af29ddd489ff19c8f2cf6
Diffstat (limited to 'lib/tun.c')
-rw-r--r--lib/tun.c123
1 files changed, 114 insertions, 9 deletions
diff --git a/lib/tun.c b/lib/tun.c
index 8325f5d..11ea7bc 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -1,6 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003, 2004 Mondru AB.
+ * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -37,16 +38,17 @@
#include <sys/socket.h>
#include <errno.h>
#include <net/route.h>
+#include <net/if.h>
#if defined(__linux__)
-#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#elif defined (__FreeBSD__)
-#include <net/if.h>
#include <net/if_tun.h>
+#include <net/if_var.h>
+#include <netinet/in_var.h>
#elif defined (__APPLE__)
#include <net/if.h>
@@ -65,8 +67,13 @@
#include "tun.h"
#include "syserr.h"
+static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
+ struct in_addr *dstaddr, struct in_addr *netmask);
+
#if defined(__linux__)
+#include <linux/ipv6.h>
+
int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
@@ -247,7 +254,7 @@ int tun_addaddr(struct tun_t *this,
struct msghdr msg;
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask);
+ return tun_setaddr4(this, addr, dstaddr, netmask);
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
@@ -345,7 +352,7 @@ int tun_addaddr(struct tun_t *this,
/* TODO: Is this needed on FreeBSD? */
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
+ return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
memset(&areq, 0, sizeof(areq));
@@ -391,7 +398,7 @@ int tun_addaddr(struct tun_t *this,
#elif defined (__sun__)
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask);
+ return tun_setaddr4(this, addr, dstaddr, netmask);
SYS_ERR(DTUN, LOGL_ERROR, errno,
"Setting multiple addresses not possible on Solaris");
@@ -403,9 +410,8 @@ int tun_addaddr(struct tun_t *this,
}
-int tun_setaddr(struct tun_t *this,
- struct in_addr *addr,
- struct in_addr *dstaddr, struct in_addr *netmask)
+static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
+ struct in_addr *dstaddr, struct in_addr *netmask)
{
struct ifreq ifr;
int fd;
@@ -498,13 +504,112 @@ int tun_setaddr(struct tun_t *this,
tun_sifflags(this, IFF_UP | IFF_RUNNING);
#if defined(__FreeBSD__) || defined (__APPLE__)
- tun_addroute(this, dstaddr, addr, netmask);
+ tun_addroute(this, dstaddr, addr, &this->netmask);
+ this->routes = 1;
+#endif
+
+ return 0;
+}
+
+static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
+ size_t prefixlen)
+{
+ struct in6_ifreq ifr;
+ int fd;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+#if defined(__linux__)
+ ifr.ifr6_prefixlen = prefixlen;
+ ifr.ifr6_ifindex = if_nametoindex(this->devname);
+ if (ifr.ifr6_ifindex == 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
+ return -1;
+ }
+#elif defined(__FreeBSD__) || defined (__APPLE__)
+ strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+#endif
+
+ /* Create a channel to the NET kernel */
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
+ return -1;
+ }
+
+#if defined(__linux__)
+ if (addr) {
+ memcpy(&this->addr, addr, sizeof(*addr));
+ memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
+ if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+ if (errno != EEXIST) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
+ } else {
+ SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address alreadsy exists");
+ }
+ close(fd);
+ return -1;
+ }
+ }
+
+#if 0
+ /* FIXME: looks like this is not possible/necessary for IPv6? */
+ if (dstaddr) {
+ memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
+ memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
+ if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
+ close(fd);
+ return -1;
+ }
+ }
+#endif
+
+#elif defined(__FreeBSD__) || defined (__APPLE__)
+ if (addr)
+ memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
+ if (dstaddr)
+ memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
+
+ if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
+ close(fd);
+ return -1;
+ }
+#endif
+
+ close(fd);
+ this->addrs++;
+
+ /* On linux the route to the interface is set automatically
+ on FreeBSD we have to do this manually */
+
+ /* TODO: How does it work on Solaris? */
+
+ tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if 0 /* FIXME */
+//#if defined(__FreeBSD__) || defined (__APPLE__)
+ tun_addroute6(this, dstaddr, addr, prefixlen);
this->routes = 1;
#endif
return 0;
}
+int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
+{
+ struct in_addr netmask;
+ switch (addr->len) {
+ case 4:
+ netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
+ return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
+ case 16:
+ return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
+ default:
+ return -1;
+ }
+}
+
int tun_route(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask, int delete)