#!/bin/sh
#
# Opennet Firmware
# 
# Copyright 2010 Rene Ejury <opennet@absorb.it>
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
# 

# include helper functions
. /usr/bin/on-helper.sh

# retrieve DEBUG-state
DEBUG=$(uci -q get on-core.defaults.debug)

#################################################################################
# if ugw has some broken MTU settings, traffic will not go through the gateway
# even if olsrd or icmp-packages are working well. Therefore check MTU before
# recognizing a ugw as working...
# $1=nr
ugw_checkMTU () {
    if [ "$(uci -q get on-usergw.opennet_ugw$1.wan)" != "ok" ]; then
        $DEBUG && logger -t on_usergateway_check "ugw_checkMTU for ugw$1 skipped, please check WAN first"
        return;
    fi
    
    remote=$(uci -q get openvpn.opennet_ugw$1.remote)
    rport=$(uci -q get openvpn.opennet_ugw$1.rport)
    ca=$(uci -q get openvpn.opennet_ugw$1.ca)
    cert=$(uci -q get openvpn.opennet_ugw$1.cert)
    key=$(uci -q get openvpn.opennet_ugw$1.key)
    openvpn_ugw_test_parameters="--mtu-test 1 --remote $remote $rport --tls-client --ns-cert-type server \
                    --ca $ca --cert $cert --key $key --comp-lzo $complzo --dev tap --inactive 10 --nice 3 \
                    --resolv-retry 0 --ping-exit 25 --ping-restart 0 --comp-lzo yes --tls-exit"

    $DEBUG && echo $openvpn_ugw_test_parameters >/tmp/openvpn_ugw_test_parameters     # to check test-parameters
    $DEBUG && logger -t on_usergateway_check "starting ugw_checkMTU for ugw$1, $remote"
    $DEBUG && logger -t on_usergateway_check "ugw_checkMTU will take around 5 minutes per gateway"
    
    openvpn $openvpn_ugw_test_parameters | \
    awk '
        BEGIN { FS="[\]\[,]" }
        /MTU test completed/ {
            system("kill 2>/dev/null $(ps|grep \"openvpn --mtu-test 1\" |grep -v grep|cut -b 0-5)");
            if ($5 <= $6 && $8 <= $9) {
                system("uci -q set on-usergw.opennet_ugw"'$1'".mtu=ok")
            }
            else {
                system("uci -q set on-usergw.opennet_ugw"'$1'".mtu=error")
            }
            system("uci -q set on-usergw.opennet_ugw"'$1'".mtu_msg=\""$0"\"")
            system("uci -q set on-usergw.opennet_ugw"'$1'".mtu_toGW_tried=\""$5"\"")
            system("uci -q set on-usergw.opennet_ugw"'$1'".mtu_toGW_actual=\""$6"\"")
            system("uci -q set on-usergw.opennet_ugw"'$1'".mtu_fromGW_tried=\""$8"\"")
            system("uci -q set on-usergw.opennet_ugw"'$1'".mtu_fromGW_actual=\""$9"\"")
        }'
    mtu=$(uci -q get on-usergw.opennet_ugw$1.mtu)
    mtu_msg=$(uci -q get on-usergw.opennet_ugw$1.mtu_msg)
    $DEBUG && logger -t on_usergateway_check "$mtu: ugw_checkMTU for ugw$1, $remote done"
    $DEBUG && logger -t on_usergateway_check "$mtu: $mtu_msg"
}

#################################################################################
# check all stored Usergateways for working MTU
ugw_checkMTUs () {
    nr=1; while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do ugw_checkMTU $((nr++)); done;
}

#################################################################################
# check if route to UGW goes trough WAN. if so, add config
# olsr put's it's routes in a separate table, therefore main table has only locally received routes
# $1=nr
ugw_checkWAN () {
    on_ugw=$(uci -q get openvpn.opennet_ugw$1.remote)
    on_ugw_ip=$(query_dns $on_ugw)
    uci -q set on-usergw.opennet_ugw$1.ipaddr=$on_ugw_ip    # store or remove stored IP
    if [ -n "$on_ugw_ip" ]; then
        on_ugw_targetDev=$(ip route get $on_ugw_ip | awk '{if ($4 == "dev") print $5}')
        wandev=$(uci get network.wan.ifname | cut -f1 -d:)
        wan="error"; wanping="error"
        if [ "$on_ugw_targetDev" = "$wandev" ]; then
            wan="ok"
            if $(ping -c 1 $on_ugw_ip >/dev/null 2>/dev/null); then wanping="ok"; fi
        fi

        $DEBUG && logger -t on_usergateway_check "checkWAN: $on_ugw routing through wan device: $wan"
        uci -q set on-usergw.opennet_ugw$1.wan=$wan
        uci -q set on-usergw.opennet_ugw$1.wanping=$wanping
    else
        $DEBUG && logger -t on_usergateway_check "checkWAN: can't resolve $on_ugw, ignoring"
    fi
}

