Portforward: A shell script example
Paul Dickson
plug-devel@lists.PLUG.phoenix.az.us
Sat Apr 9 14:34:01 2005
This is a multi-part message in MIME format.
--Multipart=_Sat__9_Apr_2005_15_57_28_-0700_Ezsulwg/D2IZ+ayA
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
This is a shell script I use on my router to forward external ports on
the router to specific ports on internal machines. For example, from the
external world, if you ssh to port 22222 on my router, it will forward
the connection to port 22 on an internal machine (the ssh port).
The script has a bunch of arguments to be processed, so it's a good
example of writting an application in a shell script.
The script is written for busybox's ash, but it will run under bash.
Be aware: the script uses default values for my network. They can be
overridden by control arguments.
-Paul
--Multipart=_Sat__9_Apr_2005_15_57_28_-0700_Ezsulwg/D2IZ+ayA
Content-Type: text/plain;
name="portforward"
Content-Disposition: attachment;
filename="portforward"
Content-Transfer-Encoding: 7bit
#!/bin/sh
# Author: Paul Dickson (paul -at permanentmail.com) on April 1, 2005.
# Licensed under the GPL License
CMD="portforward"
LOOP=true
GETOPT=$(getopt -o chop:i:e: --long close,help,ip:,open,port:,internal:,external:,sleep: -n ${CMD} -- "$@")
if [ $? != 0 ] ; then LOOP=false ; USAGE=true ; fi
eval set -- "$GETOPT"
#echo "$@"
# Get values for EXT_IF and INT_IF
[ -e /etc/sys-parms ] && . /etc/sys-parms
FUNCT=error
PORT=none
USAGE=false
while true ; do
case $1 in
-c|--close)
case "$FUNCT" in
open) FUNCT=dupcmd ; break ;;
sleep) FUNCT=badsleep ; break ;;
esac
FUNCT=close
shift
;;
-o|--open)
case "$FUNCT" in
close) FUNCT=dupcmd ; break ;;
sleep) FUNCT=badsleep ; break ;;
esac
FUNCT=open
shift
;;
--sleep)
case "$FUNCT" in
error) FUNCT=badsleep ; break ;;
close) FUNCT=badsleep ; break ;;
esac
FUNCT=sleep
SLEEP="$2"
shift 2
;;
-h|--help) USAGE=true ; break ;;
-p|--port)
case "$2" in
bt|bittorrent|6881)
PORT=6881
LPORT=6881
[ -z "$LHOST" ] && LHOST=192.168.1.4
;;
bt*) # Allow bt1-bt9 (ports 6881-6889)
PORT=$((6880+$(echo bt1|sed -e 's/bt//')))
LPORT=$PORT
[ -z "$LHOST" ] && LHOST=192.168.1.4
;;
ssh|22222)
PORT=22222
LPORT=22
[ -z "$LHOST" ] && LHOST=192.168.1.3
;;
ssh22)
PORT=22
LPORT=22
[ -z "$LHOST" ] && LHOST=192.168.1.3
;;
http)
PORT=8081
LPORT=80
[ -z "$LHOST" ] && LHOST=192.168.1.3
;;
irc)
PORT=6667
LPORT=6667
[ -z "$LHOST" ] && LHOST=192.168.1.4
;;
*)
PORT="error"
break
;;
esac
shift 2
;;
-i|--internal) INT_IF="$2" ; shift 2 ;;
-e|--external) EXT_IF="$2" ; shift 2 ;;
--ip) LHOST="$2" ; shift 2 ;;
--) shift ; break ;;
*) echo "Internal error! Verify the getopt argument lengths." ; exit 1 ;;
esac
done
if [ -z "$INT_IF" -o -z "$EXT_IF" ]; then
USAGE=true
echo "$CMD: Both --internal and --external interfaces must specified. The"
echo " interfaces correspond to the interior and exterior networks."
fi
case "$FUNCT,$PORT,$USAGE" in
dupcmd,*,false)
echo "$CMD: Only ONE of --open or --close can be specified."
USAGE=true
;;
error,*,false)
echo "$CMD: Either --open or --close must be specified."
USAGE=true
;;
badsleep,*,false)
echo "$CMD: The --sleep argument can only be used after the --open."
USAGE=true
;;
*,error,false)
echo "$CMD: Invalid port specified. Valid ports are:"
echo " bt,bt[1-9],bittorrent,6881,ssh,22222,ssh22,http(8081).irc"
USAGE=true
;;
*,none,false)
echo "$CMD: No port specified. Valid ports are:"
echo " bt,bt[1-9],bittorrent,6881,ssh,22222,ssh22,http(8081).irc"
USAGE=true
;;
open,*,false)
iptables --table nat --append PREROUTING \
-i $EXT_IF -p tcp --dport $PORT -j DNAT --to $LHOST:$LPORT
iptables --append FORWARD \
-i $EXT_IF -o $INT_IF -p tcp -d $LHOST --dport $LPORT -j ACCEPT
;;
close,*,false)
iptables --table nat --delete PREROUTING \
-i $EXT_IF -p tcp --dport $PORT -j DNAT --to $LHOST:$LPORT
iptables --delete FORWARD \
-i $EXT_IF -o $INT_IF -p tcp -d $LHOST --dport $LPORT -j ACCEPT
;;
sleep,*,false)
iptables --table nat --append PREROUTING \
-i $EXT_IF -p tcp --dport $PORT -j DNAT --to $LHOST:$LPORT
iptables --append FORWARD \
-i $EXT_IF -o $INT_IF -p tcp -d $LHOST --dport $LPORT -j ACCEPT
sleep $SLEEP
iptables --table nat --delete PREROUTING \
-i $EXT_IF -p tcp --dport $PORT -j DNAT --to $LHOST:$LPORT
iptables --delete FORWARD \
-i $EXT_IF -o $INT_IF -p tcp -d $LHOST --dport $LPORT -j ACCEPT
;;
esac
if [ "$USAGE" = "true" ]; then
echo ""
echo " $CMD (--open|--close) --port <external-port> [--ip <local-ip>]"
echo ""
echo " --open (-o) Open command."
echo " --close (c) Close command."
echo " --ip <local-ip> IP address of internal host."
echo " --port (-p) <ext-port> External port to open."
echo " --internal (-i) <if> Interior network interface (INT_IF=)."
echo " --external (-e) <if> Exterior network interface (EXT_IF=)."
echo " --sleep N Delay time before closing. N passed to sleep."
echo " --help (h) This usage message."
echo ""
exit 1
fi
exit 0
--Multipart=_Sat__9_Apr_2005_15_57_28_-0700_Ezsulwg/D2IZ+ayA--