#!/bin/bash

MAP64="/var/cache/ya-tun64/map64_rndsrc.json"
MAP64CACHED="/var/cache/ya-tun64/map64_rndsrc.json.cache"
MAP64URL="https://ro.racktables.yandex.net/export/map64_rndsrc.json"
TUN64_IFACE="tun64"
TUN64_PEER="2a02:6b8:b010:a0ff::1"

curl --ipv6 --max-time 15 --output $MAP64 --silent --show-error --url $MAP64URL
curlrc=$?

jq . $MAP64 >/dev/null 2>&1
jqrc=$?

if [[ $curlrc -ne 0 || $jqrc -ne 0 ]]; then # try to use cached map64
    if [ -f $MAP64CACHED ] && jq . $MAP64CACHED >/dev/null 2>&1; then
        cp $MAP64CACHED $MAP64
    else
        echo "Unable nor to get new valid map64 file, nor to use cached version."
        exit 1
    fi
fi

# check if mapping exists for my FQDN
fqdn="$(hostname -f)"
ipv4addr=$(jq 'to_entries[] | select(.value.fqdn=="'$fqdn'" and .value.scheme=="tun64") | .key' $MAP64 | tr -d '"')

if [ -z "$ipv4addr" ]; then # no mapping found for my FQDN, do nothing
    exit 0
fi

if ! [[ $ipv4addr =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    echo "Got invalid IPv4 address: $ipv4addr"
    exit 1
fi

# ya_check_real_interface <interface>
#   Checks that interface is not loopback, tap, tun or other additional interface.
#
ya_check_real_interface()
{
    local _iface
    if [ ${#} -lt 1 -o -z "${1}" ] ; then
        return 2
    fi

    _iface=${1}
    # delete trailing numbers and downcase letters
    _iface="$(echo ${_iface%%[0-9]*} | tr '[:upper:]' '[:lower:]')"

    if [ -z ${_iface} ] ; then
         return 1
     fi

    case ${_iface} in
    lo|vlan|tun|ip6tnl|tap|vif|ipfw|pflog|virbr|plip|dummy)
        return 1
        ;;
    --all|all)
        return 1
        ;;
    esac

    return 0
}

# ya_get_active_interface <interface>
#   Get active interface:
#       - interface must be as a default gateway.
#       - interface must has a real name.
#
ya_get_active_interface()
{
    local _ifaces _iface
    _iface=''
    _ifaces=""
    _ifaces=$(ip -4 route list exact 0.0.0.0/0 scope global | grep -v 'dev tun' | grep -oP 'dev\s+\K\w+')
    _ifaces="$_ifaces $(ip -6 route list exact ::/0 scope global | grep -oP 'dev\s+\K\w+')"
    _ifaces=$(echo $_ifaces | tr " " "\n" | uniq)

    if [ ! -z "$_iface" ] ; then
        _ifaces=$(echo $_ifaces | grep -o $_iface)
    fi

    for _iface in $_ifaces ; do
        if ya_check_real_interface $_iface ; then
            echo $_iface
            return
        fi
    done

    echo -n ''
    return
}

# ya_get_ip6addr <iface>
#   Print global IPv6 address of specified interface.
#   If address not found or interface specifed 'none' will be returned.
#
ya_get_ip6addr()
{
    local _iface _ip

    if [ ${#} -ne 1 ] ; then
        echo 'none'
        return 1
    fi

    _iface=${1}
    _ip=$(ip -o -6 addr show scope global dev ${_iface} |\
            awk '! / (fd|fc).*/ && ! /temporary/ {sub(/\/.*/, "", $4); print $4; exit}')

    if [ -z "${_ip}" ] ; then
        echo "none"
        return 2
    fi

    echo "${_ip}"
    return 0
}


_iface=$(ya_get_active_interface)
ipv6addr=$(ya_get_ip6addr $_iface)

if [ -z $ipv6addr ]; then
    echo "Unable to get local usable IPv6 address."
    exit 1
fi

# delete tunnel interface if it exists
ip -6 tunnel show $TUN64_IFACE >/dev/null 2>&1 && ip -6 tunnel del $TUN64_IFACE
# delete tun0 if it is bad
ip -6 tunnel show tun0 | grep -q 'local ::1' && ip -6 tunnel del tun0

ip -6 tunnel add $TUN64_IFACE mode ipip6 remote $TUN64_PEER local $ipv6addr
if [ $? -ne 0 ]; then
    echo "Unable to create new tunnel interface $TUN64_IFACE."
    exit 1
fi

ip address add $ipv4addr/32 dev $TUN64_IFACE
if [ $? -ne 0 ]; then
    echo "Unable set address $ipv4addr on $TUN64_IFACE."
    exit 1
fi

ip link set up dev $TUN64_IFACE
if [ $? -ne 0 ]; then
    echo "Unable to set tunnel interface $TUN64_IFACE up."
    exit 1
fi

ip route add 0/0 dev $TUN64_IFACE mtu 1450 advmss 1410
if [ $? -ne 0 ]; then
    echo "Unable to set default route via $TUN64_IFACE."
    exit 1
else
    echo "$TUN64_IFACE is up. Add tun0 iface"
    ip -6 tunnel add tun0 mode ipip6 remote 2a02:6b8:0:3400::aaaa local $ipv6addr
    ip link set dev tun0 mtu 8910 up
    ip link set dev ip6tnl0 mtu 8910 up
fi

sysctl -e -q -w net.ipv4.conf.all.rp_filter=0
sysctl -e -q -w net.ipv4.conf.default.rp_filter=0
for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do
    echo 0 > $i ;
done
sysctl -e -q -w net.ipv4.conf.all.accept_redirects=0
sysctl -e -q -w net.ipv4.conf.all.send_redirects=0

# Success, let's cache good map64 file
cp $MAP64 $MAP64CACHED
