#!/bin/sh
#
# Shorewall 3.4 -- /usr/share/shorewall/lib.tunnels
#
#     This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
#     (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007 - Tom Eastep (teastep@shorewall.net)
#
#	Complete documentation is available at http://shorewall.net
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of Version 2 of the GNU General Public License
#	as published by the Free Software Foundation.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This library is loaded by /usr/share/shorewall/compiler when the tunnels file is
# non-empty.
#

#
# Set up ipsec tunnels
#
setup_tunnels() # $1 = name of tunnels file
{
    local inchain
    local outchain
    local source
    local dest

    setup_one_ipsec() # $1 = Tunnel Kind $2 = gateway zones
    {
    	local kind=$1 noah=

	case $kind in
	    *:*)
	    	noah=${kind#*:}
		[ $noah = noah -o $noah = NOAH ] || fatal_error "Invalid IPSEC modifier $noah in tunnel \"$tunnel\""
		kind=${kind%:*}
		;;
	esac

	[ $kind = IPSEC ] && kind=ipsec

	[ $kind = ipsec ] || noah=noah

	options="-m state --state NEW -j ACCEPT"
	addrule2 $inchain	  -p 50	 $source -j ACCEPT
	addrule2 $outchain	  -p 50	 $dest   -j ACCEPT

	if [ -z "$noah" ]; then
	    run_iptables -A $inchain  -p 51 $source -j ACCEPT
	    run_iptables -A $outchain -p 51 $dest   -j ACCEPT
	fi

	run_iptables -A $outchain -p udp $dest --dport 500 $options

	if [ $kind = ipsec ]; then
	    run_iptables -A $inchain  -p udp $source --dport 500 $options
	else
	    run_iptables -A $inchain  -p udp $source --dport 500 $options
	    run_iptables -A $inchain  -p udp $source --dport 4500 $options
	fi

	for z in $(separate_list $2); do
	    if validate_zone $z; then
		if [ -z "$POLICY_MATCH" ]; then
		    addrule ${z}2${FW} -p 50 $source -j ACCEPT
		    addrule ${FW}2${z} -p 50 $dest   -j ACCEPT
		    if [ -z "$noah" ]; then
			addrule ${z}2${FW} -p 51 $source -j ACCEPT
			addrule ${FW}2${z} -p 51 $dest   -j ACCEPT
		    fi
		fi
		if [ $kind = ipsec ]; then
		    addrule ${z}2${FW} -p udp $source --dport 500 $options
		    addrule ${FW}2${z} -p udp $dest   --dport 500 $options
		else
		    addrule ${z}2${FW} -p udp $source --dport 500 $options
		    addrule ${FW}2${z} -p udp $dest   --dport 500 $options
		    addrule ${z}2${FW} -p udp $source --dport 4500 $options
		    addrule ${FW}2${z} -p udp $dest   --dport 4500 $options
		fi
	    else
		fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\""
	    fi
	done

	progress_message_and_save "   IPSEC tunnel to $gateway defined."
    }

    setup_one_other() # $1 = TYPE, $2 = protocol
    {
	addrule2 $inchain  -p $2 $source -j ACCEPT
	addrule2 $outchain -p $2 $dest   -j ACCEPT

	progress_message_and_save "   $1 tunnel to $gateway compiled."
    }

    setup_pptp_client()
    {
	addrule2 $outchain -p 47               $dest   -j ACCEPT
	addrule2 $inchain  -p 47               $source -j ACCEPT
	addrule2 $outchain -p tcp --dport 1723 $dest   -j ACCEPT

	progress_message_and_save "   PPTP tunnel to $gateway defined."
    }

    setup_pptp_server()
    {
	addrule2 $inchain  -p 47               $source -j ACCEPT
	addrule2 $outchain -p 47               $dest   -j ACCEPT
	addrule2 $inchain  -p tcp --dport 1723 $source -j ACCEPT

	progress_message_and_save "   PPTP server defined."
    }

    setup_one_openvpn() # $1 = kind[:port]
    {
	local protocol=udp
	local p=1194

	case $1 in
	    *:*:*)
		protocol=${1%:*}
		protocol=${protocol#*:}
		p=${1##*:}
		;;
	    *:tcp|*:udp|*:TCP|*:UDP)
		protocol=${1#*:}
		;;
	    *:*)
		p=${1#*:}
		;;
	esac

	addrule2 $inchain  -p $protocol $source --dport $p -j ACCEPT
	addrule2 $outchain -p $protocol $dest   --dport $p -j ACCEPT

	progress_message_and_save "   OPENVPN tunnel to $gateway:$protocol:$p defined."
    }

    setup_one_openvpn_server() # $1 = kind[:port]
    {
	local protocol=udp
	local p=1194

	case $1 in
	    *:*:*)
		protocol=${1%:*}
		protocol=${protocol#*:}
		p=${1##*:}
		;;
	    *:tcp|*:udp|*:TCP|*:UDP)
		protocol=${1#*:}
		;;
	    *:*)
		p=${1#*:}
		;;
	esac

	addrule2 $inchain  -p $protocol $source --dport $p -j ACCEPT
	addrule2 $outchain -p $protocol $dest   --sport $p -j ACCEPT

	progress_message_and_save "   OPENVPN server tunnel from $gateway:$protocol:$p defined."
    }

    setup_one_openvpn_client() # $1 = kind[:port]
    {
	local protocol=udp
	local p=1194

	case $1 in
	    *:*:*)
		protocol=${1%:*}
		protocol=${protocol#*:}
		p=${1##*:}
		;;
	    *:tcp|*:udp|*:TCP|*:UDP)
		protocol=${1#*:}
		;;
	    *:*)
		p=${1#*:}
		;;
	esac

	addrule2 $inchain  -p $protocol $source --sport $p -j ACCEPT
	addrule2 $outchain -p $protocol $dest   --dport $p -j ACCEPT

	progress_message_and_save "   OPENVPN client tunnel to $gateway:$protocol:$p defined."
    }

    setup_one_generic() # $1 = kind:protocol[:port]
    {
	local protocol
	local p=

	case $1 in
	    *:*:*)
		p=${1##*:}
		protocol=${1%:*}
		protocol=${protocol#*:}
		;;
	    *:*)
		protocol=${1#*:}
		;;
	    *)
		protocol=udp
		p=5000
		;;
	esac

	p=${p:+--dport $p}

	addrule2 $inchain  -p $protocol $source $p -j ACCEPT
	addrule2 $outchain -p $protocol $dest   $p -j ACCEPT

	progress_message_and_save "   GENERIC tunnel to $1:$p defined."
    }

    while read kind z gateway z1; do
	tunnel="$(echo $kind $z $gateway $z1)"
	if validate_zone $z; then
	    inchain=${z}2${FW}
	    outchain=${FW}2${z}
	    gateway=${gateway:-0.0.0.0/0}
	    source=$(source_ip_range $gateway)
	    dest=$(dest_ip_range $gateway)

	    case $kind in
		ipsec|IPSEC|ipsec:*|IPSEC:*)
		    setup_one_ipsec $kind $z1
		    ;;
		ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*)
		    setup_one_ipsec $kind $z1
		    ;;
		ipip|IPIP)
		    setup_one_other IPIP 4
		    ;;
		gre|GRE)
		    setup_one_other GRE 47
		    ;;
		6to4|6TO4)
		    setup_one_other 6to4 41
		    ;;
		pptpclient|PPTPCLIENT)
		    setup_pptp_client
		    ;;
		pptpserver|PPTPSERVER)
		    setup_pptp_server
		    ;;
		openvpn|OPENVPN|openvpn:*|OPENVPN:*)
		    setup_one_openvpn $kind
		    ;;
		openvpnclient|OPENVPNCLIENT|openvpnclient:*|OPENVPNCLIENT:*)
		    setup_one_openvpn_client $kind
		    ;;
		openvpnserver|OPENVPNSERVER|openvpnserver:*|OPENVPNSERVER:*)
		    setup_one_openvpn_server $kind
		    ;;
		generic:*|GENERIC:*)
		    setup_one_generic $kind
		    ;;
		*)
		    error_message "WARNING: Tunnels of type $kind are not supported:" \
			"Tunnel \"$tunnel\" Ignored"
		    ;;
	    esac
	    save_command
	else
	    error_message "ERROR: Invalid gateway zone ($z)" \
		" -- Tunnel \"$tunnel\" Ignored"
	fi
    done < $TMP_DIR/tunnels
}
