#!/bin/sh
#
# 29 August 2006
# written by Marc Singer
#
# 31 January 2008
# modified by Daniel Gimpelevich, with influences from slirp.sh by Ace Evader:
# http://exitthematrix.dod.net/matrixmirror/slirp/Linux/slirp.sh
#
# 26 March 2008
# option -p added by Daniel Gimpelevich
#
# 5 August 2008
# bugs fixed and bashisms removed by Daniel Gimpelevich
#
# slirp link initializations script
# version 1.2.2
#
# NOTES
# -----
#
# o If this will be used to handle much routing, it may be worthwhile
#   having a .slirplink file with predefined user@remote and routing.
#
# o pppd's pid file has the form "PID\nDEV\n" where PID is the process
#   ID of the daemon and DEV is the name of the ppp device.
#   Extracting the PID alone from this requires some finesse and it
#   isn't clear that it will always be correct.  What we really need
#   to be able to do is interpret the whole file as a single string
#   (line) and match against that.
#
# o PPPD silent.  Some of the howto's on the net incorrectly portray
#   the setup of this kind of link.  For example, the 'silent' option
#   to pppd will prevent the link from starting.
#
# o PASSWORD_HACK.  If you do not have password-free logins
#   to the target host, the script will fail to establish the link
#   unless the 'record' option is used with PPPD.  So, the
#   PASSWORD_HACK option may be used to cope, but the recorded data
#   will be sent to /dev/null.  It's better to configure password-free
#   logins.
#
# o TCP over TCP.  According to the page
#     http://sites.inka.de/sites/bigred/devel/tcp-tcp.html
#   running TCP over TCP effectively breaks the timeout calculation
#   for TCP.  At the moment, we don't have a lot of choice for our
#   particular purpose.  One saving grace is that the motive for this
#   work is to be able to perform SNMP queries via a host with
#   priviledge to make those queries.  Thus, we're running UDP over
#   TCP which is suceptible to this particular failure.

# Uncomment to see what is happening
#set -x

usage () {
  echo " usage: slirplink up [-d] [-P] [-D] [-r] [-p PORT] USER@REMOTE[:SLIRP] [NET]..."
  echo "         slirplink down"
  echo "         slirplink status"
  echo
  echo "    The -d option enables debugging for both PPPD and slirp."
  echo "    The -P option enable the PPPD option 'record /dev/null'"
  echo "      which is a workaround for when password-free logins don't"
  echo "      work.  It's best to make password-free logins work."
  echo "    The -D option enables the use of DNS on the target host."
  echo "    The -r option makes the link the default route."
  echo "    The -p option supplies a port other than 22 to ssh."
  echo "    The optional NET parameters define networks to be routed"
  echo "      through the remote end of the PPP link."
  echo "    If SLIRP is not specified, it defaults to 'slirp' and the "
  echo "      shell will search for it on the path."
  echo
  echo "    e.g. slirplink up joe@niagra:bin/slirp 170.35.71.1/24"
  echo
  echo "  This script must be executed as root on the local (client) host so"
  echo "  that pppd may be run to handle routing."
  exit 0
}

