aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tun6.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tun6.c')
-rw-r--r--lib/tun6.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/lib/tun6.c b/lib/tun6.c
new file mode 100644
index 0000000..85139e5
--- /dev/null
+++ b/lib/tun6.c
@@ -0,0 +1,96 @@
+#include <unistd.h>
+#include <string.h>
+#include <stropts.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/ipv6.h>
+
+#include "tun.h"
+#include "syserr.h"
+
+/* Defined in tun.c */
+int tun_sifflags(struct tun_t *this, int flags);
+
+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(&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 already 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;
+}