Official git repo for iodine dns tunnel
Go to file
2009-09-20 21:10:41 +00:00
doc #76 start merging common and docs 2009-09-20 21:10:38 +00:00
man #75, update docs 2009-09-20 15:11:10 +00:00
src #76 merge server code 2009-09-20 21:10:41 +00:00
tests Fix segfault in test for BSDs 2009-09-20 15:57:00 +00:00
CHANGELOG Update changelog after #75 2009-09-20 16:05:37 +00:00
Makefile give error when no TAP adapters found, and clean better 2009-01-25 21:40:04 +00:00
README #76 start merging common and docs 2009-09-20 21:10:38 +00:00
README-win32.txt Updated readme 2009-06-14 20:23:50 +00:00
TODO Updated docs 2007-06-07 18:06:07 +00:00

iodine - http://code.kryo.se/iodine

***********************************

This is a piece of software that lets you tunnel IPv4 data through a DNS
server. This can be usable in different situations where internet access is
firewalled, but DNS queries are allowed.


QUICKSTART:

Try it out within your own LAN! Follow these simple steps:
- On your server, run: ./iodined -f 10.0.0.1 test.asdf
  (If you already use the 10.0.0.0 network, use another internal net like 
  172.16.0.0)
- Enter a password
- On the client, run: ./iodine -f 192.168.0.1 test.asdf
  (Replace 192.168.0.1 with the server's ip address)
- Enter the same password
- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1
- Try pinging each other through the tunnel
- Done! :)
To actually use it through a relaying nameserver, see below.


HOW TO USE:

Server side:
To use this tunnel, you need control over a real domain (like mytunnel.com),
and a server with a public IP number. If the server already runs a DNS
server, change the listening port and then use the -b option to let
iodined forward the DNS requests. Then, delegate a subdomain 
(say, tunnel1.mytunnel.com) to the server. If you use BIND for the domain, 
add these lines to the zone file:

tunnel1host	IN	A	10.15.213.99
tunnel1		IN	NS	tunnel1host.mytunnel.com.

Do not use CNAME instead of A above.
If your server has a dynamic IP, use a dynamic dns provider:

tunnel1         IN      NS      tunnel1host.mydyndnsprovider.com

Now any DNS querys for domains ending with tunnel1.mytunnnel.com will be sent
to your server. Start iodined on the server. The first argument is the tunnel
IP address (like 192.168.99.1) and the second is the assigned domain (in this
case tunnel1.mytunnel.com). The -f argument will keep iodined running in the
foreground, which helps when testing. iodined will start a virtual interface,
and also start listening for DNS queries on UDP port 53. Either enter a
password on the commandline (-P pass) or after the server has started. Now 
everything is ready for the client.

Client side: 
All the setup is done, just start iodine. It takes up to two arguments, the
first is the local relaying DNS server (optional) and the second is the domain
used (tunnel1.mytunnnel.com). If DNS queries are allowed to any computer, you
can use the tunnel endpoint (example: 10.15.213.99 or tunnel1host.mytunnel.com)
as the first argument. The tunnel interface will get an IP close to the servers
(in this case 192.168.99.2) and a suitable MTU.  Enter the same password as on
the server either by argument or after the client has started. Now you should
be able to ping the other end of the tunnel from either side.  


MISC. INFO:

Routing:
The normal case is to route all traffic through the DNS tunnel. To do this, first
add a route to the nameserver you use with the default gateway as gateway. Then
replace the default gateway with the servers IP address within the DNS tunnel,
and configure the server to do NAT.

The DNS-response fragment size is normally autoprobed to get maximum bandwidth.
To force a specific value (and speed things up), use the -m option.

The iodined server replies to NS requests sent for subdomains of the tunnel
domain. If your domain is tunnel.com, send a NS request for foo.tunnel.com
to see if the delegation works. dig is a good tool for this:
dig -t NS foo123.tunnel.com

The upstream data is sent gzipped encoded with Base32, or Base64 if the relay
server support '+' in domain names. DNS protocol allows one query per packet,
and one query can be max 256 chars. Each domain name part can be max 63 chars.
So your domain name and subdomain should be as short as possible to allow
maximum upstream throughput.