#################################################################################
# check all stored Usergateways for working MTU
ugw_checkWANs () {
    nr=1; while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do ugw_checkWAN $((nr++)); done;
}

#################################################################################
# param is ugw-tunnel to shut down
# init-script requires enable to be active while shutting down
# $1=nr
ugw_shutDown () {
    if [ -e /var/run/openvpn-opennet_ugw$1.pid ]; then
        $DEBUG && logger -t on_usergateway_check "disabling tunnel opennet_ugw$1"
        oldenable=$(uci -q get openvpn.opennet_ugw$1.enable)
        uci -q set openvpn.opennet_ugw$1.enable=1
        /etc/init.d/openvpn down opennet_ugw$1
        uci -q set openvpn.opennet_ugw$1.enable=$oldenable
        uci commit openvpn
    fi
}

#################################################################################
# removes config and changes numbering of all other ugw-configs
# results of ugw-checking in on-usergw will be cleaned
# all changed usergw will be shut down (and canbe later restarted)
# $1=nr
ugw_delConfigByNr () {
    $DEBUG && logger -t on_usergateway_check "started ugw_delConfigByNr $1"
    current=$1
    ugw_shutDown $current
    uci -q delete openvpn.opennet_ugw$current
    uci -q delete on-usergw.opennet_ugw$current
    # check if higher-numbered ugw's exist, if so move them to close the gap
    while ( [ -n "$(uci -q get openvpn.opennet_ugw$((current+1)))" ] ); do
        next=$((current+1))
        uci -q set openvpn.opennet_ugw$current=openvpn
        eval $(uci -q show openvpn.opennet_ugw$next | awk 'BEGIN{FS="="} {gsub("ugw'$next'","ugw'$current'"); print "uci -q set "$1"=\""$2"\";"}')
        uci -q set on-usergw.opennet_ugw$current="usergateway"
        ugw_shutDown $next
        uci -q delete openvpn.opennet_ugw$next
        uci -q delete on-usergw.opennet_ugw$next
        : $((current++));
    done
    uci commit openvpn
    uci commit on-usergw
}

#################################################################################
# copy the preset-config from on-usergw to openvpn
# $1=[add|del] $2=on_ugw
ugw_openvpnConfig () {
    nr=1;
    if [ "$1" = "add" ]; then
        while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do
            if [ "$(uci -q get openvpn.opennet_ugw$nr.remote)" = "$2" ]; then return; fi    # if found don't add again
            : $((nr++));
        done;
        # add new usergateway config
        uci -q set openvpn.opennet_ugw$nr=openvpn
        # duplicate default config from presets
        eval $(uci show on-usergw.opennet_ugw | awk 'BEGIN{FS="[\.=]"} {printf "uci -q set openvpn.opennet_ugw"'$nr'"."$3"=\""$4; if ($5 != "") { printf"."$5; }; print "\";"}')
        uci -q set openvpn.opennet_ugw$nr.remote=$2
        uci -q set on-usergw.opennet_ugw$nr="usergateway"
        uci commit openvpn
        uci commit on-usergw
        $DEBUG && logger -t on_usergateway_check "added openvpn config for ugw$nr ($2)"
    else
        while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do
            remote=$(uci -q get openvpn.opennet_ugw$nr.remote);
            if [ "$2" != "" ] && [ "$2" != "$remote" ]; then
                : $((nr++)); continue;
            else
                ugw_delConfigByNr $nr
                $DEBUG && logger -t on_usergateway_check "removed openvpn config for ugw$nr ($remote)"
            fi
        done;
    fi
}

#################################################################################
# syncs config from on-usergw to openvpn
ugw_syncConfig () {
    # parse current OpenVPN-config for leftover former usergateways
    nr=1;
    while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do
        remote=$(uci -q get openvpn.opennet_ugw$nr.remote);
        found="";
        for on_ugw in $(uci get on-usergw.@usergw[0].name); do
            if [ "$remote" = "$on_ugw" ]; then found="true"; break; fi
        done;
        if [ -n "$found" ]; then
            : $((nr++));
        else
            ugw_delConfigByNr $nr
        fi
    done;
    # now check, if all usergateways have a config in openvpn
    for on_ugw in $(uci get on-usergw.@usergw[0].name); do
        found="";
        nr=1;
        while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do
            remote=$(uci -q get openvpn.opennet_ugw$nr.remote);
            if [ "$remote" = "$on_ugw" ]; then found="true"; break; fi
            : $((nr++));
        done;
        if [ -z "$found" ]; then ugw_openvpnConfig add $on_ugw; fi
    done;
}

#################################################################################
# check if sharingInternet is temporarily blocked
# if so, reduce blocking time by 5 (minutes)
ugw_checkSharingBlocked () {
    on_share_internet_blocked=$(uci -q get on-usergw.ugw_sharing.blockSharing)
    if [ -n "$on_share_internet_blocked" ]; then
        uci set on-usergw.ugw_sharing.blockSharing=$(($(uci get on-usergw.ugw_sharing.blockSharing) - 5))

        # if counter reaches zero, ugw is re-activated
        if [ "$on_share_internet_blocked" -le "0" ]; then
            uci delete on-usergw.ugw_sharing.blockSharing
            uci set on-usergw.ugw_sharing.shareInternet='on'
        elif [ "$(uci -q get on-usergw.ugw_sharing.shareInternet)" = "on" ]; then
            uci set on-usergw.ugw_sharing.shareInternet='off'
        fi
    fi
}

#################################################################################
# check if sharingInternet is enabled and tunnel's are active or vice versa
ugw_shareInternet () {
    sharing="$(uci -q get on-usergw.ugw_sharing.shareInternet)"
    nr=1;
    while ( [ -n "$(uci -q get openvpn.opennet_ugw$nr)" ] ); do
        mtu=$(uci -q get on-usergw.opennet_ugw$nr.mtu)
        wan=$(uci -q get on-usergw.opennet_ugw$nr.wan)
        enable=$(uci -q get openvpn.opennet_ugw$nr.enable)

        if [ "$mtu" = "ok" ] && [ "$wan" = "ok" ] && [ "$enable" = "0" ]; then
            uci -q set openvpn.opennet_ugw$nr.enable=1; uci commit openvpn;
        elif ( [ "$mtu" != "ok" ] || [ "$wan" != "ok" ] ) && [ "$enable" = "1" ]; then
            uci -q set openvpn.opennet_ugw$nr.enable=0; uci commit openvpn;
        fi

        # reread enable-setting
        enable=$(uci -q get openvpn.opennet_ugw$nr.enable)

        if [ ! -e /var/run/openvpn-opennet_ugw$nr.pid ] && [ "$sharing" = "on" ] && [ "$enable" = "1" ]; then
            $DEBUG && logger -t on_usergateway_check "activating tunnel opennet_ugw$nr"
            /etc/init.d/openvpn up opennet_ugw$nr
        elif [ -e /var/run/openvpn-opennet_ugw$nr.pid ] && ( [ "$sharing" = "off" ] || [ "$enable" = "0" ] ); then
            ugw_shutDown $nr    # it will restore former 'enable'-value
        fi
        : $((nr++));
    done;
}

#################################################################################
# start here - with the menu
case "$1" in
    "--help")           echo -e "\
Usage: on_usergateway_check [OPTION]...
    usually called by cron every 5 minutes
Options:
  syncConfig        transfer UGW config from on-usergw to openvpn
  checkWan          check if routes to UGW go through WAN-device
  checkMtu          check if MTU of WAN-UGW-Tunnel is ok
                    this test will last ~5 minutes per tunnel
                    results will be stored in on-usergw config
  shareInternet     start UGW-tunnels if MTU and WAN ok and sharing is enabled
  cleanConfig       removes all on-usergw config from openvpn config
  --help              this help"; exit 0; ;;
esac

if [ -e /var/run/on_usergateway_check ]; then
    $DEBUG && logger -t on_usergateway_check "parallel instance running, exit"
    exit;
fi
echo "running" >/var/run/on_usergateway_check
$DEBUG && logger -t on_usergateway_check "starting another instance"

case "$1" in
    "syncConfig")       ugw_syncConfig; ;;
    "checkWan")         ugw_checkWANs; ;;
    "checkMtu")         ugw_checkMTUs; ;;
    "shareInternet")    ugw_checkSharingBlocked; ugw_shareInternet; ;;
    "cleanConfig")      ugw_openvpnConfig del; ;;
    *)                  ugw_syncConfig; ugw_checkWANs; ugw_checkSharingBlocked; ugw_shareInternet; ;;
esac

#################################################################################
# commit changes, otherwise they will slow down the whole system significantly
uci commit on-usergw

rm /var/run/on_usergateway_check
$DEBUG && logger -t on_usergateway_check "finishing another instance"