aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tun6.c
blob: 85139e59b47ead3c105c3c0ce68897166256a3b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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;
}