further IPv6 changes

This commit is contained in:
Chris Hellberg 2022-05-08 19:45:32 +00:00
parent 3eb959c8fe
commit 846082f13e
11 changed files with 427 additions and 94 deletions

View File

@ -9,7 +9,7 @@ ARCH = `uname -m`
HEAD_COMMIT = `git rev-parse --short HEAD` HEAD_COMMIT = `git rev-parse --short HEAD`
LIBPATH = -L. LIBPATH = -L.
LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH) LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH) -lm
CFLAGS += -std=c99 -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` -DGITREVISION=\"$(HEAD_COMMIT)\" CFLAGS += -std=c99 -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` -DGITREVISION=\"$(HEAD_COMMIT)\"
CFLAGS += -Wstrict-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-prototypes CFLAGS += -Wstrict-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-prototypes

View File

@ -28,6 +28,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <zlib.h> #include <zlib.h>
#include <time.h> #include <time.h>
#include <stdbool.h>
#ifdef WINDOWS32 #ifdef WINDOWS32
#include "windows.h" #include "windows.h"
@ -101,6 +103,7 @@ static time_t lastdownstreamtime;
static long send_query_sendcnt = -1; static long send_query_sendcnt = -1;
static long send_query_recvcnt = 0; static long send_query_recvcnt = 0;
static int hostname_maxlen = 0xFF; static int hostname_maxlen = 0xFF;
static bool use_v6 = false;
void void
client_init() client_init()
@ -1374,7 +1377,7 @@ handshake_version(int dns_fd, int *seed)
} }
static int static int
handshake_login(int dns_fd, int seed, int forward_v6) handshake_login(int dns_fd, int seed)
{ {
char in[4096]; char in[4096];
char login[16]; char login[16];
@ -1405,7 +1408,7 @@ handshake_login(int dns_fd, int seed, int forward_v6)
server[64] = 0; server[64] = 0;
client[64] = 0; client[64] = 0;
if (tun_setip(client, server, netmask, forward_v6) == 0 && if (tun_setip(client, server, netmask) == 0 &&
tun_setmtu(mtu) == 0) { tun_setmtu(mtu) == 0) {
fprintf(stderr, "Server tunnel IP is %s\n", server); fprintf(stderr, "Server tunnel IP is %s\n", server);
@ -2326,7 +2329,7 @@ handshake_set_fragsize(int dns_fd, int fragsize)
} }
int int
client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize, int forward_v6) client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize)
{ {
int seed; int seed;
int upcodec; int upcodec;
@ -2349,7 +2352,7 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz
return r; return r;
} }
r = handshake_login(dns_fd, seed, forward_v6); r = handshake_login(dns_fd, seed);
if (r) { if (r) {
return r; return r;
} }
@ -2414,8 +2417,72 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz
handshake_set_fragsize(dns_fd, fragsize); handshake_set_fragsize(dns_fd, fragsize);
if (!running) if (!running)
return -1; return -1;
handshake_check_v6(dns_fd);
if (!running)
return -1;
} }
return 0; return 0;
} }
static
void send_v6_probe(int dns_fd)
{
char data[4096];
data[0] = userid;
send_packet(dns_fd, 'g', data, sizeof(data));
}
int
handshake_check_v6(int dns_fd)
{
char in[4096];
char server6[1024];
char client6[1024];
int i;
int read;
int netmask6 = 0;
fprintf(stderr, "Autoprobing server IPV6 tunnel support\n");
for (i = 0; running && i < 5; i++) {
send_v6_probe(dns_fd);
read = handshake_waitdns(dns_fd, in, sizeof(in), 'g', 'G', i+1);
if (read > 0) {
/*
* including a terminating dash to allow for future IPv6 options, e.g.
* netmask. Currently assumes /64. MTU is taken from the IPv4 handshake.
* A future IPv6-only implementation would need to pass mtu
* in the IPV6 handshake.
*/
if (sscanf(in, "%512[^-]-%512[^-]-%d", server6, client6, &netmask6) == 3) {
fprintf(stderr, "Server tunnel IPv6 is %s\n", server6);
fprintf(stderr, "Local tunnel IPv6 is %s\n", client6);
if (tun_setip6(client6, server6, netmask6) == 0) {
use_v6 = true;
return 0;
} else {
errx(4, "Failed to set IPv6 tunnel address");
}
} else {
fprintf(stderr, "Received bad IPv6 tunnel handshake\n");
}
}
fprintf(stderr, "Retrying IPv6 tunnel handshake...\n");
}
if (!running)
return -1;
return 0;
}

View File