OP=$1
[ $# -eq 0 ] && usage
shift

DEBUG=0
PASSWORD_HACK=0
PEER_DNS=0
DEF_ROUTE=0
SSH_OPTIONS="-t -e none"

while true ; do
    case "$1" in
	"-d" )
	    DEBUG=1
	    [ $# -eq 0 ] && usage
	    shift
	    ;;
	"-P" )
	    PASSWORD_HACK=1
	    [ $# -eq 0 ] && usage
	    shift
	    ;;
	"-D" )
	    PEER_DNS=1
	    [ $# -eq 0 ] && usage
	    shift
	    ;;
	"-r" )
	    DEF_ROUTE=1
	    [ $# -eq 0 ] && usage
	    shift
	    ;;
	"-p" )
	    [ $# -eq 0 ] && usage
	    shift
	    SSH_OPTIONS="$SSH_OPTIONS -p $1"
	    [ $# -eq 0 ] && usage
	    shift
	    ;;
	*)
	    break
	    ;;
    esac
done

URI=$1
[ $# -eq 0 ] || shift
[ `id -u` = 0 ] || usage

# === Slirp Special Addresses

SLIRP_NET="10.0.2.0/28"
SLIRP_LOCAL="10.0.2.15"
SLIRP_EXEC="10.0.2.1"
SLIRP_REMOTE="10.0.2.2"
SLIRP_DNS="10.0.2.3"


# === Executable path for pppd and ssh on the local host

PPPD=`which pppd`
[ -x "$PPPD" ] || PPPD=/usr/sbin/pppd
[ -x "$PPPD" ] || { echo "Unable to locate pppd program" ; exit 1; }
SSH=`which ssh`
[ -x "$SSH" ] || SSH=/usr/bin/ssh
[ -x "$SSH" ] || { echo "Unable to locate ssh program" ; exit 1; }
LINKNAME=`basename "$0"`
PIDFILE="/var/run/ppp-$LINKNAME.pid"

# === Parse the connection URI

REMOTE_USER=`echo $URI | sed -e 's/^\([^@]*\)@.*/\1/'`
REMOTE_SERVER=`echo $URI | sed -e 's/^[^@]*@\([^:]*\).*/\1/'`
REMOTE_SLIRP=`echo $URI | sed -e 's/^[^:]*:\(.*\)/\1/'`
[ x"$REMOTE_SLIRP" != x -a x"$REMOTE_SLIRP" != x"$URI" ] || REMOTE_SLIRP=slirp

# === Configure slirp options

REMOTE_SLIRP_OPTIONS="-P -b 2147483647"
[ $DEBUG = 0 ] || REMOTE_SLIRP_OPTIONS="$REMOTE_SLIRP_OPTIONS -d -1 debugppp"
REMOTE_SLIRP_OPTIONS="$REMOTE_SLIRP_OPTIONS \"'mru 1500'\" \"'mtu 1500'\""

# === Configure pppd and ssh options

PPPD_OPTIONS="noauth local $SLIRP_LOCAL:$SLIRP_REMOTE"
#PPPD_OPTIONS="$PPPD_OPTIONS silent"	# DO NOT USE
[ $PEER_DNS = 0 ]\
  || PPPD_OPTIONS="$PPPD_OPTIONS usepeerdns"
[ $DEF_ROUTE = 0 ]\
  || PPPD_OPTIONS="$PPPD_OPTIONS defaultroute replacedefaultroute"
PPPD_OPTIONS="$PPPD_OPTIONS passive noipdefault"
PPPD_OPTIONS="$PPPD_OPTIONS updetach"	# detach only after link established
PPPD_OPTIONS="$PPPD_OPTIONS linkname $LINKNAME"
SSH_OPTIONS="$SSH_OPTIONS \$REMOTE_USER@\$SERVER_IP"
[ $PASSWORD_HACK = 0 ]\
  && SSH_OPTIONS="$SSH_OPTIONS -o Batchmode=yes"\
  || PPPD_OPTIONS="$PPPD_OPTIONS record /dev/null"
[ $DEBUG = 0 ]\
  || PPPD_OPTIONS="$PPPD_OPTIONS debug nodetach record /tmp/ppp-$LINKNAME.log"
PPPD_OPTIONS="$PPPD_OPTIONS connect-delay 5000"

# ===  Perform OP

case "$OP" in
    "up" | "u" )
	[ ! -f $PIDFILE ] || { echo "link already established" ; exit ; }
	[ x"$REMOTE_USER" != x -a x"$REMOTE_SERVER" != x ] || usage
	SERVER_IP=`ping -nc 1 $REMOTE_SERVER|tr ' ' '\n'|sed '1,2d;4,$d'|tr '()' ' '`
	SERVER_IP=`echo $SERVER_IP`
	[ -z "$SERVER_IP" ] && SERVER_IP="$REMOTE_SERVER" ||
	LOCAL_ROUTE=`ip route get ${SERVER_IP}|head -n 1|sed 's,src .*$,,'`
	$PPPD $PPPD_OPTIONS pty\
	    "$SSH `eval echo $SSH_OPTIONS` $REMOTE_SLIRP $REMOTE_SLIRP_OPTIONS"
	[ $? != 0 ] && exit 1
	[ $DEBUG != 0 ] || echo "link established"
	if [ $PEER_DNS != 0 ]; then
	    echo "nameserver $SLIRP_DNS" >> /etc/ppp/resolv.conf
	fi
	ip route add $SLIRP_NET via $SLIRP_REMOTE
	[ -z "$LOCAL_ROUTE" ] || ip route add $LOCAL_ROUTE
	for NET ; do ip route add $NET via $SLIRP_REMOTE ; done
	;;
    "down" | "d" )
	[ -f $PIDFILE ] || { echo "no link established" ; exit ; }
	kill `cat $PIDFILE | grep -o -E '\<[[:digit:]]+'`
	REAL_DEFAULT=`ip route show|grep -w default|tr ' ' '\n'|grep -v '[^.0-9]'`
	ip route del `ip route show via $REAL_DEFAULT|head -n 1`
	echo "link dropped"
	;;
    "status" | "s" )
	[ -f $PIDFILE ] || { echo "link is down" ; exit ; }
	echo "pid: `cat $PIDFILE | grep -o -x -E '[[:digit:]]+'`"
	echo "device: `cat $PIDFILE | grep -o -x -E '[^[:digit:]].*'`"
	;;
    "check" | "c" )
	echo "REMOTE_USER   $REMOTE_USER"
	echo "REMOTE_SERVER $REMOTE_SERVER"
	echo "REMOTE_SLIRP  $REMOTE_SLIRP"
	echo "DEF_ROUTE     $DEF_ROUTE"
	echo "PEER_DNS      $PEER_DNS"
	echo "PASSWORD_HACK $PASSWORD_HACK"
	echo "DEBUG         $DEBUG"
	[ -z "$*" ] || for NET ; do echo "NET           $NET" ; done
	;;
    * )
	usage
	;;
esac
