#!/bin/sh

AUTOMODE='on'
_TRAFFICLOCK='/var/lock/iep-traffic.lock'
_BASELOCK='/var/lock/iep_traffic_macnat_'
_NATHOSTS='/var/lib/3d-p/iep-traffic/ns.d'

help() {

    cat <<EOF
`basename ${0}` is an tool to faciliate setting up macnat for client bridging.
Use of the commands below:

iep-macnat-do up <bridge> <wlan interface>
    start the macnat service for a specified <bridge> with and use
    <wlan interface> as the nat interface. no checks are performed on
    the bridge and interface names.

iep-macnat-do down <bridge> <wlan interface>
    stop the macnat service for a specified <bridge> with and use
    <wlan interface> as the nat interface. No checks are performed on
    the bridge and interface names.

iep-macnat-do stop
    stop all macnat related services

iep-macnat-do ns
    show namespaces

iep-macnatd-do hosts     <namespace>
iep-macnatd-do add-host  <namespace> <mac> <ip4>
iep-macnatd-do del-host  <namespace> <mac> <ip4>
iep-macnatd-do del-hosts <namespace>
    show, add, delete known host pairs for a given namespace.
    Namespaces must be in the range [a-z]
    Added hosts will be automatically added to client bridging setups.


OPTIONS:

    --auto=on|off   enables or disables macnat auto mode on up

EOF

}


check_args() {
    [ "${1}" -le "${2}" ] || {
        echo 'missing args'
        return 1
    }
    return 0
}


check_namespace() {
    r="$(echo "${1}" | tr -c -d [:alnum:])"
    [ "${r}" = "${1}" ] || {
        echo 'bad namespace. ${1}; namespace must be alpha numeric'
        return 1
    }
    return 0
}


up_trafficd() {
    trafficpid="$(cat ${_TRAFFICLOCK} 2>/dev/null)"
    if [ ! -z ${trafficpid} ] && [ -e "/proc/${trafficpid}" ]; then
        kill -s USR1 ${trafficpid}
    else
        iep-trafficd >/dev/null 2>/dev/null &
    fi
    return 0
}


down_trafficd() {
    trafficpid="$(cat ${_TRAFFICLOCK} 2>/dev/null)"
    if [ ! -z ${trafficpid} ] && [ -e "/proc/${trafficpid}" ]; then
        kill -s USR2 ${trafficpid}
    fi
    return 0
}


is_macnatd_running() {
    macnatpid="$(cat ${_BASELOCK}${1}_${2}.lock 2>/dev/null)"
    if [ ! -z "${macnatpid}" ] && [ -e "/proc/${macnatpid}" ]; then
        return 0
    fi
    return 1
}

up_macnatd() {
    br="${1}"
    iface="${2}"
    shift
    shift
    iep-traffic-macnatd $@ -h "${_NATHOSTS}/" -b ${br} -w ${iface} >/dev/null 2>/dev/null &
    return 0
}

down_macnatd() {
    [ ! -e "${_BASELOCK}${1}_${2}.lock" ] && return 0
    kill -s INT "$(cat ${_BASELOCK}${1}_${2}.lock)"
    return 0
}


command_up() {
    echo 'RESUMING macnatd' $@

    if is_macnatd_running ${1} ${2}; then
        return 0
    fi

    if [ "${AUTOMODE}" = "on" ]; then
        echo "up trafficd"
        up_trafficd
        echo "up macnatd"
        up_macnatd ${1} ${2}
    else
        echo "up macnatd noauto"
        up_macnatd ${1} ${2} "--noauto"
    fi

    return 0
}


command_down() {
    echo 'PAUSING macnat ' $@
    down_macnatd ${1} ${2}
    down_trafficd
}

command_stop() {
    echo 'STOPPING macnat ' $@
    for item in $(ls ${_BASELOCK}*.lock 2>/dev/null); do
        kill -s INT "$(cat ${item})"  2>/dev/null
    done
    kill -s INT "$(cat ${_TRAFFICLOCK})" 2>/dev/null
}

command_ns() {
    all_namespaces=''
    for item in $(ls ${_NATHOSTS}/*.ns 2>/dev/null); do
        p="$(echo ${item} | cut -d_ -f1)"
        p="$(basename ${p})"
        m=''
        for ns in ${all_namespaces}; do
            if [ "${p}" = "${ns}" ]; then
                m='y'
                break
            fi
        done
        if [ -z "${m}" ]; then
            all_namespaces="${p}"
            echo ${p}
        fi
    done
    return 0
}

command_hosts() {
    cat ${_NATHOSTS}/${1}_*.ns 2>/dev/null
    return 0
}

command_add_host() {
   echo 'ADDING HOST {'${2}','${3}'} IN '${1}
   lname="$( echo "${1}_${2}${3}" | tr -d [:.,] )"
   echo "${2},${3}" > "${_NATHOSTS}/${lname}.ns" || return 1
   return 0
}

command_del_host() {
    echo 'REMOVING HOST {'${2}','${3}'} FROM '${1}
    lname="$( echo "${1}_${2}${3}" | tr -d [:.,] )"
    rm -f "${_NATHOSTS}/${lname}.ns"
    return 0
}

command_del_hosts() {
    echo 'REMOVING ALL HOSTS in' ${1}
    lname="$( echo "${1}_" | tr -d [:.,] )"
    rm -f ${_NATHOSTS}/${lname}*.ns
    return 0
}



#===========
# MAIN
#===========

if [ ${#} -lt 1 ]; then
    echo 'Please specifiy an action'
    exit 0
fi

SHORTOPTS='a:h'
LONGOPTS="auto:,help"
ARGS=$(getopt -s bash --options ${SHORTOPTS} --longoptions ${LONGOPTS} -- "$@" )
eval set -- "$ARGS"
while true; do
    case $1 in
        -a|--auto) shift; AUTOMODE="$1"; ;;
        -h|--help) help; exit 0 ;;
        --)        shift; break ;;
        *)         break ;;
    esac
    shift
done

case $AUTOMODE in
    "on"|"ON"|"yes"|"YES"|"y"|"Y") AUTOMODE="on";;
    *) AUTOMODE="off" ;;
esac

mkdir -p ${_NATHOSTS}


command="${1}"
shift

case ${command} in
    "up")
        check_args 2 ${#} || exit 1
        
        readonly _sleep_time=0.1
        _sleep_cycles=100
        while true; do
                if [ -e "/sys/class/net/$1" ] && [ "$(cat "/sys/class/net/$1/carrier")" -eq 1 ]; then
                        #bridge is online
                        if [ -e "/sys/class/net/$2" ] && [ "$(cat "/sys/class/net/$2/carrier")" -eq 1 ]; then
                                #link is online
                                break #okay to proceed
                        fi
                fi
                if [ $_sleep_cycles -eq 0 ]; then
                        logger "$0: network devices ($1, $2) were not prepared in time; iep-traffic may not work as expected"
                        break
                fi
                
                sleep $_sleep_time
                _sleep_cycles=$((_sleep_cycles - 1))
        done
        
        command_up $@ || exit 1
        exit 0
        ;;

    "down")
        check_args 2 ${#} || exit 1
        command_down $@ || exit 1
        exit 0
        ;;

    "stop")
        command_stop || exit 1
        exit 0
        ;;

    "ns")
        command_ns || exit 1
        exit 0
        ;;

    "hosts")
        check_args 1 ${#} || exit 1
        check_namespace ${1} || exit 1
        command_hosts $@ || exit 1
        exit 0
        ;;

    "add-host")
        check_args 3 ${#} || exit 1
        check_namespace ${1} || exit 1
        command_add_host $@ || exit 1
        exit 0
        ;;

    "del-host")
        check_args 3 ${#} || exit 1
        check_namespace ${1} || exit 1
        command_del_host $@ || exit 1
        exit 0
        ;;

    "del-hosts")
        check_args 1 ${#} || exit 1
        check_namespace ${1} || exit 1
        command_del_hosts $@ || exit 1
        exit 0
        ;;

    *)
        echo "command ${command} is invalid"
        exit 1
        ;;
esac

exit 0
