VPN_CONFIG="/var/ipfire/vpn/config"
+ROUTE_TABLE="220"
+ROUTE_TABLE_PRIO="128"
+
+eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
eval $(/usr/local/bin/readhash /var/ipfire/vpn/settings)
+# Get RED interface name
+if [ -r "/var/ipfire/red/iface" ]; then
+ RED_INTF="$(</var/ipfire/red/iface)"
+else
+ RED_INTF="red0"
+fi
+
VARS=(
- id status name lefthost type ctype x1 x2 x3 leftsubnets
- remote righthost rightsubnets x5 x6 x7 x8 x9 x10 x11 x12
- x13 x14 x15 x16 x17 x18 x19 x20 x21 proto x22 x23 x24
- route x26 mode interface_mode interface_address interface_mtu rest
+ id status name lefthost type ctype psk local local_id leftsubnets
+ remote_id remote rightsubnets x3 x4 x5 x6 x7 x8 x9 x10 x11 x12
+ x13 x14 x15 x16 x17 x18 x19 proto x20 x21 x22
+ route x23 mode interface_mode interface_address interface_mtu rest
)
log() {
logger -t ipsec "$@"
}
+resolve_hostname() {
+ local hostname="${1}"
+
+ dig +short A "${hostname}" | tail -n1
+}
+
+ip_encode() {
+ local address="${1}"
+
+ local int=0
+ for field in ${address//./ }; do
+ int=$(( $(( ${int} << 8 )) | ${field} ))
+ done
+
+ echo ${int}
+}
+
+function ip_in_subnet() {
+ local address="${1}"
+ local subnet="${2}"
+
+ local netmask="${subnet#*/}"
+
+ # Convert netmask to prefix if necessary
+ case "${netmask}" in
+ [0-9]+)
+ ;;
+ *)
+ netmask="$(netmask2prefix "${netmask}")"
+ ;;
+ esac
+
+ local vlsm=$(( -1 << $(( 32 - ${netmask} )) ))
+
+ [ "$(( $(ip_encode "${address}") & ${vlsm} ))" -eq "$(( $(ip_encode "${subnet%/*}") & ${vlsm} ))" ]
+}
+
+netmask2prefix() {
+ local netmask="${1}"
+ local mask="$(ip_encode "${netmask}")"
+
+ local cidr=0
+ local x="$(( 128 << 24 ))" # 0x80000000
+
+ while [ $(( ${x} & ${mask} )) -ne 0 ]; do
+ [ ${mask} -eq ${x} ] && mask=0 || mask=$(( ${mask} << 1 ))
+ cidr=$(( ${cidr} + 1 ))
+ done
+
+ echo "${cidr}"
+}
+
main() {
# Register local variables
local "${VARS[@]}"
local interfaces=()
+ # Flush IPsec routes
+ ip route flush table "${ROUTE_TABLE}"
+
+ # Remove lookups
+ ip rule del lookup "${ROUTE_TABLE}"
+
# We are done when IPsec is not enabled
if [ "${ENABLED}" = "on" ]; then
+ # Enable route table lookup
+ ip rule add lookup "${ROUTE_TABLE}" prio "${ROUTE_TABLE_PRIO}"
+
while IFS="," read -r "${VARS[@]}"; do
# Check if the connection is enabled
[ "${status}" = "on" ] || continue
local intf="${interface_mode}${id}"
;;
*)
+ # Install routes
+ local address
+
+ local _address
+ for _address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do
+ local leftsubnet
+ for leftsubnet in ${leftsubnets//\|/ }; do
+ if ip_in_subnet "${_address}" "${leftsubnet}"; then
+ address="${_address}"
+ break
+ fi
+ done
+
+ # End loop when address is set
+ [ -n "${address}" ] && break
+ done
+
+ local rightsubnet
+ for rightsubnet in ${rightsubnets//\|/ }; do
+ # Ignore default
+ case "${rightsubnet}" in
+ 0.0.0.0/*)
+ continue
+ ;;
+ esac
+
+ log "Creating route to ${rightsubnet} (via ${address} and ${RED_INTF})"
+ ip route add table "${ROUTE_TABLE}" "${rightsubnet}" proto static \
+ dev "${RED_INTF}" src "${address}"
+ done
+
+ # No interface processing required
continue
;;
esac
# Add the interface to the list of all interfaces
interfaces+=( "${intf}" )
+ # Compat for older connections
+ if [ "${local}" = "off" ]; then
+ if [ "${VPN_IP}" = "%defaultroute" ]; then
+ local=""
+ else
+ local="${VPN_IP}"
+ fi
+ fi
+
+ # Handle %defaultroute
+ if [ -z "${local}" ]; then
+ if [ -r "/var/ipfire/red/local-ipaddress" ]; then
+ local="$(</var/ipfire/red/local-ipaddress)"
+
+ elif [ "${RED_TYPE}" = "STATIC" -a -n "${RED_ADDRESS}" ]; then
+ local="${RED_ADDRESS}"
+ fi
+ fi
+
+ # Resolve any hostnames
+ if [[ ! ${remote} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ remote="$(resolve_hostname "${remote}")"
+ fi
+
local args=(
- "local" "${VPN_IP}"
- "remote" "${righthost}"
- "ttl" "255"
+ "local" "${local}"
+ "remote" "${remote}"
)
- # Add key for VTI
- if [ "${interface_mode}" = "vti" ]; then
- args+=( key "${id}" )
- fi
+ case "${interface_mode}" in
+ gre)
+ # Add TTL
+ args+=( "ttl" "255" )
+ ;;
+
+ vti)
+ # Add key for VTI
+ args+=( "key" "${id}" )
+ ;;
+ esac
# Update the settings when the interface already exists
if [ -d "/sys/class/net/${intf}" ]; then
# Delete all other interfaces
local intf
- for intf in /sys/class/net/gre* /sys/class/net/vti*; do
+ for intf in /sys/class/net/gre[0-9]* /sys/class/net/vti[0-9]*; do
intf="$(basename "${intf}")"
# Ignore a couple of interfaces that cannot be deleted
log "Deleting interface ${intf}"
ip link del "${intf}" &>/dev/null
done
+
+ # (Re-)Apply all static routes
+ /etc/init.d/static-routes start
}
main || exit $?