3 ### Script to set up an iodine tunnel route traffic through it
5 ### Copyright 2008 Barak A. Pearlmutter <bap@debian.org>
9 ### Permission to use, copy, modify, and distribute this software for
10 ### any purpose with or without fee is hereby granted, provided that
11 ### the above copyright notice and this permission notice appear in
14 ### THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
15 ### WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
16 ### WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
17 ### AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
18 ### CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19 ### LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 ### NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 ### CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 ## Cause script to bail immediately on failed command
26 ## Options for user to set.
28 ## Minimal customization: put the two lines
29 ## subdomain=your.tunnel.sub.domain
30 ## passed=password_for_that_tunnel
31 ## in the file /etc/default/iodine-client.
33 echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
35 if [ -r ${iodine_client_rc} ]; then
38 echo WARNING: Cannot read ${iodine_client_rc}
41 if [ -z ${subdomain} ]; then
42 read -p "DNS tunnel DNS subdomain: " subdomain
45 if [ -z ${subdomain} ]; then
46 echo ERROR: Must set subdomain.
50 if [ -z ${passwd} ]; then
51 read -p "Password for DNS tunnel over ${subdomain}: " passwd
54 ## This is a host name used for testing DNS and for pinging
55 echo "${testhost:=slashdot.org}" > /dev/null
57 ## Set if local network should be taken down and then up
58 echo "${bounce_localnet:=true}" > /dev/null
60 ## Set for testing network availability via ping at various points
61 echo "${test_ping_localnet:=true}" > /dev/null
62 echo "${test_ping_tunnel:=true}" > /dev/null
63 echo "${test_ping_final:=true}" > /dev/null
65 ## Set if the script cannot find and then incorrectly guesses the
66 ## local network router
67 echo "${default_router}" > /dev/null
69 ## Set if script uses the wrong hardware interface
70 echo "{interface}" > /dev/null
72 ## Set if the script should continue even if a command fails.
73 ## Used to test script when running as non-root.
74 if [ $(whoami) = root ]; then
75 echo "${continue_on_error:=false}" > /dev/null
77 echo "${continue_on_error:=true}" > /dev/null
80 ## DEBIAN PACKAGES TO INSTALL: these are needed to run this script
81 ## iodine (for /usr/sbin/iodine)
82 ## iproute (for /bin/ip)
83 ## ipcalc (for /usr/bin/ipcalc)
84 ## dnsutils (for /usr/bin/dig)
85 ## fping (for /usr/bin/fping)
87 ## The default tunnel MTU is 1024.
88 ## If local DNS server restricts to 512 byte packets then do this:
89 # ifconfig ${d} mtu 220
92 ## - avoid double ping when DNS server and local router are the same
93 ## - option to not kill existing iodine DNS tunnels, in case there
94 ## are meant to be more than one
95 ## - sanify check whether default_router is on local network
97 echo ==== Creating IP-over-DNS tunnel over local network connection...
100 ## Find a network interface
102 if [ -z ${interface} ]; then
103 interface=$(tail --lines=+3 /proc/net/wireless \
104 | head -1 | tr -d : | awk '{print $1}')
107 if [ -z ${interface} ]; then
108 interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \
109 | head -1 | awk '{print $1}')
112 if [ -z ${interface} ]; then
113 echo ERROR: No network interface found.
117 echo ==== Local network interface: ${interface}
119 ## Down any existing DNS tunnel (wish there were "approved" way to do this)
121 echo ==== Killing existing DNS tunnels...
122 if killall --quiet --wait --verbose --signal HUP iodine; then
126 ## Stabilize local network
128 if ${bounce_localnet}; then
129 echo ==== Bouncing local network connection...
130 ifdown --force ${interface} || true
131 ifup ${interface} || ${continue_on_error}
134 ## Fetch some information about the local network
136 addr=$(ip -4 addr show dev ${interface} scope global \
137 | tail -1 | awk '{print $2}')
138 prefix_len=$(echo ${addr} | sed 'sX^.*/XX')
139 local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}')
141 echo ==== Local address: ${addr}
142 echo ==== Local network: ${local_net}
144 router=$(ip -4 route list dev ${interface} \
145 | awk '$1=="default" {print $3}' | head -1)
146 if [ -z ${router} ]; then
147 ## This can happen when the default local route is already deleted
148 if [ -z ${default_router} ]; then
149 echo WARNING: no default route, guessing local router IP address.
150 ## Minimum address on local net is usually right
151 router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}')
153 echo WARNING: no default route, using configured default router.
154 ## But sometimes need to hardwire...
155 router=${default_router}
159 echo ==== Local network router: ${router}
163 testhost_ip=$(dig +short -t A -q ${testhost})
164 if [ -z ${testhost_ip} ]; then
165 echo WARNING: Failure on DNS lookup of ${testhost}.
170 nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf)
171 if [ -n "${nameservers}" ]; then
172 echo ==== DNS servers: ${nameservers}
174 echo ERROR: No DNS servers found.
178 ## Test if local network is up
180 if ${test_ping_localnet}; then
181 echo ==== Ping test of local network router and DNS servers...
182 fping -C1 ${router} ${nameservers} \
183 || echo WARNING: Ping test failed.
186 ## Add point-to-point routes for any non-local DNS servers
188 for n in ${nameservers}; do
189 n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}')
190 if [ "${n_net}" != "${local_net}" ]; then
191 echo ==== Adding point-to-point route for DNS server ${n}
192 ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
196 ## Bring up DNS tunnel
198 echo ==== Creating IP-over-DNS tunnel...
199 iodine -P ${passwd} ${subdomain} || ${continue_on_error}
201 ## Find DNS tunnel interface
203 tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1)
204 if [ -z "${tunnel_interface}" ]; then
205 echo WARNING: Cannot find DNS tunnel interface, using default.
206 tunnel_interface=dns0
208 echo ==== DNS tunnel interface: ${tunnel_interface}
210 ## Figure out router at other end of tunnel, assuming router uses final octet .1
211 ## (There should be some way to get this information out of iodine, since
212 ## it *prints* it as it sets up the tunnel, so it does know it.)
214 tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \
215 | awk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1)
217 if [ -z ${tunnel_remote} ]; then
218 echo ERROR: Cannot find DNS tunnel remote endpoint.
220 ## set something random if debugging
221 echo WARNING: Confabulating DNS tunnel remote endpoint.
222 tunnel_remote=192.168.253.1
225 echo ==== DNS tunnel remote endpoint: ${tunnel_remote}
227 if ${test_ping_tunnel}; then
228 echo ==== Ping test of local router, nameserver, and DNS tunnel...
229 fping -C1 ${router} ${nameservers} ${tunnel_remote} \
230 || echo WARNING: Ping test failed.
233 ## Modify routing table to send trafic via DNS tunnel
235 echo ==== Setting default route through DNS tunnel...
237 ## Remove default route via local router
238 ip -4 route del default via ${router} || ${continue_on_error}
239 ## Add default via tunnel
240 ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
242 ## Test if all is well
244 if ${test_ping_final}; then
245 echo ==== Ping test of local router, nameserver, DNS tunnel, external test host...
246 fping -C1 ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
247 || echo WARNING: Ping test failed.