Set Standards-Version to 3.8.4 (no changes).
[debian/iodine.git] / debian / iodine-client-start
1 #! /bin/bash
2
3 ### Script to set up an iodine tunnel route traffic through it
4 ###
5 ### Copyright 2008 Barak A. Pearlmutter <bap@debian.org>
6 ###
7 ### License: MIT
8 ###
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
12 ### all copies.
13 ###
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.
22
23 ## Cause script to bail immediately on failed command
24 set -e
25
26 ## Options for user to set.
27
28 ## Minimal customization: put the two lines
29 ##  subdomain=your.tunnel.sub.domain
30 ##  passwd=password_for_that_tunnel
31 ## in the file /etc/default/iodine-client.
32
33 echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
34
35 if [ -r ${iodine_client_rc} ]; then
36     . ${iodine_client_rc}
37 else
38     echo WARNING: Cannot read ${iodine_client_rc}
39 fi
40
41 if [ -z ${subdomain} ]; then
42     read -p "DNS tunnel DNS subdomain: " subdomain
43 fi
44
45 if [ -z ${subdomain} ]; then
46     echo ERROR: Must set subdomain.
47     exit 1
48 fi
49
50 if [ -z ${passwd} ]; then
51     read -p "Password for DNS tunnel over ${subdomain}: " passwd
52 fi
53
54 ## This is a host name used for testing DNS and for pinging
55 echo "${testhost:=slashdot.org}"                > /dev/null
56
57 ## Set if local network should be taken down and then up
58 echo "${bounce_localnet:=true}"                 > /dev/null
59
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
64
65 ## Set if the script cannot find and then incorrectly guesses the
66 ## local network router
67 echo "${default_router}"                        > /dev/null
68
69 ## Set if script uses the wrong hardware interface
70 echo "${interface}"                             > /dev/null
71
72 ## Set if MTU needs to be manually altered (lowered)
73 ##  - the default tunnel MTU is 1024.
74 ##  - if local DNS server restricts to 512 byte packets then use MTU 220
75 echo "${mtu}"                                   > /dev/null
76
77 ## Set if the script should continue even if a command fails.
78 ## Used to test script when running as non-root.
79 if [ $(whoami) = root ]; then
80     echo "${continue_on_error:=false}"          > /dev/null
81 else
82     echo "${continue_on_error:=true}"           > /dev/null
83 fi
84
85 ## DEBIAN PACKAGES TO INSTALL: these are needed to run this script
86 ##  iodine (for /usr/sbin/iodine)
87 ##  iproute (for /bin/ip)
88 ##  ipcalc (for /usr/bin/ipcalc)
89 ##  dnsutils (for /usr/bin/dig)
90 ##  fping (for /usr/bin/fping)
91 ##  gawk (for /usr/bin/gawk, to use gensub())
92
93 ## TO DO
94 ## - avoid double ping when DNS server and local router are the same
95 ## - option to not kill existing iodine DNS tunnels, in case there
96 ##   are meant to be more than one
97 ## - sanify check whether default_router is on local network
98
99 echo ==== Creating IP-over-DNS tunnel over local network connection...
100
101
102 ## Find a network interface
103
104 if [ -z ${interface} ]; then
105     interface=$(tail --lines=+3 /proc/net/wireless \
106         | head -1 | tr -d : | awk '{print $1}')
107 fi
108
109 if [ -z ${interface} ]; then
110     interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \
111         | head -1 | awk '{print $1}')
112 fi
113
114 if [ -z ${interface} ]; then
115     echo ERROR: No network interface found.
116     exit 1
117 fi
118
119 echo ==== Local network interface: ${interface}
120
121 ## Down any existing DNS tunnel (wish there were "approved" way to do this)
122
123 echo ==== Killing existing DNS tunnels...
124 if killall --quiet --wait --verbose --signal HUP iodine; then
125     sleep 2
126 fi
127
128 ## Stabilize local network
129
130 if ${bounce_localnet}; then
131     echo ==== Bouncing local network connection...
132     ifdown --force ${interface} || true
133     ifup ${interface} || ${continue_on_error}
134 fi
135
136 ## Fetch some information about the local network
137
138 addr=$(ip -4 addr show dev ${interface} scope global \
139     | tail -1 | awk '{print $2}')
140 prefix_len=$(echo ${addr} | sed 'sX^.*/XX')
141 local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}')
142
143 echo ==== Local address: ${addr}
144 echo ==== Local network: ${local_net}
145
146 router=$(ip -4 route list dev ${interface} \
147     | awk '$1=="default" {print $3}' | head -1)
148 if [ -z ${router} ]; then
149     ## This can happen when the default local route is already deleted
150     if [ -z ${default_router} ]; then
151         echo WARNING: no default route, guessing local router IP address.
152         ## Minimum address on local net is usually right
153         router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}')
154     else
155         echo WARNING: no default route, using configured default router.
156         ## But sometimes need to hardwire...
157         router=${default_router}
158     fi
159 fi
160
161 echo ==== Local network router: ${router}
162
163 ## Test DNS service
164
165 testhost_ip=$(dig +short -t A -q ${testhost})
166 if [ -z ${testhost_ip} ]; then
167     echo WARNING: Failure on DNS lookup of ${testhost}.
168 fi
169
170 ## fetch DNS servers
171
172 nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf)
173 if [ -n "${nameservers}" ]; then
174     echo ==== DNS servers: ${nameservers}
175 else
176     echo ERROR: No DNS servers found.
177     exit 1
178 fi
179
180 ## Test if local network is up
181
182 if ${test_ping_localnet}; then
183     echo ==== Ping test of  local network router and DNS servers...
184     fping -C1 ${router} ${nameservers} \
185         || echo WARNING: Ping test failed.
186 fi
187
188 ## Add point-to-point routes for any non-local DNS servers
189
190 for n in ${nameservers}; do
191     n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}')
192     if [ "${n_net}" != "${local_net}" ]; then
193         echo ==== Adding point-to-point route for DNS server ${n}
194         ## remove point-to-point route first, in case it is already present
195         ip -4 route del ${n}/32 || true
196         ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
197     fi
198 done
199
200 ## Bring up DNS tunnel
201
202 echo ==== Creating IP-over-DNS tunnel...
203 iodine -P "${passwd}" "${subdomain}" || ${continue_on_error}
204
205 ## Find DNS tunnel interface
206
207 tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1)
208 if [ -z "${tunnel_interface}" ]; then
209     echo WARNING: Cannot find DNS tunnel interface, using default.
210     tunnel_interface=dns0
211 fi
212 echo ==== DNS tunnel interface: ${tunnel_interface}
213
214 ## Maybe try to change MTU
215
216 if [ -n "${mtu}" ]; then
217     echo ==== Setting MTU of ${tunnel_interface} to ${mtu}
218     ifconfig ${tunnel_interface} mtu ${mtu}
219 fi
220
221 ## Figure out router at other end of tunnel, assuming router uses final octet .1
222 ## (There should be some way to get this information out of iodine, since
223 ## it *prints* it as it sets up the tunnel, so it does know it.)
224
225 tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \
226     | gawk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1)
227
228 if [ -z ${tunnel_remote} ]; then
229     echo ERROR: Cannot find DNS tunnel remote endpoint.
230     ${continue_on_error}
231     ## set something random if debugging
232     echo WARNING: Confabulating DNS tunnel remote endpoint.
233     tunnel_remote=192.168.253.1
234 fi
235
236 echo ==== DNS tunnel remote endpoint: ${tunnel_remote}
237
238 if ${test_ping_tunnel}; then
239     echo ==== Ping test of local router, nameserver, and DNS tunnel...
240     fping -C1 ${router} ${nameservers} ${tunnel_remote} \
241         || echo WARNING: Ping test failed.
242 fi
243
244 ## Modify routing table to send trafic via DNS tunnel
245
246 echo ==== Setting default route through DNS tunnel...
247
248 ## Remove default route via local router
249 ip -4 route del default via ${router} || ${continue_on_error}
250 ## Add default via tunnel
251 ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
252
253 ## Test if all is well
254
255 if ${test_ping_final}; then
256     echo ==== Ping test of local router, nameserver, DNS tunnel, external test host...
257     fping -C1 ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
258         || echo WARNING: Ping test failed.
259 fi