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
52 The configuration file consists of lines which are either comments
53 starting with '#', or settings of the form VAR="val". Valid VARs are:
56 Sample value: your.tunnel.sub.domain (no default, must be set)
59 Sample value: password_for_that_tunnel (no default, must be set)
62 Hostname to ping when testing if network is working (default:
66 Take the local network down and then up again before starting
67 tunnel (default: true)
70 Test if the local network is working by pinging the gateway
74 Test if the iodine tunnel is working after it has been set up by
75 pinging the host at the other end (default: true)
78 Test if the tunnel is working after everything is ostensibly set
79 up by trying to ping an external host (default: true)
82 IP address of router on the local network---should be found
83 automatically, set this if that fails and the program guesses wrong.
86 Interface to use (e.g., eth1, eth0, etc) for connection to DNS
87 server used for the iodine tunnel---should be found automatically,
88 set this if that fails and the program guesses wrong.
91 Set if tunnel MTU needs to be manually changed (lowered). Should
92 not be necessary anymore, as recent versions of iodine negotiate
93 an appropriate MTU during tunnel setup. But if that negotiation
94 does not happen, or if you are using an older version of iodine,
95 the default tunnel MTU is 1024, and if the local DNS server
96 restricts to 512 byte packets you might need to use an MTU of 220.
99 Set if the script should continue even if a command fails.
100 Use to test script when running as non-root. Defaults to false
101 if running as root, true otherwise.
107 echo iodine-client-start 1.0.1
130 echo error: unknown option "$1"
136 echo error: too many arguments "$*"
141 echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
143 if [ -r ${iodine_client_rc} ]; then
144 . ${iodine_client_rc}
146 echo WARNING: Cannot read ${iodine_client_rc}
149 if [ -z ${subdomain} ]; then
150 read -p "DNS tunnel DNS subdomain: " subdomain
153 if [ -z ${subdomain} ]; then
154 echo ERROR: Must set subdomain.
158 if [ -z ${passwd} ]; then
159 read -p "Password for DNS tunnel over ${subdomain}: " passwd
162 ## This is a host name used for testing DNS and for pinging
163 echo "${testhost:=slashdot.org}" > /dev/null
165 ## Set if local network should be taken down and then up
166 echo "${bounce_localnet:=true}" > /dev/null
168 ## Set for testing network availability via ping at various points
169 echo "${test_ping_localnet:=true}" > /dev/null
170 echo "${test_ping_tunnel:=true}" > /dev/null
171 echo "${test_ping_final:=true}" > /dev/null
173 ## Set if the script cannot find and then incorrectly guesses the
174 ## local network router
175 echo "${default_router}" > /dev/null
177 ## Set if script uses the wrong hardware interface
178 echo "${interface}" > /dev/null
180 ## Set if MTU needs to be manually altered (lowered)
181 ## - the default tunnel MTU is 1024.
182 ## - if local DNS server restricts to 512 byte packets then use MTU 220
183 echo "${mtu}" > /dev/null
185 ## Set if the script should continue even if a command fails.
186 ## Used to test script when running as non-root.
187 if [ $(whoami) = root ]; then
188 echo "${continue_on_error:=false}" > /dev/null
190 echo "${continue_on_error:=true}" > /dev/null
193 ## DEBIAN PACKAGES TO INSTALL: these are needed to run this script
194 ## iodine (for /usr/sbin/iodine)
195 ## iproute (for /bin/ip)
196 ## ipcalc (for /usr/bin/ipcalc)
197 ## dnsutils (for /usr/bin/dig)
198 ## fping (for /usr/bin/fping)
199 ## gawk (for /usr/bin/gawk, to use gensub())
202 ## - avoid double ping when DNS server and local router are the same
203 ## - option to not kill existing iodine DNS tunnels, in case there
204 ## are meant to be more than one
205 ## - sanify check whether default_router is on local network
207 echo ==== Creating IP-over-DNS tunnel over local network connection...
210 ## Find a network interface
212 if [ -z ${interface} ]; then
213 interface=$(tail --lines=+3 /proc/net/wireless \
214 | head -1 | tr -d : | awk '{print $1}')
217 if [ -z ${interface} ]; then
218 interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \
219 | head -1 | awk '{print $1}')
222 if [ -z ${interface} ]; then
223 echo ERROR: No network interface found.
227 echo ==== Local network interface: ${interface}
229 ## Down any existing DNS tunnel (wish there were "approved" way to do this)
231 echo ==== Killing existing DNS tunnels...
232 if killall --quiet --wait --verbose --signal HUP iodine; then
236 ## Stabilize local network
238 if ${bounce_localnet}; then
239 echo ==== Bouncing local network connection...
240 ifdown --force ${interface} || true
241 ifup ${interface} || ${continue_on_error}
244 ## Fetch some information about the local network
246 addr=$(ip -4 addr show dev ${interface} scope global \
247 | tail -1 | awk '{print $2}')
248 prefix_len=$(echo ${addr} | sed 'sX^.*/XX')
249 local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}')
251 echo ==== Local address: ${addr}
252 echo ==== Local network: ${local_net}
254 router=$(ip -4 route list dev ${interface} \
255 | awk '$1=="default" {print $3}' | head -1)
256 if [ -z ${router} ]; then
257 ## This can happen when the default local route is already deleted
258 if [ -z ${default_router} ]; then
259 echo WARNING: no default route, guessing local router IP address.
260 ## Minimum address on local net is usually right
261 router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}')
263 echo WARNING: no default route, using configured default router.
264 ## But sometimes need to hardwire...
265 router=${default_router}
269 echo ==== Local network router: ${router}
273 testhost_ip=$(dig +short -t A -q ${testhost})
274 if [ -z ${testhost_ip} ]; then
275 echo WARNING: Failure on DNS lookup of ${testhost}.
280 nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf)
281 if [ -n "${nameservers}" ]; then
282 echo ==== DNS servers: ${nameservers}
284 echo ERROR: No DNS servers found.
288 ## Test if local network is up
290 if ${test_ping_localnet}; then
291 echo ==== Ping test of local network router and DNS servers...
292 fping -C1 ${router} ${nameservers} \
293 || echo WARNING: Ping test failed.
296 ## Add point-to-point routes for any non-local DNS servers
298 for n in ${nameservers}; do
299 n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}')
300 if [ "${n_net}" != "${local_net}" ]; then
301 echo ==== Adding point-to-point route for DNS server ${n}
302 ## remove point-to-point route first, in case it is already present
303 ip -4 route del ${n}/32 || true
304 ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
308 ## Bring up DNS tunnel
310 echo ==== Creating IP-over-DNS tunnel...
311 iodine -P "${passwd}" "${subdomain}" || ${continue_on_error}
313 ## Find DNS tunnel interface
315 tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1)
316 if [ -z "${tunnel_interface}" ]; then
317 echo WARNING: Cannot find DNS tunnel interface, using default.
318 tunnel_interface=dns0
320 echo ==== DNS tunnel interface: ${tunnel_interface}
322 ## Maybe try to change MTU
324 if [ -n "${mtu}" ]; then
325 echo ==== Setting MTU of ${tunnel_interface} to ${mtu}
326 ifconfig ${tunnel_interface} mtu ${mtu}
329 ## Figure out router at other end of tunnel, assuming router uses final octet .1
330 ## (There should be some way to get this information out of iodine, since
331 ## it *prints* it as it sets up the tunnel, so it does know it.)
333 tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \
334 | gawk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1)
336 if [ -z ${tunnel_remote} ]; then
337 echo ERROR: Cannot find DNS tunnel remote endpoint.
339 ## set something random if debugging
340 echo WARNING: Confabulating DNS tunnel remote endpoint.
341 tunnel_remote=192.168.253.1
344 echo ==== DNS tunnel remote endpoint: ${tunnel_remote}
346 if ${test_ping_tunnel}; then
347 echo ==== Ping test of local router, nameserver, and DNS tunnel...
348 fping -C1 ${router} ${nameservers} ${tunnel_remote} \
349 || echo WARNING: Ping test failed.
352 ## Modify routing table to send trafic via DNS tunnel
354 echo ==== Setting default route through DNS tunnel...
356 ## Remove default route via local router
357 ip -4 route del default via ${router} || ${continue_on_error}
358 ## Add default via tunnel
359 ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
361 ## Test if all is well
363 if ${test_ping_final}; then
364 echo ==== Ping test of local router, nameserver, DNS tunnel, external test host...
365 fping -C1 ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
366 || echo WARNING: Ping test failed.