## Cause script to bail immediately on failed command
set -e
-## Options for user to set.
+function usage
+{
+ cat <<EOF
+'iodine-client-start' starts an iodine IP-over-DNS tunnel.
-## Minimal customization: put the two lines
-## subdomain=your.tunnel.sub.domain
-## passwd=password_for_that_tunnel
-## in the file /etc/default/iodine-client.
+Usage: iodine-client-start [option]
+
+ -h, --help Print help and exit
+ -v, --version Print version info and exit
+
+Invoking the program without options attempts to set up and configure
+an iodine IP-over-DNS tunnel using the configuration in the file
+/etc/default/iodine-client or by querying the user. It tries to
+figure out the right way to set things up by observing the network,
+and if all else fails by guessing.
+
+QUICK CONFIGURATION
+
+Put two lines in the file /etc/default/iodine-client
+
+ subdomain=your.tunnel.sub.domain
+
+ passwd=password_for_that_tunnel
+
+
+or invoke the script with those environment variables set:
+
+ env subdomain=xxx passwd=xxx iodine-client-start
+
+If these are not set, the script will query the user for them.
+
+DETAILS
+
+The configuration file consists of lines which are either comments
+starting with '#', or settings of the form VAR="val". Valid VARs are:
+
+subdomain
+ Sample value: your.tunnel.sub.domain (no default, must be set)
+
+passwd
+ Sample value: password_for_that_tunnel (no default, must be set)
+
+testhost
+ Hostname to ping when testing if network is working (default:
+ slashdot.org)
+
+bounce_localnet
+ Take the local network down and then up again before starting
+ tunnel (default: false)
+
+test_ping_localnet
+ Test if the local network is working by pinging the gateway
+ (default: true)
+
+test_ping_tunnel
+ Test if the iodine tunnel is working after it has been set up by
+ pinging the host at the other end (default: true)
+
+test_ping_final
+ Test if the tunnel is working after everything is ostensibly set
+ up by trying to ping an external host (default: true)
+
+default_router
+ IP address of router on the local network---should be found
+ automatically, set this if that fails and the program guesses wrong.
+
+interface
+ Interface to use (e.g., eth1, eth0, etc) for connection to DNS
+ server used for the iodine tunnel---should be found automatically,
+ set this if that fails and the program guesses wrong.
+
+mtu
+ Set if tunnel MTU needs to be manually changed (lowered). Should
+ not be necessary anymore, as recent versions of iodine negotiate
+ an appropriate MTU during tunnel setup. But if that negotiation
+ does not happen, or if you are using an older version of iodine,
+ the default tunnel MTU is 1024, and if the local DNS server
+ restricts to 512 byte packets you might need to use an MTU of 220.
+
+skip_raw_udp_mode
+ Set "-r" option in iodine command line. With this option, iodine
+ does not try to establish a direct UDP socket to the iodine server
+ on port 53. (default: true).
+
+continue_on_error
+ Set if the script should continue even if a command fails.
+ Use to test script when running as non-root. Defaults to false
+ if running as root, true otherwise.
+EOF
+}
+
+function version
+{
+ echo iodine-client-start 1.0.5
+}
+
+case $# in
+ 0)
+ ;;
+ 1)
+ case "$1" in
+ # start)
+ # ;;
+ # stop)
+ # ;;
+ # restart)
+ # ;;
+ --version|-v)
+ version
+ exit
+ ;;
+ --help|-h)
+ usage
+ exit
+ ;;
+ *)
+ echo error: unknown option "$1"
+ exit 1
+ esac
+ exit
+ ;;
+ *)
+ echo error: too many arguments "$*"
+ exit 1
+ ;;
+esac
echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null
echo "${testhost:=slashdot.org}" > /dev/null
## Set if local network should be taken down and then up
-echo "${bounce_localnet:=true}" > /dev/null
+echo "${bounce_localnet:=false}" > /dev/null
## Set for testing network availability via ping at various points
echo "${test_ping_localnet:=true}" > /dev/null
## - if local DNS server restricts to 512 byte packets then use MTU 220
echo "${mtu}" > /dev/null
+## Set it if you want try RAW udp mode
+echo "${skip_raw_udp_mode:=true}" > /dev/null
+
## Set if the script should continue even if a command fails.
## Used to test script when running as non-root.
if [ $(whoami) = root ]; then
## ipcalc (for /usr/bin/ipcalc)
## dnsutils (for /usr/bin/dig)
## fping (for /usr/bin/fping)
+## or oping (for /usr/bin/oping)
## gawk (for /usr/bin/gawk, to use gensub())
+if type -P fping > /dev/null; then
+ ping_cmd="fping -C1"
+elif type -P oping > /dev/null; then
+ ping_cmd="oping -c1"
+else
+ ping_cmd="echo would ping"
+fi
+
## TO DO
## - avoid double ping when DNS server and local router are the same
## - option to not kill existing iodine DNS tunnels, in case there
## Find a network interface
if [ -z ${interface} ]; then
- interface=$(tail --lines=+3 /proc/net/wireless \
- | head -1 | tr -d : | awk '{print $1}')
+ interfaces=$(tail --lines=+3 /proc/net/wireless \
+ | tr -d : | awk '{print $1}')
+ for dev in ${interfaces}; do
+ if ip -4 addr show dev ${dev} | grep -q inet; then
+ interface=${dev}
+ fi
+ done
fi
if [ -z ${interface} ]; then
if ${test_ping_localnet}; then
echo ==== Ping test of local network router and DNS servers...
- fping -C1 ${router} ${nameservers} \
+ ${ping_cmd} ${router} ${nameservers} \
|| echo WARNING: Ping test failed.
fi
for n in ${nameservers}; do
n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}')
+ n_net8=$(ipcalc --nobinary ${n}/8 | awk '$1=="Network:" {print $2}')
if [ "${n_net}" != "${local_net}" ]; then
- echo ==== Adding point-to-point route for DNS server ${n}
- ## remove point-to-point route first, in case it is already present
- ip -4 route del ${n}/32 || true
- ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
+ if [ "${n_net8}" != "127.0.0.0/8" ]; then
+ echo ==== Adding point-to-point route for DNS server ${n}
+ ## remove point-to-point route first, in case it is already present
+ ip -4 route del ${n}/32 || true
+ ip -4 route add ${n}/32 via ${router} || ${continue_on_error}
+ fi
fi
done
## Bring up DNS tunnel
echo ==== Creating IP-over-DNS tunnel...
-iodine -P "${passwd}" "${subdomain}" || ${continue_on_error}
+if ${skip_raw_udp_mode}; then
+ iodine_opts="${iodine_opts} -r"
+fi
+
+iodine ${iodine_opts} -P "${passwd}" "${subdomain}" || ${continue_on_error}
## Find DNS tunnel interface
if ${test_ping_tunnel}; then
echo ==== Ping test of local router, nameserver, and DNS tunnel...
- fping -C1 ${router} ${nameservers} ${tunnel_remote} \
+ ${ping_cmd} ${router} ${nameservers} ${tunnel_remote} \
|| echo WARNING: Ping test failed.
fi
echo ==== Setting default route through DNS tunnel...
## Remove default route via local router
-ip -4 route del default via ${router} || ${continue_on_error}
+ip -4 route del default via ${router} || echo WARNING: No default route to delete
## Add default via tunnel
ip -4 route add default via ${tunnel_remote} || ${continue_on_error}
if ${test_ping_final}; then
echo ==== Ping test of local router, nameserver, DNS tunnel, external test host...
- fping -C1 ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
+ ${ping_cmd} ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \
|| echo WARNING: Ping test failed.
fi