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
29 'iodine-client-start' starts an iodine IP-over-DNS tunnel.
31 Usage: iodine-client-start [option]
33 -h, --help Print help and exit
34 -v, --version Print version info and exit
36 Invoking the program without options attempts to set up and configure
37 an iodine IP-over-DNS tunnel using the configuration in the file
38 /etc/default/iodine-client or by querying the user. It tries to
39 figure out the right way to set things up by observing the network,
40 and if all else fails by guessing.
44 Put two lines in the file /etc/default/iodine-client
46 subdomain=your.tunnel.sub.domain
48 passwd=password_for_that_tunnel
51 or invoke the script with those environment variables set:
53 env subdomain=xxx passwd=xxx iodine-client-start
55 If these are not set, the script will query the user for them.
59 The configuration file consists of lines which are either comments
60 starting with '#', or settings of the form VAR="val". Valid VARs are:
63 Sample value: your.tunnel.sub.domain (no default, must be set)
66 Sample value: password_for_that_tunnel (no default, must be set)
69 Hostname to ping when testing if network is working (default:
73 Take the local network down and then up again before starting
74 tunnel (default: false)
77 Test if the local network is working by pinging the gateway
81 Test if the iodine tunnel is working after it has been set up by
82 pinging the host at the other end (default: true)
85 Test if the tunnel is working after everything is ostensibly set
86 up by trying to ping an external host (default: true)
89 IP address of router on the local network---should be found
90 automatically, set this if that fails and the program guesses wrong.
93 Interface to use (e.g., eth1, eth0, etc) for connection to DNS
94 server used for the iodine tunnel---should be found automatically,
95 set this if that fails and the program guesses wrong.
98 Set if tunnel MTU needs to be manually changed (lowered). Should
99 not be necessary anymore, as recent versions of iodine negotiate
100 an appropriate MTU during tunnel setup. But if that negotiation
101 does not happen, or if you are using an older version of iodine,
102 the default tunnel MTU is 1024, and if the local DNS server
103 restricts to 512 byte packets you might need to use an MTU of 220.
106 Set "-r" option in iodine command line. With this option, iodine
107 does not try to establish a direct UDP socket to the iodine server
108 on port 53. (default: true).
111 Set if the script should continue even if a command fails.
112 Use to test script when running as non-root. Defaults to false
113 if running as root, true otherwise.
119 echo iodine-client-start 1.0.5
142 echo error: unknown option "$1"
148 echo error: too many arguments "$*"
153 echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
155 if [ -r ${iodine_client_rc} ]; then
156 . ${iodine_client_rc}
158 echo WARNING: Cannot read ${iodine_client_rc}
161 if [ -z ${subdomain} ]; then
162 read -p "DNS tunnel DNS subdomain: " subdomain
165 if [ -z ${subdomain} ]; then
166 echo ERROR: Must set subdomain.
170 if [ -z ${passwd} ]; then
171 read -p "Password for DNS tunnel over ${subdomain}: " passwd
174 ## This is a host name used for testing DNS and for pinging
175 echo "${testhost:=slashdot.org}" > /dev/null
177 ## Set if local network should be taken down and then up
178 echo "${bounce_localnet:=false}" > /dev/null
180 ## Set for testing network availability via ping at various points
181 echo "${test_ping_localnet:=true}" > /dev/null
182 echo "${test_ping_tunnel:=true}" > /dev/null
183 echo "${test_ping_final:=true}" > /dev/null
185 ## Set if the script cannot find and then incorrectly guesses the
186 ## local network router
187 echo "${default_router}" > /dev/null
189 ## Set if script uses the wrong hardware interface
190 echo "${interface}" > /dev/null
192 ## Set if MTU needs to be manually altered (lowered)
193 ## - the default tunnel MTU is 1024.
194 ## - if local DNS server restricts to 512 byte packets then use MTU 220
195 echo "${mtu}" > /dev/null
197 ## Set it if you want try RAW udp mode
198 echo "${skip_raw_udp_mode:=true}" > /dev/null
200 ## Set if the script should continue even if a command fails.
201 ## Used to test script when running as non-root.
202 if [ $(whoami) = root ]; then
203 echo "${continue_on_error:=false}" > /dev/null
205 echo "${continue_on_error:=true}" > /dev/null
208 ## DEBIAN PACKAGES TO INSTALL: these are needed to run this script
209 ## iodine (for /usr/sbin/iodine)
210 ## iproute (for /bin/ip)
211 ## ipcalc (for /usr/bin/ipcalc)
212 ## dnsutils (for /usr/bin/dig)
213 ## fping (for /usr/bin/fping)
214 ## or oping (for /usr/bin/oping)
215 ## gawk (for /usr/bin/gawk, to use gensub())
217 if type -P fping > /dev/null; then
219 elif type -P oping > /dev/null; then
222 ping_cmd="echo would ping"
226 ## - avoid double ping when DNS server and local router are the same
227 ## - option to not kill existing iodine DNS tunnels, in case there
228 ## are meant to be more than one
229 ## - sanify check whether default_router is on local network
231 echo ==== Creating IP-over-DNS tunnel over local network connection...
234 ## Find a network interface
236 if [ -z ${interface} ]; then
237 interfaces=$(tail --lines=+3 /proc/net/wireless \
238 | tr -d : | awk '{print $1}')
239 for dev in ${interfaces}; do
240 if ip -4 addr show dev ${dev} | grep -q inet; then
246 if [ -z ${interface} ]; then
247 interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \
248 | head -1 | awk '{print $1}')
251 if [ -z ${interface} ]; then
252 echo ERROR: No network interface found.
256 echo ==== Local network interface: ${interface}
258 ## Down any existing DNS tunnel (wish there were "approved" way to do this)
260 echo ==== Killing existing DNS tunnels...
261 if killall --quiet --wait --verbose --signal HUP iodine; then
265 ## Stabilize local network
267 if ${bounce_localnet}; then
268 echo ==== Bouncing local network connection...
269 ifdown --force ${interface} || true
270 ifup ${interface} || ${continue_on_error}
273 ## Fetch some information about the local network
275 addr=$(ip -4 addr show dev ${interface} scope global \
276 | tail -1 | awk '{print $2}')
277 prefix_len=$(echo ${addr} | sed 'sX^.*/XX')
278 local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}')
280 echo ==== Local address: ${addr}
281 echo ==== Local network: ${local_net}
283 router=$(ip -4 route list dev ${interface} \
284 | awk '$1=="default" {print $3}' | head -1)
285 if [ -z ${router} ]; then
286 ## This can happen when the default local route is already deleted
287 if [ -z ${default_router} ]; then
288 echo WARNING: no default route, guessing local router IP address.
289 ## Minimum address on local net is usually right
290 router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}')
292 echo WARNING: no default route, using configured default router.
293 ## But sometimes need to hardwire...
294 router=${default_router}
298 echo ==== Local network router: ${router}
302 testhost_ip=$(dig +short -t A -q ${testhost})
303 if [ -z ${testhost_ip} ]; then
304 echo WARNING: Failure on DNS lookup of ${testhost}.
309 nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf)
310 if [ -n "${nameservers}" ]; then
311 echo ==== DNS servers: ${nameservers}
313 echo ERROR: No DNS servers found.
317 ## Test if local network is up
319 if ${test_ping_localnet}; then
320 echo ==== Ping test of local network router and DNS servers...
321 ${ping_cmd} ${router} ${nameservers} \
322 || echo WARNING: Ping test failed.
325 ## Add point-to-point routes for any non-local DNS servers
327 for n in ${nameservers}; do
328 n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}')
329 n_net8=$(ipcalc --nobinary ${n}/8 | awk '$1=="Network:" {print $2}')
330 if [ "${n_net}" != "${local_net}" ]; then
331 if [ "${n_net8}" != "127.0.0.0/8" ]; then
332 echo ==== Adding point-to-point route for DNS server ${n}
333 ## remove point-to-point route first, in case it is already present
334 ip -4 route del ${n}/32 || true
335 ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
340 ## Bring up DNS tunnel
342 echo ==== Creating IP-over-DNS tunnel...
343 if ${skip_raw_udp_mode}; then
344 iodine_opts="${iodine_opts} -r"
347 iodine ${iodine_opts} -P "${passwd}" "${subdomain}" || ${continue_on_error}
349 ## Find DNS tunnel interface
351 tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1)
352 if [ -z "${tunnel_interface}" ]; then
353 echo WARNING: Cannot find DNS tunnel interface, using default.
354 tunnel_interface=dns0
356 echo ==== DNS tunnel interface: ${tunnel_interface}
358 ## Maybe try to change MTU
360 if [ -n "${mtu}" ]; then
361 echo ==== Setting MTU of ${tunnel_interface} to ${mtu}
362 ifconfig ${tunnel_interface} mtu ${mtu}
365 ## Figure out router at other end of tunnel, assuming router uses final octet .1
366 ## (There should be some way to get this information out of iodine, since
367 ## it *prints* it as it sets up the tunnel, so it does know it.)
369 tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \
370 | gawk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1)
372 if [ -z ${tunnel_remote} ]; then
373 echo ERROR: Cannot find DNS tunnel remote endpoint.
375 ## set something random if debugging
376 echo WARNING: Confabulating DNS tunnel remote endpoint.
377 tunnel_remote=192.168.253.1
380 echo ==== DNS tunnel remote endpoint: ${tunnel_remote}
382 if ${test_ping_tunnel}; then
383 echo ==== Ping test of local router, nameserver, and DNS tunnel...
384 ${ping_cmd} ${router} ${nameservers} ${tunnel_remote} \
385 || echo WARNING: Ping test failed.
388 ## Modify routing table to send trafic via DNS tunnel
390 echo ==== Setting default route through DNS tunnel...
392 ## Remove default route via local router
393 ip -4 route del default via ${router} || echo WARNING: No default route to delete
394 ## Add default via tunnel
395 ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
397 ## Test if all is well
399 if ${test_ping_final}; then
400 echo ==== Ping test of local router, nameserver, DNS tunnel, external test host...
401 ${ping_cmd} ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
402 || echo WARNING: Ping test failed.