@ -35,7 +35,7 @@ void client_set_lazymode(int lazy_mode);
void client_set_hostname_maxlen(int i); void client_set_hostname_maxlen(int i);
int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size,
int fragsize, int forward_v6); int fragsize);
int client_tunnel(int tun_fd, int dns_fd); int client_tunnel(int tun_fd, int dns_fd);
int handshake_check_v6(int tun_fd);
#endif #endif

View File

@ -13,6 +13,8 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
*/ */
#include <time.h> #include <time.h>
@ -557,3 +559,17 @@ fd_set_close_on_exec(int fd)
} }
#endif #endif
bool
isV6AddrSet(struct in6_addr *ip6)
{
int i;
for (i = 0; i < sizeof(ip6->s6_addr); i++) {
if (ip6->s6_addr[i] != 0) {
return true;
}
}
return false;
}

View File

@ -33,6 +33,7 @@
extern const unsigned char raw_header[RAW_HDR_LEN]; extern const unsigned char raw_header[RAW_HDR_LEN];
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#ifdef WINDOWS32 #ifdef WINDOWS32
#include "windows.h" #include "windows.h"
#else #else
@ -129,6 +130,7 @@ void read_password(char*, size_t);
int check_topdomain(char *, int, char **); int check_topdomain(char *, int, char **);
int query_datalen(const char *qname, const char *topdomain); int query_datalen(const char *qname, const char *topdomain);
bool isV6AddrSet(struct in6_addr *);
#if defined(WINDOWS32) || defined(ANDROID) #if defined(WINDOWS32) || defined(ANDROID)
#ifndef ANDROID #ifndef ANDROID

View File

@ -72,7 +72,7 @@ static void help(FILE *stream, bool verbose)
{ {
fprintf(stream, fprintf(stream,
"iodine IP over DNS tunneling client\n\n" "iodine IP over DNS tunneling client\n\n"
"Usage: %s [-46fhrvS] [-u user] [-t chrootdir] [-d device] [-P password]\n" "Usage: %s [-46fhrv] [-u user] [-t chrootdir] [-d device] [-P password]\n"
" [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec]\n" " [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec]\n"
" [-z context] [-F pidfile] [nameserver] topdomain\n", __progname); " [-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
@ -100,7 +100,6 @@ static void help(FILE *stream, bool verbose)
" -t dir to chroot to directory dir\n" " -t dir to chroot to directory dir\n"
" -d device to set tunnel device name\n" " -d device to set tunnel device name\n"
" -z context, to apply specified SELinux context after initialization\n" " -z context, to apply specified SELinux context after initialization\n"
" -S enable forwarding of IPv6 packets within the tunnel\n"
" -F pidfile to write pid to a file\n\n" " -F pidfile to write pid to a file\n\n"
"nameserver is the IP number/hostname of the relaying nameserver. If absent,\n" "nameserver is the IP number/hostname of the relaying nameserver. If absent,\n"
" /etc/resolv.conf is used\n" " /etc/resolv.conf is used\n"
@ -153,7 +152,6 @@ int main(int argc, char **argv)
struct sockaddr_storage nameservaddr; struct sockaddr_storage nameservaddr;
int nameservaddr_len; int nameservaddr_len;
int nameserv_family; int nameserv_family;
int forward_v6;
nameserv_host = NULL; nameserv_host = NULL;
topdomain = NULL; topdomain = NULL;
@ -178,7 +176,6 @@ int main(int argc, char **argv)
selecttimeout = 4; selecttimeout = 4;
hostname_maxlen = 0xFF; hostname_maxlen = 0xFF;
nameserv_family = AF_UNSPEC; nameserv_family = AF_UNSPEC;
forward_v6 = 0;
#ifdef WINDOWS32 #ifdef WINDOWS32
WSAStartup(req_version, &wsa_data); WSAStartup(req_version, &wsa_data);
@ -195,7 +192,8 @@ int main(int argc, char **argv)
__progname++; __progname++;
#endif #endif
while ((choice = getopt(argc, argv, "t:d:R:P:m:M:z:F:T:O:L:I:46vfhruS")) != -1) { while ((choice = getopt(argc, argv, "46vfhru:t:d:R:P:m:M:F:T:O:L:I:")) != -1) {
switch(choice) { switch(choice) {
case '4': case '4':
nameserv_family = AF_INET; nameserv_family = AF_INET;
@ -220,9 +218,6 @@ int main(int argc, char **argv)
case 'u': case 'u':
username = optarg; username = optarg;
break; break;
case 'S':
forward_v6 = 1;
break;
case 't': case 't':
newroot = optarg; newroot = optarg;
break; break;
@ -372,7 +367,7 @@ int main(int argc, char **argv)
fprintf(stderr, "Sending DNS queries for %s to %s\n", fprintf(stderr, "Sending DNS queries for %s to %s\n",
topdomain, format_addr(&nameservaddr, nameservaddr_len)); topdomain, format_addr(&nameservaddr, nameservaddr_len));
if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size, forward_v6)) { if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
retval = 1; retval = 1;
goto cleanup2; goto cleanup2;
} }

View File

@ -88,10 +88,16 @@ static int created_users;
static int check_ip; static int check_ip;
static int my_mtu; static int my_mtu;
static in_addr_t my_ip; static in_addr_t my_ip;
char display_ip6[INET6_ADDRSTRLEN];
char *display_ip6_buffer;
char *ip6_netmask_buffer;
static struct in6_addr my_ip6;
static int netmask; static int netmask;
static int ip6_netmask = 64;
static in_addr_t ns_ip; static in_addr_t ns_ip;
static int bind_port; static int bind_port;
static int debug; static int debug;
@ -658,9 +664,9 @@ static int tunnel_tun(int tun_fd, struct dnsfd *dns_fds)
/* find target ip in packet, in is padded with 4 bytes TUN header */ /* find target ip in packet, in is padded with 4 bytes TUN header */
header = (struct ip*) (in + 4); header = (struct ip*) (in + 4);
ip_version = in[4] & 0xf0; ip_version = get_ipversion(in[4]);
if (ip_version == 64) { /* IPv4 */ if (ip_version == 4) { /* IPv4 */
header = (struct ip*) (in + 4); header = (struct ip*) (in + 4);
userid = find_user_by_ip(header->ip_dst.s_addr); userid = find_user_by_ip(header->ip_dst.s_addr);
} else { /* IPv6 */ } else { /* IPv6 */
@ -674,6 +680,7 @@ static int tunnel_tun(int tun_fd, struct dnsfd *dns_fds)
return 0; return 0;
outlen = sizeof(out); outlen = sizeof(out);
compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9); compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9);
if (users[userid].conn == CONN_DNS_NULL) { if (users[userid].conn == CONN_DNS_NULL) {
@ -1303,6 +1310,32 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
!users[userid].lazy) !users[userid].lazy)
send_chunk_or_dataless(dns_fd, userid, &users[userid].q); send_chunk_or_dataless(dns_fd, userid, &users[userid].q);
/* IPv6 tunnel address probe */
} else if (in[0] == 'G' || in[0] == 'g') {
char client_ip6[INET6_ADDRSTRLEN];
char display_my_ip6[INET6_ADDRSTRLEN];
read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, &base32_ops);
if (read < 1) {
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
}
/* Ping packet, store userid */
userid = unpacked[0];
if (check_authenticated_user_and_ip(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
inet_ntop(AF_INET6, &users[userid].tun_ip6, client_ip6, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &my_ip6, display_my_ip6, INET6_ADDRSTRLEN);
read = snprintf(out, sizeof(out), "%s-%s-%d-",
display_my_ip6, client_ip6, ip6_netmask);
write_dns(dns_fd, q, out, read, users[userid].downenc);
return;
} else if ((in[0] >= '0' && in[0] <= '9') } else if ((in[0] >= '0' && in[0] <= '9')
|| (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'a' && in[0] <= 'f')
|| (in[0] >= 'A' && in[0] <= 'F')) { || (in[0] >= 'A' && in[0] <= 'F')) {
@ -2288,10 +2321,10 @@ write_dns(int fd, struct query *q, const char *data, int datalen, char downenc)
static void print_usage(FILE *stream) static void print_usage(FILE *stream)
{ {
fprintf(stream, fprintf(stream,
"Usage: %s [-46cDfsvS] [-u user] [-t chrootdir] [-d device] [-m mtu]\n" "Usage: %s [-46cDfsv] [-u user] [-t chrootdir] [-d device] [-m mtu]\n"
" [-z context] [-l ipv4 listen address] [-L ipv6 listen address]\n" " [-z context] [-l ipv4 listen address] [-L ipv6 listen address]\n"
" [-p port] [-n auto|external_ip] [-b dnsport] [-P password]\n" " [-p port] [-n auto|external_ip] [-b dnsport] [-P password]\n"
" [-F pidfile] [-i max idle time] tunnel_ip[/netmask] topdomain\n", " [-F pidfile] [-S ipv6 tunnel address] [-i max idle time] tunnel_ip[/netmask] topdomain\n",
__progname); __progname);
} }
@ -2333,7 +2366,7 @@ static void help(FILE *stream)
" -b port to forward normal DNS queries to (on localhost)\n" " -b port to forward normal DNS queries to (on localhost)\n"
" -P password used for authentication (max 32 chars will be used)\n" " -P password used for authentication (max 32 chars will be used)\n"
" -F pidfile to write pid to a file\n" " -F pidfile to write pid to a file\n"
" -S enable forwarding of IPv6 packets within the tunnel\n" " -S IPv6 server address within the tunnel. Netmask fixed at /64\n"
" -i maximum idle time before shutting down\n\n" " -i maximum idle time before shutting down\n\n"
"tunnel_ip is the IP number of the local tunnel interface.\n" "tunnel_ip is the IP number of the local tunnel interface.\n"
" /netmask sets the size of the tunnel network.\n" " /netmask sets the size of the tunnel network.\n"
@ -2405,7 +2438,6 @@ main(int argc, char **argv)
int dns4addr_len; int dns4addr_len;
struct sockaddr_storage dns6addr; struct sockaddr_storage dns6addr;
int dns6addr_len; int dns6addr_len;
int forward_v6;
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
int nb_fds; int nb_fds;
#endif #endif
@ -2434,7 +2466,6 @@ main(int argc, char **argv)
debug = 0; debug = 0;
netmask = 27; netmask = 27;
pidfile = NULL; pidfile = NULL;
forward_v6 = 0;
retval = 0; retval = 0;
#ifdef WINDOWS32 #ifdef WINDOWS32
@ -2452,7 +2483,8 @@ main(int argc, char **argv)
srand(time(NULL)); srand(time(NULL));
fw_query_init(); fw_query_init();
while ((choice = getopt(argc, argv, "t:d:m:l:L:p:n:b:P:z:F:i:46vcsSfhDu")) != -1) { while ((choice = getopt(argc, argv, "46vcsfhDuS:t:d:m:l:L:p:n:b:P:z:F:i:")) != -1) {
switch(choice) { switch(choice) {
case '4': case '4':
addrfamily = AF_INET; addrfamily = AF_INET;
@ -2524,7 +2556,7 @@ main(int argc, char **argv)
memset(optarg, 0, strlen(optarg)); memset(optarg, 0, strlen(optarg));
break; break;
case 'S': case 'S':
forward_v6 = 1; display_ip6_buffer = optarg;
break; break;
case 'z': case 'z':
context = optarg; context = optarg;
@ -2553,10 +2585,28 @@ main(int argc, char **argv)
my_ip = inet_addr(argv[0]); my_ip = inet_addr(argv[0]);
if (my_ip == INADDR_NONE) { if (my_ip == INADDR_NONE) {
warnx("Bad IP address to use inside tunnel."); warnx("Bad IP address to use inside tunnel.");
usage(); usage();
} }
ip6_netmask_buffer = strchr(display_ip6_buffer, '/');
if (ip6_netmask_buffer) {
if (atoi(ip6_netmask_buffer+1) != ip6_netmask) {
warnx("IPv6 address must be a 64-bit mask.");
usage();
}
/* remove masklen */
memcpy(display_ip6, display_ip6_buffer, strlen(display_ip6_buffer) - strlen(ip6_netmask_buffer));
display_ip6[strlen(display_ip6)+1] = '\0';
}
/* IPV6 address sanity check */
if (inet_pton(AF_INET6, display_ip6, &my_ip6) != 1) {
warnx("Bad IPv6 address to use inside tunnel.");
usage();
}
topdomain = strdup(argv[1]); topdomain = strdup(argv[1]);
if (check_topdomain(topdomain, 1, &errormsg)) { if (check_topdomain(topdomain, 1, &errormsg)) {
warnx("Invalid topdomain: %s", errormsg); warnx("Invalid topdomain: %s", errormsg);
@ -2685,7 +2735,7 @@ main(int argc, char **argv)
dns_fds.v4fd = -1; dns_fds.v4fd = -1;
dns_fds.v6fd = -1; dns_fds.v6fd = -1;
created_users = init_users(my_ip, netmask); created_users = init_users(my_ip, netmask, my_ip6, ip6_netmask);
if ((tun_fd = open_tun(device)) == -1) { if ((tun_fd = open_tun(device)) == -1) {
/* nothing to clean up, just return */ /* nothing to clean up, just return */
@ -2693,16 +2743,26 @@ main(int argc, char **argv)
} }
if (!skipipconfig) { if (!skipipconfig) {
const char *other_ip = users_get_first_ip(); const char *other_ip = users_get_first_ip();
if (tun_setip(argv[0], other_ip, netmask, forward_v6) != 0 || tun_setmtu(mtu) != 0) { const char *display_other_ip6 = users_get_first_ip6();
if (tun_setip(argv[0], other_ip, netmask) != 0 || tun_setmtu(mtu) != 0) {
retval = 1; retval = 1;
free((void*) other_ip); free((void*) other_ip);
goto cleanup; goto cleanup;
} }
if ((mtu < 1280) && (forward_v6)) {
warnx("Interface mtu of %d below the 1280 threshold needed for IPv6 tunneling.\n", mtu); if (tun_setip6(display_ip6, display_other_ip6, ip6_netmask) != 0 ) {
warnx("Proceeding without IPv6 tunneling\n"); retval = 1;
} goto cleanup;
}
if ((mtu < 1280) && (sizeof(display_ip6)) != 0) {
warnx("Interface mtu of %d below the 1280 threshold needed for IPv6 tunneling.\n", mtu);
warnx("Proceeding without IPv6 tunneling\n");
}
free((void*) other_ip); free((void*) other_ip);
} }

205
src/tun.c
View File

@ -35,6 +35,11 @@
#include <netinet/ip.h> #include <netinet/ip.h>
#endif #endif
#if defined FREEBSD || defined NETBSD
#include <sys/ioctl.h>
#include <net/if_tun.h>
#endif
#ifndef IFCONFIGPATH #ifndef IFCONFIGPATH
#define IFCONFIGPATH "PATH=/sbin:/bin " #define IFCONFIGPATH "PATH=/sbin:/bin "
#endif #endif
@ -82,6 +87,7 @@ static char if_name[250];
#include <net/if.h> #include <net/if.h>
#include <linux/if_tun.h> #include <linux/if_tun.h>
int int
open_tun(const char *tun_device) open_tun(const char *tun_device)
{ {
@ -452,7 +458,15 @@ open_tun(const char *tun_device)
snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i); snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i);
if ((tun_fd = open(tun_name, O_RDWR)) >= 0) { if ((tun_fd = open(tun_name, O_RDWR)) >= 0) {
fprintf(stderr, "Opened %s\n", tun_name); #if defined FREEBSD || defined NETBSD
/* FreeBSD requires a packet header for
* IPv6 traffic
*/
if (ioctl(tun_fd, TUNSIFHEAD, &(int){1}) != 0) {
fprintf(stderr, "Not able to enable TUNSIFHEAD\n");
break;
}
#endif /* LINUX */
snprintf(if_name, sizeof(if_name), "tun%d", i); snprintf(if_name, sizeof(if_name), "tun%d", i);
fd_set_close_on_exec(tun_fd); fd_set_close_on_exec(tun_fd);
return tun_fd; return tun_fd;
@ -530,9 +544,12 @@ read_tun(int tun_fd, char *buf, size_t len)
static int static int
tun_uses_header(void) tun_uses_header(void)
{ {
#if defined (FREEBSD) || defined (NETBSD) #if defined FREEBSD || defined NETBSD || defined OPENBSD
/* FreeBSD/NetBSD has no header */ /* To enable IPv6 in FreeBSD tunnels, tunnel
return 0; * headers now enabled for that platform
*/
return 1;
#elif defined (DARWIN) #elif defined (DARWIN)
/* Darwin tun has no header, Darwin utun does */ /* Darwin tun has no header, Darwin utun does */
return !strncmp(if_name, "utun", 4); return !strncmp(if_name, "utun", 4);
@ -544,41 +561,82 @@ tun_uses_header(void)
int int
write_tun(int tun_fd, char *data, size_t len) write_tun(int tun_fd, char *data, size_t len)
{ {
int ip_version = 0;
if (!tun_uses_header()) { if (!tun_uses_header()) {
data += 4; data += 4;
len -= 4; len -= 4;
} else { } else {
int i = data[4] & 0xf0;
ip_version = get_ipversion(data[4]);
if (ip_version < 0) {
return 1; /* Cannot read IP version number from packet */
}
#ifdef LINUX #ifdef LINUX
if (i == 64) { if (ip_version == 4) {
// Look at the fifth bype
// Linux prefixes with 32 bits ethertype // Linux prefixes with 32 bits ethertype
// 0x0800 for IPv4, 0x86DD for IPv6 // 0x0800 for IPv4, 0x86DD for IPv6
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
data[2] = 0x08; data[2] = 0x08;
data[3] = 0x00; data[3] = 0x00;
} else { /* 96 for IPV6 */ } else { /* IPV6 */
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
data[2] = 0x86; data[2] = 0x86;
data[3] = 0xDD; data[3] = 0xDD;
} }
#else /* OPENBSD and DARWIN(utun) */ #elif defined (FREEBSD) || defined (OPENBSD)
// BSDs prefix with 32 bits address family // BSDs prefix with 32 bits address family
// AF_INET for IPv4, AF_INET6 for IPv6 // AF_INET for IPv4, AF_INET6 for IPv6
if (i == 64) { if (ip_version == 4) {
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
data[2] = 0x00; data[2] = 0x00;
data[3] = 0x02; data[3] = 0x02;
} else { /* 96 for IPV6 */ } else { /* IPV6 */
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x1C;
}
#elif defined NETBSD
// BSDs prefix with 32 bits address family
// AF_INET for IPv4, AF_INET6 for IPv6
if (ip_version == 4) {
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x02;
} else { /* IPV6 */
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x18;
}
#else /* DARWIN(utun) and all others */
// BSDs prefix with 32 bits address family
// AF_INET for IPv4, AF_INET6 for IPv6
if (ip_version == 4) {
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x02;
} else { /* IPV6 */
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
data[2] = 0x00; data[2] = 0x00;
data[3] = 0x1E; data[3] = 0x1E;
} }
#endif #endif
} }
@ -610,14 +668,12 @@ read_tun(int tun_fd, char *buf, size_t len)
#endif #endif
int int
tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6) tun_setip(const char *ip, const char *other_ip, int netbits)
{ {
char cmdline[512]; char cmdline[512];
char v6_cmdline[512];
int netmask; int netmask;
struct in_addr net; struct in_addr net;
int i; int i;
int v6_r;
#ifndef LINUX #ifndef LINUX
int r; int r;
#endif #endif
@ -650,20 +706,7 @@ tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6)
# else # else
display_ip = ip; display_ip = ip;
# endif # endif
if (forward_v6) {
fprintf(stderr, "Setting IPv6 of %s to ::%s\n", if_name, ip);
snprintf(v6_cmdline, sizeof(cmdline),
IFCONFIGPATH "ifconfig %s inet6 add ::%s/64",
if_name,
display_ip);
v6_r = system(v6_cmdline);
if (v6_r != 0) {
return v6_r;
}
}
snprintf(cmdline, sizeof(cmdline), snprintf(cmdline, sizeof(cmdline),
IFCONFIGPATH "ifconfig %s %s %s netmask %s", IFCONFIGPATH "ifconfig %s %s %s netmask %s",
if_name, if_name,
@ -722,14 +765,89 @@ tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6)
return system(cmdline); return system(cmdline);
#endif #endif
if (forward_v6) { }
snprintf(cmdline, sizeof(cmdline),
IFCONFIGPATH "ifconfig %s inet6 add ::%s/64",
if_name,
ip);
fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip); int
tun_setip6(char *display_ip6, const char *display_other_ip6, int netbits6)
{
int v6_r;
struct in6_addr ip6;
char v6_cmdline[512];
if (inet_pton(AF_INET6, display_ip6, &ip6) < 1){
warnx("Error in IPv6 address");
} }
#ifdef WINDOWS32
/*
DWORD status;
DWORD ipdata[3];
struct in_addr addr;
DWORD len;
*/
#endif
if (netbits6 > 0) {
fprintf(stderr, "Setting IPv6 of %s to %s\n", if_name, display_ip6);
#if defined LINUX
snprintf(v6_cmdline, sizeof(v6_cmdline),
IFCONFIGPATH "ifconfig %s inet6 add %s/%d",
if_name,
display_ip6, netbits6);
#else
snprintf(v6_cmdline, sizeof(v6_cmdline),
IFCONFIGPATH "ifconfig %s inet6 %s/%d",
if_name,
display_ip6, netbits6);
#endif
v6_r = system(v6_cmdline);
if (v6_r != 0) {
return v6_r;
} else {
return 0;
}
}
#ifdef WINDOWS32 /* WINDOWS32 */
/* Set device as connected */
fprintf(stderr, "Enabling interface '%s'\n", if_name);
status = 1;
r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status,
sizeof(status), &status, sizeof(status), &len, NULL);
if (!r) {
fprintf(stderr, "Failed to enable interface\n");
return -1;
}
if (inet_aton(ip, &addr)) {
ipdata[0] = (DWORD) addr.s_addr; /* local ip addr */
ipdata[1] = net.s_addr & ipdata[0]; /* network addr */
ipdata[2] = (DWORD) net.s_addr; /* netmask */
} else {
return -1;
}
/* Tell ip/networkaddr/netmask to device for arp use */
r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata,
sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL);
if (!r) {
fprintf(stderr, "Failed to set interface in TUN mode\n");
return -1;
}
/* use netsh to set ip address */
fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip);
snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s",
if_name, ip, inet_ntoa(net));
return system(cmdline);
#endif
return 0;
} }
int int
@ -757,3 +875,18 @@ tun_setmtu(const unsigned mtu)
#endif #endif
} }
int get_ipversion(char first_byte)
{
int v;
v = first_byte & 0xf0;
if (v == 64) {
return 4;
} else if (v == 96) {
return 6;
} else {
return -1;
}
}

View File

@ -22,7 +22,9 @@ int open_tun(const char *);
void close_tun(int); void close_tun(int);
int write_tun(int, char *, size_t); int write_tun(int, char *, size_t);
ssize_t read_tun(int, char *, size_t); ssize_t read_tun(int, char *, size_t);
int tun_setip(const char *, const char *, int, int); int tun_setip(const char *, const char *, int);
int tun_setip6(char *, const char *, int);
int tun_setmtu(const unsigned); int tun_setmtu(const unsigned);
int get_ipversion(char);
#endif /* _TUN_H_ */ #endif /* _TUN_H_ */

View File

@ -23,6 +23,7 @@
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <math.h>
#ifdef WINDOWS32 #ifdef WINDOWS32
#include <winsock2.h> #include <winsock2.h>
@ -37,25 +38,26 @@
struct tun_user *users; struct tun_user *users;
unsigned usercount; unsigned usercount;
int init_users(in_addr_t my_ip, int netbits) int init_users(in_addr_t my_ip, int netbits, struct in6_addr my_ip6, int netbits6)
{ {
int i; int i;
int i6;
int skip = 0; int skip = 0;
int skip6 = 0;
char newip[16]; char newip[16];
char ip6Tmp[16];
char ip6Tmp2[18];
int maxusers; int maxusers;
in_addr_t netmask = 0; in_addr_t netmask = 0;
struct in_addr net; struct in_addr net;
struct in_addr ipstart; struct in_addr ipstart;
struct in6_addr ip6start;
/* For IPv6, we take the IPv4 address and simply prepend :: unsigned ip6_netmask[16];
* and use a 64-bit mask. Reduces the need to parse
* netmasks.
*/
bool ip6_enabled;
ip6_enabled = isV6AddrSet(&my_ip6);
for (i = 0; i < netbits; i++) { for (i = 0; i < netbits; i++) {
netmask = (netmask << 1) | 1; netmask = (netmask << 1) | 1;
} }
@ -63,17 +65,39 @@ int init_users(in_addr_t my_ip, int netbits)
net.s_addr = htonl(netmask); net.s_addr = htonl(netmask);
ipstart.s_addr = my_ip & net.s_addr; ipstart.s_addr = my_ip & net.s_addr;
/* Covert IPv6 netbits to IPv6 netmask and work
* out the network address from my IP address. Start
* assigning IPv6 address from the network address + 1
*/
if (ip6_enabled == true) {
for (i6 = 0; i6 < netbits6 / 8; i6++) {
ip6_netmask[i6] |= 0xFF;
}
ip6_netmask[netbits6 / 8] = pow(2, (netbits6 % 8 )) - 1;
ip6_netmask[netbits6 / 8] <<= (8-(netbits6 % 8));
for (i6 = 0; i6 < 16; i6++) {
ip6start.s6_addr[i6] = my_ip6.s6_addr[i6] & ip6_netmask[i6];
}
}
maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */ maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */
usercount = MIN(maxusers, USERS); usercount = MIN(maxusers, USERS);
users = calloc(usercount, sizeof(struct tun_user)); users = calloc(usercount, sizeof(struct tun_user));
/*
* IPv6 note: Current behavior is to populate the users structure
* with the IPv4 addresses that are expected to be used.
* In the future with IPv6-only tunnel transport, we should not be
* populating a /64 (or whatever mask) in the users structure
* and should shift to an on-demand scheme. For now
* we expect dual-stack and pre-allocate IPv6 addresses into the
* users struct as we do with IPv4.
*/
for (i = 0; i < usercount; i++) { for (i = 0; i < usercount; i++) {
in_addr_t ip; in_addr_t ip;
users[i].id = i; users[i].id = i;
memset(ip6Tmp,0,strlen(ip6Tmp));
memset(ip6Tmp2,0,strlen(ip6Tmp2));
snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1);
ip = ipstart.s_addr + inet_addr(newip); ip = ipstart.s_addr + inet_addr(newip);
@ -82,15 +106,36 @@ int init_users(in_addr_t my_ip, int netbits)
skip++; skip++;
snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1);
ip = ipstart.s_addr + inet_addr(newip); ip = ipstart.s_addr + inet_addr(newip);
} }
users[i].tun_ip = ip; users[i].tun_ip = ip;
inet_ntop(AF_INET, &ip, ip6Tmp, INET_ADDRSTRLEN); if (ip6_enabled == true) {
snprintf(ip6Tmp2, sizeof(ip6Tmp2), "::%s", ip6Tmp); struct in6_addr temp_ip6;
inet_pton(AF_INET6, ip6Tmp2, &users[i].tun_ip6); /*
* start assigning host addresses from the network address + 1
* unless that is my_ip, in which case, use the following address.
*/
memcpy(temp_ip6.s6_addr, ip6start.s6_addr, sizeof(ip6start.s6_addr));
temp_ip6.s6_addr[15] = ip6start.s6_addr[15] + skip + 1 + i;
if (v6AddressesEqual(&temp_ip6, &my_ip6) == true &&
skip6 == 0) {
/* This IPv6 was taken by iodined */
skip6++;
/*
* We expect to start assigning addresses at the network address + 1 and
* to not worry about assigning more than 254 host addresses. If we did, we have to
* iterate through lower order bytes of ip6. This plus a few other corner cases
* is why we enourage/force/assume the user to specify a /64 V6 address
*/
temp_ip6.s6_addr[15] = ip6start.s6_addr[15] + skip + 1 + i;
}
memcpy(users[i].tun_ip6.s6_addr, temp_ip6.s6_addr, sizeof(temp_ip6.s6_addr));
}
net.s_addr = ip; net.s_addr = ip;
users[i].disabled = 0; users[i].disabled = 0;
users[i].authenticated = 0; users[i].authenticated = 0;
@ -110,6 +155,18 @@ const char *users_get_first_ip(void)
return strdup(inet_ntoa(ip)); return strdup(inet_ntoa(ip));
} }
const char *users_get_first_ip6(void)
{
struct in6_addr ip6;
char display_ip6[INET6_ADDRSTRLEN];
memcpy(&ip6, &users[0].tun_ip6, sizeof(struct in6_addr));
inet_ntop(AF_INET6, &ip6, display_ip6, INET6_ADDRSTRLEN);
return strdup(display_ip6);
}
int find_user_by_ip6(struct in6_addr *v6Addr) int find_user_by_ip6(struct in6_addr *v6Addr)
{ {
int i; int i;
@ -122,23 +179,23 @@ int find_user_by_ip6(struct in6_addr *v6Addr)
users[i].authenticated && users[i].authenticated &&
!users[i].disabled && !users[i].disabled &&
users[i].last_pkt + 60 > time(NULL) && users[i].last_pkt + 60 > time(NULL) &&
(areV6AddressesEqual(v6Addr, &users[i].tun_ip6) == 0) ) { v6AddressesEqual(v6Addr, &users[i].tun_ip6) == true) {
return i; return i;
} }
} }
return -1; return -1;
} }
int areV6AddressesEqual(struct in6_addr *v6Struct1, struct in6_addr *v6Struct2) bool v6AddressesEqual(struct in6_addr *v6Struct1, struct in6_addr *v6Struct2)
{ {
int i; int i;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
if (v6Struct1->s6_addr[i] != v6Struct2->s6_addr[i]) { if (v6Struct1->s6_addr[i] != v6Struct2->s6_addr[i]) {
return -1; return false;
} }
} }
return 0; return true;
} }

View File

@ -81,14 +81,15 @@ struct tun_user {
extern struct tun_user *users; extern struct tun_user *users;
int init_users(in_addr_t, int); int init_users(in_addr_t, int, struct in6_addr, int);
const char* users_get_first_ip(void); const char* users_get_first_ip(void);
const char* users_get_first_ip6(void);
int find_user_by_ip(uint32_t); int find_user_by_ip(uint32_t);
int find_user_by_ip6(struct in6_addr *v6Addr); int find_user_by_ip6(struct in6_addr *v6Addr);
int all_users_waiting_to_send(void); int all_users_waiting_to_send(void);
int find_available_user(void); int find_available_user(void);
void user_switch_codec(int userid, const struct encoder *enc); void user_switch_codec(int userid, const struct encoder *enc);
void user_set_conn_type(int userid, enum connection c); void user_set_conn_type(int userid, enum connection c);
int areV6AddressesEqual(struct in6_addr *, struct in6_addr *); bool v6AddressesEqual(struct in6_addr *, struct in6_addr *);
#endif #endif