The default is to use DNS NULL-type queries, as this provides the largest
downstream bandwidth. If your DNS server blocks NULL requests, try TXT or
CNAME queries via the -T option. Also supported are A (returning CNAME) and
MX requests, but these may/will cause additional lookups by "smart" caching
nameservers to get an actual IP address, which may either slow down or fail
completely. DNS responses for non-NULL are Base32 encoded by default, which
should always work. For more bandwidth, try Base64 or Raw (TXT only) via the
-O option. If Base64/Raw doesn't work, you'll see many failures in the
fragment size autoprobe.

Normal operation now is for the server to _not_ answer a DNS request until
the next DNS request has come in, a.k.a. being "lazy". This way, the server
will always have a DNS request handy when new downstream data has to be sent.
This greatly improves (interactive) performance and latency, and allows to
slow down the quiescent ping requests to 4 second intervals by default.
In fact, the main purpose of the pings now is to force a reply to the previous
ping, and prevent DNS server timeouts (usually 5-10 seconds per RFC1035).
In the unlikely case that you do experience DNS server timeouts (SERVFAIL),
decrease the -I option to 1. If you are running on a local network without
any DNS server in-between, try -I 50 (iodine and iodined time out after 60
seconds). The only time you'll notice a slowdown, is when DNS reply packets
go missing; the iodined server then has to wait for a new ping to re-send the
data. You can speed this up by generating some upstream traffic (keypress,
ping). If this happens often, check your network for bottlenecks and/or run
with -I1 .

Some DNS servers appear to be quite impatient and start retrying DNS requests
(with _different_ DNS ids!) when an answer does not appear within a few
milliseconds. Usually they scale back retries when iodined's lazy mode
repeatedly takes several seconds to answer; and they scale up retries again
when iodined answers fast during heavy data transfer. Some commercial DNS
servers advertise this as "carrier-grade adaptive retransmission techniques".
The effect will only be visible in the network traffic at the iodined server,
and will not affect the client's connection. Iodined has rather elaborate
logic to deal with (i.e., ignore) these unwanted duplicates.

Other DNS servers, notably the opendns.com network, seem to regard iodined's
lazyness as incompetency, and will start shuffling requests around, possibly
in an attempt to reduce iodined's workload. The resulting out-of-sequence DNS
traffic works quite badly for lazy mode. The iodine client will detect this,
and switch back to legacy mode ("immediate ping-pong") automatically. In these
cases, start the iodine client with -L0 to prevent it from operating in lazy
mode altogether. Note that this will negatively affect interactive performance
and latency, especially in the downstream direction.

If you have problems, try inspecting the traffic with network monitoring tools
and make sure that the relaying DNS server has not cached the response. A
cached error message could mean that you started the client before the server.
The -D (and -DD) option on the server can also show received and sent queries.


TIPS & TRICKS:

If your port 53 is taken on a specific interface by an application that does 
not use it, use -p on iodined to specify an alternate port (like -p 5353) and 
use for instance iptables (on Linux) to forward the traffic:
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353
(Sent in by Tom Schouten)

Iodined will reject data from clients that have not been active (data/pings)
for more than 60 seconds. Similarly, iodine will exit when no downstream
data has been received for 60 seconds. In case of a long network outage or
similar, just restart iodine (re-login), possibly multiple times until you get
your old IP address back. Once that's done, just wait a while, and you'll
eventually see the tunneled TCP traffic continue to flow from where it left
off before the outage.

With the introduction of the downstream packet queue in the server, its memory
usage has increased with several megabytes in the default configuration.
For use in low-memory environments (e.g. running on your DSL router), you can
decrease USERS and undefine OUTPACKETQ_LEN in user.h without any ill conse-
quence, assuming at most one client will be connected at any time. A small
DNSCACHE_LEN is still advised, preferably 2 or higher, however you can also
undefine it to save a few more kilobytes.


PERFORMANCE:

This section tabulates some performance measurements. To view properly, use
a fixed-width font like Courier.

Measurements were done in protocol 00000500 with lazy mode unless indicated
otherwise. Upstream encoding always Base64.
Upstream/downstream throughput was measured by scp'ing a file previously
read from /dev/urandom (i.e. incompressible), and measuring size with
"ls -l ; sleep 30 ; ls -l" on a separate non-tunneled connection. Given the
large scp block size of 16 kB, this gives a resolution of 4.3 kbit/s, which
explains why many values are exactly equal.
Ping round-trip times measured with "ping -c100", presented are average rtt
and mean deviation (indicating spread around the average), in milliseconds.


Situation 1:
Laptop  ->   Wifi AP   ->  Home server  ->  DSL provider  ->  Datacenter
 iodine    DNS "relay"        bind9           DNS cache        iodined

                        downstr.  upstream downstr.  ping-up       ping-down
                        fragsize   kbit/s   kbit/s  avg +/-mdev   avg +/-mdev
------------------------------------------------------------------------------

iodine -> Wifi AP :53
  -Tnull (= -Oraw)           982    39.3    148.5   26.7    3.1   26.6    3.0

iodine -> Home server :53
  -Tnull (= -Oraw)          1174    43.6    174.7   25.2    4.0   25.5    3.4

iodine -> DSL provider :53  
  -Tnull (= -Oraw)          1174    52.4    200.9   20.3    3.2   20.3    2.7
  -Ttxt -Obase32             730    52.4    192.2*
  -Ttxt -Obase64             874    52.4    192.2
  -Ttxt -Oraw               1162    52.4    192.2
  -Tcname -Obase32           148    52.4     48.0
  -Tcname -Obase64           181    52.4     61.1

iodine -> DSL provider :53  
  wired (no Wifi) -Tnull    1174    65.5    244.6   17.7    1.9   17.8    1.6

 [192.2* : nice, because still 2frag/packet]


Situation 2:
Laptop -> (wire) -> (Home server) -> (DSL) ->  opendns.com ->  Datacenter
 iodine                                         DNS cache       iodined

                        downstr.  upstream downstr.  ping-up       ping-down
                        fragsize   kbit/s   kbit/s  avg +/-mdev   avg +/-mdev
------------------------------------------------------------------------------

iodine -> opendns.com :53
  -Tnull -L1 (lazy mode)     230      -        -   404.4  196.2  663.8  679.6
                                                    (20% lost)     (2% lost)

  -Tnull -L0 (legacy mode)   230     5.6      7.4  197.3    4.7  610.8  323.5

 [Note: Throughput measured over 300 seconds to get better resolution]


Situation 3:
Laptop  ->  Wifi+vpn / wired  ->  Home server
 iodine                            iodined

                        downstr.  upstream downstr.  ping-up       ping-down
                        fragsize   kbit/s   kbit/s  avg +/-mdev   avg +/-mdev
------------------------------------------------------------------------------

wifi + openvpn  -Tnull      1186   183.5    611.6    5.7    1.4    7.0    2.7

wired  -Tnull               1186   685.9   2350.5    1.3    0.1    1.4    0.4


Performance is strongly coupled to low ping times, as iodine requires
confirmation for every data fragment before moving on to the next. Allowing
multiple fragments in-flight like TCP could possibly increase performance,
but it would likely cause serious overload for the intermediary DNS servers.
The current protocol scales performance with DNS responsivity, since the
DNS servers are on average handling at most one DNS request per client.


PORTABILITY:

iodine has been tested on Linux (arm, ia64, x86, AMD64 and SPARC64), FreeBSD
(ia64, x86), OpenBSD (x86), NetBSD (x86), MacOS X (ppc and x86, with
http://tuntaposx.sourceforge.net/). and Windows (with OpenVPN TAP32 driver, see
win32 readme file).  It should be easy to port to other unix-like systems that
has TUN/TAP tunneling support. Let us know if you get it to run on other
platforms. 


THE NAME:

The name iodine was chosen since it starts with IOD (IP Over DNS) and since
iodine has atomic number 53, which happens to be the DNS port number.


THANKS:

- To kuxien for FreeBSD and OS X testing
- To poplix for code audit


AUTHORS & LICENSE:

Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>

Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.


MD5 implementation by L. Peter Deutsch (license and source in src/md5.[ch])
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.