IPv6 support for DNS traffic in server

Server will by default listen on both IPv4 and IPv6.
No way to only listen on one protocol right now.

Use -L to only listen on a specific v6 address.

IP address to use for raw mode is still IPv4 only.
Use -n on server to make raw mode work from IPv6 clients,
then they will get an IPv4 address from the server for raw mode.

Tunnel data is still IPv4.
This commit is contained in:
Erik Ekman 2015-06-28 20:01:48 +02:00
parent 07c2fd4068
commit 7a117bd71e
5 changed files with 92 additions and 43 deletions

View File

@ -14,6 +14,8 @@ master:
- Linux: use pkg-config for systemd support flags. - Linux: use pkg-config for systemd support flags.
Patch by Jason A. Donenfeld. Patch by Jason A. Donenfeld.
- Change external IP webservice to ipify.org - Change external IP webservice to ipify.org
- Add support for IPv6 in the server. (Raw mode missing)
Traffic inside tunnel is still IPv4.
2014-06-16: 0.7.0 "Kryoptonite" 2014-06-16: 0.7.0 "Kryoptonite"
- Partial IPv6 support (#107) - Partial IPv6 support (#107)

View File

@ -121,14 +121,16 @@ end of the tunnel. In this case, `ping 192.168.99.1` from the iodine client, and
### MISC. INFO ### MISC. INFO
#### IPv6 #### IPv6
At the moment the iodined server only supports IPv4. The data inside the tunnel The data inside the tunnel is IPv4 only.
is IPv4 only.
The server listens to both IPv4 and IPv6 for incoming requests. Raw mode
currently only works for IPv4, or can use IPv4 from IPv6 login if -n option is
used.
The client can use IPv4 or IPv6 nameservers to connect to iodined. The relay The client can use IPv4 or IPv6 nameservers to connect to iodined. The relay
nameservers will translate between protocols automatically if needed. Use nameservers will translate between protocols automatically if needed. Use
options `-4` or `-6` to force the client to use a specific IP version for its DNS options `-4` or `-6` to force the client to use a specific IP version for its DNS
queries. The client has to force IPv4 if it has dual-stack connectivity and queries.
the hostname handling the tunnel domain has both `A` and `AAAA` records.
#### Routing #### Routing
It is possible to route all traffic through the DNS tunnel. To do this, first It is possible to route all traffic through the DNS tunnel. To do this, first

View File

@ -54,7 +54,9 @@ iodine, iodined \- tunnel IPv4 over DNS
.B ] [-m .B ] [-m
.I mtu .I mtu
.B ] [-l .B ] [-l
.I listen_ip .I listen_ip4
.B ] [-L
.I listen_ip6
.B ] [-p .B ] [-p
.I port .I port
.B ] [-n .B ] [-n
@ -81,9 +83,9 @@ iodine, iodined \- tunnel IPv4 over DNS
.I topdomain .I topdomain
.SH DESCRIPTION .SH DESCRIPTION
.B iodine .B iodine
lets you tunnel IPv4 data through a DNS lets you tunnel IPv4 data through a DNS
server. This can be useful in situations where Internet access is firewalled, server. This can be useful in situations where Internet access is firewalled,
but DNS queries are allowed. It needs a TUN/TAP device to operate. The but DNS queries are allowed. It needs a TUN/TAP device to operate. The
bandwidth is asymmetrical, bandwidth is asymmetrical,
with a measured maximum of 680 kbit/s upstream and 2.3 Mbit/s with a measured maximum of 680 kbit/s upstream and 2.3 Mbit/s
downstream in a wired LAN test network. downstream in a wired LAN test network.
@ -122,7 +124,7 @@ and otherwise tunX. On Mac OS X 10.6, this can also be utunX, which will attempt
to use an utun device built into the OS. to use an utun device built into the OS.
.TP .TP
.B -P password .B -P password
Use 'password' to authenticate. If not used, Use 'password' to authenticate. If not used,
.B stdin .B stdin
will be used as input. Only the first 32 characters will be used. will be used as input. Only the first 32 characters will be used.
.TP .TP
@ -245,7 +247,7 @@ rejected, however this will cause problems when requests are routed
via a cluster of DNS servers. via a cluster of DNS servers.
.TP .TP
.B -s .B -s
Don't try to configure IP address or MTU. Don't try to configure IP address or MTU.
This should only be used if you have already configured the device that will be This should only be used if you have already configured the device that will be
used. used.
.TP .TP
@ -261,18 +263,22 @@ This is easily done with : "LC_ALL=C luit iodined \-DD ..."
(see luit(1)). (see luit(1)).
.TP .TP
.B -m mtu .B -m mtu
Set 'mtu' as mtu size for the tun device. Set 'mtu' as mtu size for the tun device.
This will be sent to the client on login, and the client will use the same mtu This will be sent to the client on login, and the client will use the same mtu
for its tun device. Default 1130. Note that the DNS traffic will be for its tun device. Default 1130. Note that the DNS traffic will be
automatically fragmented when needed. automatically fragmented when needed.
.TP .TP
.B -l listen_ip .B -l listen_ip4
Make the server listen only on 'listen_ip' for incoming requests. Make the server listen only on 'listen_ip4' for incoming IPv4 requests.
By default, incoming requests are accepted from all interfaces. By default, incoming requests are accepted from all interfaces (0.0.0.0).
.TP
.B -L listen_ip6
Make the server listen only on 'listen_ip6' for incoming IPv6 requests.
By default, incoming requests are accepted from all interfaces (::)
.TP .TP
.B -p port .B -p port
Make the server listen on 'port' instead of 53 for traffic. Make the server listen on 'port' instead of 53 for traffic.
If 'listen_ip' does not include localhost, this 'port' can be the same If 'listen_ip4' does not include localhost, this 'port' can be the same
as 'dnsport'. as 'dnsport'.
.B Note: .B Note:
You must make sure the dns requests are forwarded to this port yourself. You must make sure the dns requests are forwarded to this port yourself.
@ -308,7 +314,7 @@ file.
.B topdomain .B topdomain
The dns traffic will be sent as queries for subdomains under The dns traffic will be sent as queries for subdomains under
\'topdomain'. This is normally a subdomain to a domain you own. Use a short \'topdomain'. This is normally a subdomain to a domain you own. Use a short
domain name to get better throughput. If domain name to get better throughput. If
.B nameserver .B nameserver
is the iodined server, then the topdomain can be chosen freely. This argument is the iodined server, then the topdomain can be chosen freely. This argument
must be the same on both the client and the server. must be the same on both the client and the server.
@ -316,15 +322,15 @@ must be the same on both the client and the server.
.TP .TP
.B tunnel_ip[/netmask] .B tunnel_ip[/netmask]
This is the server's ip address on the tun interface. The client will be This is the server's ip address on the tun interface. The client will be
given the next ip number in the range. It is recommended to use the given the next ip number in the range. It is recommended to use the
10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overridden 10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overridden
by specifying it here. Using a smaller network will limit the number of by specifying it here. Using a smaller network will limit the number of
concurrent users. concurrent users.
.TP .TP
.B topdomain .B topdomain
The dns traffic is expected to arrive as queries for The dns traffic is expected to arrive as queries for
subdomains under 'topdomain'. This is normally a subdomain to a domain you subdomains under 'topdomain'. This is normally a subdomain to a domain you
own. Use a short domain name to get better throughput. This argument must be own. Use a short domain name to get better throughput. This argument must be
the same on both the client and the server. Queries for domains other the same on both the client and the server. Queries for domains other
than 'topdomain' will be forwarded when the \-b option is given, otherwise than 'topdomain' will be forwarded when the \-b option is given, otherwise
they will be dropped. they will be dropped.
@ -349,7 +355,7 @@ except to the used ssh or vpn ports.
If the environment variable If the environment variable
.B IODINE_PASS .B IODINE_PASS
is set, iodine will use the value it is set to as password instead of asking is set, iodine will use the value it is set to as password instead of asking
for one. The for one. The
.B -P .B -P
option still has precedence. option still has precedence.
.SS IODINED_PASS .SS IODINED_PASS

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>, * Copyright (c) 2006-2015 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se> * 2006-2009 Bjorn Andersson <flex@kryo.se>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
@ -97,7 +97,7 @@ struct query {
unsigned short id; unsigned short id;
struct in_addr destination; struct in_addr destination;
struct sockaddr_storage from; struct sockaddr_storage from;
int fromlen; socklen_t fromlen;
unsigned short id2; unsigned short id2;
struct sockaddr from2; struct sockaddr from2;
int fromlen2; int fromlen2;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>, * Copyright (c) 2006-2015 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se> * 2006-2009 Bjorn Andersson <flex@kryo.se>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
@ -225,6 +225,14 @@ check_user_and_ip(int userid, struct query *q)
received = (struct sockaddr_in *) &(q->from); received = (struct sockaddr_in *) &(q->from);
return memcmp(&(expected->sin_addr), &(received->sin_addr), sizeof(struct in_addr)); return memcmp(&(expected->sin_addr), &(received->sin_addr), sizeof(struct in_addr));
} }
/* Check IPv6 */
if (q->from.ss_family == AF_INET6) {
struct sockaddr_in6 *expected, *received;
expected = (struct sockaddr_in6 *) &(users[userid].host);
received = (struct sockaddr_in6 *) &(q->from);
return memcmp(&(expected->sin6_addr), &(received->sin6_addr), sizeof(struct in6_addr));
}
/* Unknown address family */ /* Unknown address family */
return 1; return 1;
} }
@ -1769,9 +1777,16 @@ tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
} }
FD_ZERO(&fds); FD_ZERO(&fds);
maxfd = 0;
FD_SET(dns_fds->v4fd, &fds); if (dns_fds->v4fd >= 0) {
maxfd = dns_fds->v4fd; FD_SET(dns_fds->v4fd, &fds);
maxfd = MAX(dns_fds->v4fd, maxfd);
}
if (dns_fds->v6fd >= 0) {
FD_SET(dns_fds->v6fd, &fds);
maxfd = MAX(dns_fds->v6fd, maxfd);
}
if (bind_fd) { if (bind_fd) {
/* wait for replies from real DNS */ /* wait for replies from real DNS */
@ -1815,6 +1830,9 @@ tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
if (FD_ISSET(dns_fds->v4fd, &fds)) { if (FD_ISSET(dns_fds->v4fd, &fds)) {
tunnel_dns(tun_fd, dns_fds->v4fd, dns_fds, bind_fd); tunnel_dns(tun_fd, dns_fds->v4fd, dns_fds, bind_fd);
} }
if (FD_ISSET(dns_fds->v6fd, &fds)) {
tunnel_dns(tun_fd, dns_fds->v6fd, dns_fds, bind_fd);
}
if (FD_ISSET(bind_fd, &fds)) { if (FD_ISSET(bind_fd, &fds)) {
tunnel_bind(bind_fd, dns_fds); tunnel_bind(bind_fd, dns_fds);
} }
@ -2015,7 +2033,7 @@ static int
read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q) read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q)
/* FIXME: dns_fds and tun_fd are because of raw_decode() below */ /* FIXME: dns_fds and tun_fd are because of raw_decode() below */
{ {
struct sockaddr_in from; struct sockaddr_storage from;
socklen_t addrlen; socklen_t addrlen;
char packet[64*1024]; char packet[64*1024];
int r; int r;
@ -2025,7 +2043,7 @@ read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q)
struct iovec iov; struct iovec iov;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
addrlen = sizeof(struct sockaddr); addrlen = sizeof(struct sockaddr_storage);
iov.iov_base = packet; iov.iov_base = packet;
iov.iov_len = sizeof(packet); iov.iov_len = sizeof(packet);
@ -2039,7 +2057,7 @@ read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q)
r = recvmsg(fd, &msg, 0); r = recvmsg(fd, &msg, 0);
#else #else
addrlen = sizeof(struct sockaddr); addrlen = sizeof(struct sockaddr_storage);
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
#endif /* !WINDOWS32 */ #endif /* !WINDOWS32 */
@ -2236,26 +2254,27 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc)
} }
static void static void
usage() { print_usage() {
extern char *__progname; extern char *__progname;
fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
"[-t chrootdir] [-d device] [-m mtu] [-z context] " "[-t chrootdir] [-d device] [-m mtu] [-z context] "
"[-l ip address to listen on] [-p port] [-n external ip] " "[-l ipv4 listen address] [-L ipv6 listen address] "
"[-b dnsport] [-P password] [-F pidfile] [-i max idle time] " "[-p port] [-n external ip] [-b dnsport] "
"[-P password] [-F pidfile] [-i max idle time] "
"tunnel_ip[/netmask] topdomain\n", __progname); "tunnel_ip[/netmask] topdomain\n", __progname);
}
static void
usage() {
print_usage();
exit(2); exit(2);
} }
static void static void
help() { help() {
extern char *__progname;
fprintf(stderr, "iodine IP over DNS tunneling server\n"); fprintf(stderr, "iodine IP over DNS tunneling server\n");
fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " print_usage();
"[-t chrootdir] [-d device] [-m mtu] [-z context] "
"[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password] "
"[-F pidfile] tunnel_ip[/netmask] topdomain\n", __progname);
fprintf(stderr, " -v to print version info and exit\n"); fprintf(stderr, " -v to print version info and exit\n");
fprintf(stderr, " -h to print this help and exit\n"); fprintf(stderr, " -h to print this help and exit\n");
fprintf(stderr, " -c to disable check of client IP/port on each request\n"); fprintf(stderr, " -c to disable check of client IP/port on each request\n");
@ -2269,8 +2288,10 @@ help() {
fprintf(stderr, " -d device to set tunnel device name\n"); fprintf(stderr, " -d device to set tunnel device name\n");
fprintf(stderr, " -m mtu to set tunnel device mtu\n"); fprintf(stderr, " -m mtu to set tunnel device mtu\n");
fprintf(stderr, " -z context to apply SELinux context after initialization\n"); fprintf(stderr, " -z context to apply SELinux context after initialization\n");
fprintf(stderr, " -l ip address to listen on for incoming dns traffic " fprintf(stderr, " -l IPv4 address to listen on for incoming dns traffic "
"(default 0.0.0.0)\n"); "(default 0.0.0.0)\n");
fprintf(stderr, " -L IPv6 address to listen on for incoming dns traffic "
"(default ::)\n");
fprintf(stderr, " -p port to listen on for incoming dns traffic (default 53)\n"); fprintf(stderr, " -p port to listen on for incoming dns traffic (default 53)\n");
fprintf(stderr, " -n ip to respond with to NS queries\n"); fprintf(stderr, " -n ip to respond with to NS queries\n");
fprintf(stderr, " -b port to forward normal DNS queries to (on localhost)\n"); fprintf(stderr, " -b port to forward normal DNS queries to (on localhost)\n");
@ -2295,6 +2316,7 @@ main(int argc, char **argv)
{ {
extern char *__progname; extern char *__progname;
char *listen_ip4; char *listen_ip4;
char *listen_ip6;
char *errormsg; char *errormsg;
#ifndef WINDOWS32 #ifndef WINDOWS32
struct passwd *pw; struct passwd *pw;
@ -2322,7 +2344,9 @@ main(int argc, char **argv)
int retval; int retval;
int max_idle_time = 0; int max_idle_time = 0;
struct sockaddr_storage dns4addr; struct sockaddr_storage dns4addr;
int dns4addr_len; socklen_t dns4addr_len;
struct sockaddr_storage dns6addr;
socklen_t dns6addr_len;
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
int nb_fds; int nb_fds;
#endif #endif
@ -2341,6 +2365,7 @@ main(int argc, char **argv)
mtu = 1130; /* Very many relays give fragsize 1150 or slightly mtu = 1130; /* Very many relays give fragsize 1150 or slightly
higher for NULL; tun/zlib adds ~17 bytes. */ higher for NULL; tun/zlib adds ~17 bytes. */
listen_ip4 = NULL; listen_ip4 = NULL;
listen_ip6 = NULL;
port = 53; port = 53;
ns_ip = INADDR_ANY; ns_ip = INADDR_ANY;
ns_get_externalip = 0; ns_get_externalip = 0;
@ -2373,7 +2398,7 @@ main(int argc, char **argv)
srand(time(NULL)); srand(time(NULL));
fw_query_init(); fw_query_init();
while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:n:b:P:z:F:i:")) != -1) { while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:L:p:n:b:P:z:F:i:")) != -1) {
switch(choice) { switch(choice) {
case 'v': case 'v':
version(); version();
@ -2408,6 +2433,9 @@ main(int argc, char **argv)
case 'l': case 'l':
listen_ip4 = optarg; listen_ip4 = optarg;
break; break;
case 'L':
listen_ip6 = optarg;
break;
case 'p': case 'p':
port = atoi(optarg); port = atoi(optarg);
break; break;
@ -2505,7 +2533,12 @@ main(int argc, char **argv)
dns4addr_len = get_addr(listen_ip4, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dns4addr); dns4addr_len = get_addr(listen_ip4, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dns4addr);
if (dns4addr_len < 0) { if (dns4addr_len < 0) {
warnx("Bad IP address to listen on."); warnx("Bad IPv4 address to listen on.");
usage();
}
dns6addr_len = get_addr(listen_ip6, port, AF_INET6, AI_PASSIVE | AI_NUMERICHOST, &dns6addr);
if (dns6addr_len < 0) {
warnx("Bad IPv6 address to listen on.");
usage(); usage();
} }
@ -2589,13 +2622,17 @@ main(int argc, char **argv)
retval = 1; retval = 1;
goto cleanup_tun; goto cleanup_tun;
} }
if ((dns_fds.v6fd = open_dns(&dns6addr, dns6addr_len)) < 0) {
retval = 1;
goto cleanup_dns;
}
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
} }
#endif #endif
if (bind_enable) { if (bind_enable) {
if ((bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) { if ((bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) {
retval = 1; retval = 1;
goto cleanup_dns4; goto cleanup_dns;
} }
} }
@ -2644,7 +2681,9 @@ main(int argc, char **argv)
syslog(LOG_INFO, "stopping"); syslog(LOG_INFO, "stopping");
close_dns(bind_fd); close_dns(bind_fd);
cleanup_dns4: cleanup_dns:
if (dns_fds.v6fd >= 0)
close_dns(dns_fds.v6fd);
if (dns_fds.v4fd >= 0) if (dns_fds.v4fd >= 0)
close_dns(dns_fds.v4fd); close_dns(dns_fds.v4fd);
cleanup_tun: cleanup_tun: