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.
Patch by Jason A. Donenfeld.
- 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"
- 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
#### IPv6
At the moment the iodined server only supports IPv4. The data inside the tunnel
is IPv4 only.
The data inside the tunnel 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
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
queries. The client has to force IPv4 if it has dual-stack connectivity and
the hostname handling the tunnel domain has both `A` and `AAAA` records.
queries.
#### Routing
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
.I mtu
.B ] [-l
.I listen_ip
.I listen_ip4
.B ] [-L
.I listen_ip6
.B ] [-p
.I port
.B ] [-n
@ -81,9 +83,9 @@ iodine, iodined \- tunnel IPv4 over DNS
.I topdomain
.SH DESCRIPTION
.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,
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,
with a measured maximum of 680 kbit/s upstream and 2.3 Mbit/s
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.
.TP
.B -P password
Use 'password' to authenticate. If not used,
Use 'password' to authenticate. If not used,
.B stdin
will be used as input. Only the first 32 characters will be used.
.TP
@ -245,7 +247,7 @@ rejected, however this will cause problems when requests are routed
via a cluster of DNS servers.
.TP
.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
used.
.TP
@ -261,18 +263,22 @@ This is easily done with : "LC_ALL=C luit iodined \-DD ..."
(see luit(1)).
.TP
.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
for its tun device. Default 1130. Note that the DNS traffic will be
automatically fragmented when needed.
.TP
.B -l listen_ip
Make the server listen only on 'listen_ip' for incoming requests.
By default, incoming requests are accepted from all interfaces.
.B -l listen_ip4
Make the server listen only on 'listen_ip4' for incoming IPv4 requests.
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
.B -p port
Make the server listen on 'port' instead of 53 for traffic.
If 'listen_ip' does not include localhost, this 'port' can be the same
Make the server listen on 'port' instead of 53 for traffic.
If 'listen_ip4' does not include localhost, this 'port' can be the same
as 'dnsport'.
.B Note:
You must make sure the dns requests are forwarded to this port yourself.
@ -308,7 +314,7 @@ file.
.B topdomain
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
domain name to get better throughput. If
domain name to get better throughput. If
.B nameserver
is the iodined server, then the topdomain can be chosen freely. This argument
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
.B tunnel_ip[/netmask]
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
by specifying it here. Using a smaller network will limit the number of
concurrent users.
.TP
.B topdomain
The dns traffic is expected to arrive as queries for
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
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
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
they will be dropped.
@ -349,7 +355,7 @@ except to the used ssh or vpn ports.
If the environment variable
.B IODINE_PASS
is set, iodine will use the value it is set to as password instead of asking
for one. The
for one. The
.B -P
option still has precedence.
.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>
*
* Permission to use, copy, modify, and/or distribute this software for any
@ -97,7 +97,7 @@ struct query {
unsigned short id;
struct in_addr destination;
struct sockaddr_storage from;
int fromlen;
socklen_t fromlen;
unsigned short id2;
struct sockaddr from2;
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>
*
* 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);
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 */
return 1;
}
@ -1769,9 +1777,16 @@ tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
}
FD_ZERO(&fds);
maxfd = 0;
FD_SET(dns_fds->v4fd, &fds);
maxfd = dns_fds->v4fd;
if (dns_fds->v4fd >= 0) {
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) {
/* 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)) {
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)) {
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)
/* FIXME: dns_fds and tun_fd are because of raw_decode() below */
{
struct sockaddr_in from;
struct sockaddr_storage from;
socklen_t addrlen;
char packet[64*1024];
int r;
@ -2025,7 +2043,7 @@ read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q)
struct iovec iov;
struct cmsghdr *cmsg;
addrlen = sizeof(struct sockaddr);
addrlen = sizeof(struct sockaddr_storage);
iov.iov_base = 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);
#else
addrlen = sizeof(struct sockaddr);
addrlen = sizeof(struct sockaddr_storage);
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
#endif /* !WINDOWS32 */
@ -2236,26 +2254,27 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc)
}
static void
usage() {
print_usage() {
extern char *__progname;
fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
"[-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] [-i max idle time] "
"[-l ipv4 listen address] [-L ipv6 listen address] "
"[-p port] [-n external ip] [-b dnsport] "
"[-P password] [-F pidfile] [-i max idle time] "
"tunnel_ip[/netmask] topdomain\n", __progname);
}
static void
usage() {
print_usage();
exit(2);
}
static void
help() {
extern char *__progname;
fprintf(stderr, "iodine IP over DNS tunneling server\n");
fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
"[-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);
print_usage();
fprintf(stderr, " -v to print version info 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");
@ -2269,8 +2288,10 @@ help() {
fprintf(stderr, " -d device to set tunnel device name\n");
fprintf(stderr, " -m mtu to set tunnel device mtu\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");
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, " -n ip to respond with to NS queries\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;
char *listen_ip4;
char *listen_ip6;
char *errormsg;
#ifndef WINDOWS32
struct passwd *pw;
@ -2322,7 +2344,9 @@ main(int argc, char **argv)
int retval;
int max_idle_time = 0;
struct sockaddr_storage dns4addr;
int dns4addr_len;
socklen_t dns4addr_len;
struct sockaddr_storage dns6addr;
socklen_t dns6addr_len;
#ifdef HAVE_SYSTEMD
int nb_fds;
#endif
@ -2341,6 +2365,7 @@ main(int argc, char **argv)
mtu = 1130; /* Very many relays give fragsize 1150 or slightly
higher for NULL; tun/zlib adds ~17 bytes. */
listen_ip4 = NULL;
listen_ip6 = NULL;
port = 53;
ns_ip = INADDR_ANY;
ns_get_externalip = 0;
@ -2373,7 +2398,7 @@ main(int argc, char **argv)
srand(time(NULL));
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) {
case 'v':
version();
@ -2408,6 +2433,9 @@ main(int argc, char **argv)
case 'l':
listen_ip4 = optarg;
break;
case 'L':
listen_ip6 = optarg;
break;
case 'p':
port = atoi(optarg);
break;
@ -2505,7 +2533,12 @@ main(int argc, char **argv)
dns4addr_len = get_addr(listen_ip4, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dns4addr);
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();
}
@ -2589,13 +2622,17 @@ main(int argc, char **argv)
retval = 1;
goto cleanup_tun;
}
if ((dns_fds.v6fd = open_dns(&dns6addr, dns6addr_len)) < 0) {
retval = 1;
goto cleanup_dns;
}
#ifdef HAVE_SYSTEMD
}
#endif
if (bind_enable) {
if ((bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) {
retval = 1;
goto cleanup_dns4;
goto cleanup_dns;
}
}
@ -2644,7 +2681,9 @@ main(int argc, char **argv)
syslog(LOG_INFO, "stopping");
close_dns(bind_fd);
cleanup_dns4:
cleanup_dns:
if (dns_fds.v6fd >= 0)
close_dns(dns_fds.v6fd);
if (dns_fds.v4fd >= 0)
close_dns(dns_fds.v4fd);
cleanup_tun: