#!/usr/bin/env bash

CONFIG_PATH="/etc/network/interfaces.d"
MAC_URL="169.254.169.254/2009-04-04/meta-data/network/interfaces/macs"

NET_CLOUD_CFG="/etc/network/interfaces.d/50-cloud-init.cfg"
CLOUDINIT_CFG="/etc/cloud/cloud.cfg"
RT_TABLES="/etc/iproute2/rt_tables"
PRIMARY_NIC=
DEFAULT_EIP_NIC=
DEFAULT_EIP=
DEFAULT_EIP_GATEWAY=
EIP_OPT=
SCRIPT_VERSION="3.0.20260323"
declare -A nametoprimaryip
primary_ip_cahce=
ipv4_sed_index=3
declare -A nametomac
mac_address_cache=
ONLY_CONFIG_IPv4=0
ONLY_CONFIG_IPv6=0

function netmask2cidr() { 
    local mask=$1
    if [[ $mask =~ ":" ]]; then
        # If the mask is IPv6
        local bin_mask=$(echo $mask | tr -d ':' | xxd -r -p | xxd -b -c 16 | tr -d ' \n')
        local count_segment=0
        for ((i=0; i<${#bin_mask}; i++)); do
            if [[ "${bin_mask:$i:1}" == "1" ]]; then
                ((count_segment++))
            fi
        done
        echo $count_segment
    else
        # If the mask is IPv4
        awk -F. '{
        split($0, arrSep)
        for (i in arrSep) {
            mask += int(8 - log(2^8 - arrSep[i])/log(2));
        }
        print mask
        }' <<< $mask
    fi
}
function get_MTU(){  
    name=$1
    get_mac_address $name
    mac=$mac_address_cache
    mtu=$(curl -s "$MAC_URL/$mac/mtu")
    echo $mtu
} 

# disable cloud-init's network configuration capabilities
function disable_cloud_network(){
    grep -i -q "network: {config: disabled}" $CLOUDINIT_CFG
    if [[ "$?" -ne 0 ]]; then
        echo "" >> $CLOUDINIT_CFG
        echo "network: {config: disabled}" >> $CLOUDINIT_CFG
    fi
}

function disable_networkmanager(){
    version=$1
    echo "  Disable NetworkManager."
    if [[ "$version" == "7" ]]; then
        #/etc/init.d/network-manager stop >/dev/null 2>&1
        service network-manager stop >/dev/null 2>&1
        update-rc.d -f network-manager remove >/dev/null 2>&1
        #apt-get --purge remove -y network-manager >/dev/null 2>&1
    else
        systemctl stop NetworkManager >/dev/null 2>&1
        systemctl disable NetworkManager >/dev/null 2>&1
    fi
}
# reset interfaces config file
function modify_interfaces_file(){
    cat > /etc/network/interfaces  << EOF
auto lo
iface lo inet loopback

source /etc/network/interfaces.d/*
EOF
    if [[ ! -d "$CONFIG_PATH" ]]; then
        mkdir $CONFIG_PATH
    fi
}

function get_mac_address(){
    name=$1
    if [[ -z ${nametomac[$name]} ]]; then
        mac_address_tmp=$(cat /sys/class/net/$name/address)
        mac_address=$(curl -s "$MAC_URL" | grep -iE "$mac_address_tmp" | sed 's/\///g')
        nametomac[$name]=$mac_address
        mac_address_cache=$mac_address
    else
        mac_address_cache=${nametomac[$name]}
    fi
}

function get_primary_nic(){
    nics=$(ls /sys/class/net/ | grep "^eth")
    for name in ${nics[*]}
    do
        get_mac_address $name
        mac_addr=$mac_address_cache
        eni_type=$(curl -f -s "$MAC_URL/$mac_addr/eni_type")
        if [[ "$eni_type" == "primary" ]]; then
            echo $name
            break
        fi
    sleep 1 
    done
}


function get_meta_value(){
    name=$1
    ip=$2
    item=$3
    get_mac_address $name
    mac=$mac_address_cache
    ret=0
    for ((i=1; i<=3; i++)); do
        value=$(curl -f -s $MAC_URL/$mac/fixed_ips/$ip/$item)
        ret="$?"
        if [[ "$ret" -eq 0 ]]; then
            break
	    fi
        sleep 1
    done
    echo $value
}

function create_net_config(){
    name=$1
    mac=$2
    local primary_ip
    local netmask
    if [[ "${name}" != "${PRIMARY_NIC}" ]]; then
        get_primary_ip "${name}"
        primary_ip=$primary_ip_cahce
        netmask=$(curl -s "${MAC_URL}/${mac}/fixed_ips/${primary_ip}/mask")
        cat  > "$CONFIG_PATH/${name}.cfg" << EOF
auto ${name}
iface ${name} inet static
hwaddress ether ${mac}
address ${primary_ip}
netmask ${netmask}
mtu $(get_MTU $name)
EOF
    else
        cat  > "$CONFIG_PATH/${name}.cfg" << EOF
auto $name
iface $name inet dhcp
hwaddress ether $mac
mtu $(get_MTU $name)
EOF
    fi
}

function restrict_default_route(){
    name=$(get_primary_nic)
    cat > /etc/dhcp/dhclient-enter-hooks.d/restrict-default-route << EOF
case \${interface} in
  $name)
    ;;
  *)
    unset new_routers
    ;;
esac
EOF
}

function get_primary_ip(){
    name=$1
    if [[ -z ${nametoprimaryip[$name]} ]]; then
        get_mac_address $name
        mac=$mac_address_cache
        primary_ip=$(curl -s "$MAC_URL/$mac/primary_ipv4_addr")
        nametoprimaryip[$name]=$primary_ip
        primary_ip_cahce=$primary_ip
    else
        primary_ip_cahce=${nametoprimaryip[$name]}
    fi
}

function is_eip_direct_enabled() {
    local mac=$1
    local ip=$2
    eip_direct=$(curl -s "${MAC_URL}/${mac}/fixed_ips/${ip}/eip_direct")
    if [ "$eip_direct"x = "true"x ]; then
        return 0
    fi
    return 1
}

function config_eip_direct() {
    local name=$1
    local mac=$2
    local ip=$3
    local index=$4
    local eip=$(curl -s "${MAC_URL}/${mac}/fixed_ips/${ip}/eip")
    local eip_gateway=$(curl -s "${MAC_URL}/${mac}/fixed_ips/${ip}/gateway")
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        cat >> $CONFIG_PATH/${name}.cfg << EOF

iface ${name} inet static
address $eip
netmask 255.255.255.255
EOF
    else
        cat >> $CONFIG_PATH/${name}.cfg << EOF

iface ${name} inet6 static
address $eip
netmask 128
EOF
    fi
    if [[ "${EIP_OPT}" = "install" ]]; then
        echo "${DEFAULT_EIP:=${eip}}" > /dev/null
        if [[ "${DEFAULT_EIP}" = "${eip}" ]]; then
            DEFAULT_EIP_NIC="${name}"
            DEFAULT_EIP_GATEWAY="${eip_gateway}"
        fi
    elif [[ "${EIP_OPT}" = "uninstall" ]]; then
        DEFAULT_EIP=""
        DEFAULT_EIP_NIC=""
        DEFAULT_EIP_GATEWAY=""
    fi

}


function config_secondary_ips(){
    name=$1
    mac=$2
    ips=($(curl -s $MAC_URL/$mac/fixed_ips | sed 's/\///g'))
    get_primary_ip "${name}"
    primary_ip=$primary_ip_cahce
    for ip in ${ips[*]}
    do
        if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
            if [ $ONLY_CONFIG_IPv6 -eq 0 ]; then
                if is_eip_direct_enabled "${mac}" "${ip}"; then
                   config_eip_direct "${name}" "${mac}" "${ip}"
                fi

                if [ "$ip" != "$primary_ip" ]; then
                 netmask=$(curl -s "$MAC_URL/$mac/fixed_ips/$ip/mask")
cat >> $CONFIG_PATH/${name}.cfg << EOF

iface $name inet static
address $ip
netmask $netmask
EOF
                fi
            fi
        else
            if [ $ONLY_CONFIG_IPv4 -eq 0 ]; then
                if is_eip_direct_enabled "${mac}" "${ip}"; then
                config_eip_direct "${name}" "${mac}" "${ip}"
                fi
                netmask=$(curl -s "$MAC_URL/$mac/fixed_ips/$ip/mask")
cat >> $CONFIG_PATH/${name}.cfg << EOF

iface $name inet6 static
address $ip
netmask $(netmask2cidr $netmask)
EOF
            fi
        fi
    done

}

# create/recreate nic's config file
function create_nics_config(){
    # get all nics
    nics=$(ls /sys/class/net/ | grep "^eth")
    # remove old elastic nic's config file
    rm -f "${CONFIG_PATH}"/*.cfg
    for name in ${nics[*]}
    do
        get_mac_address $name
        mac=$mac_address_cache
        create_net_config $name $mac
        config_secondary_ips $name $mac
    done
    echo -e "    create/recreate network config file \033[32m[OK]\033[0m"
}

function add_route_table(){
    if command -v wc >/dev/null 2>&1 && command -v tail >/dev/null 2>&1 ; then
        if [ $(tail -c 1 /etc/iproute2/rt_tables | wc -l) -eq 0 ]; then
            echo "Unexpected end of rt_tables" | logger -i -t 'bcc_elastic_net_debian_manual.sh'
            echo >> /etc/iproute2/rt_tables
        fi
    fi
    nics=$(ls /sys/class/net/ | grep "^eth")
    index=10
    for name in ${nics[*]}
    do
        get_primary_ip "${name}"
        primary_ip=$primary_ip_cahce
        netmask=$(get_meta_value $name $primary_ip "mask")
        if [[ -z $netmask ]]; then
            echo -e "\033[31m  can't get the netmask for $primary_ip from the metadata server.\033[0m"
            exit 
        fi

        gateway=$(get_meta_value $name $primary_ip "gateway")
        if [[ -z $gateway ]]; then
            echo -e "\033[31m  can't get the gateway for $primary_ip from the metadata server.\033[0m"
            exit
        fi

        table_name="rt_$name"

        grep -q "^$index $table_name" $RT_TABLES >/dev/null 2>&1
        if [ "$?" -ne 0 ]; then
            echo "$index $table_name" >> $RT_TABLES
        fi

        ip route flush table $table_name >/dev/null 2>&1
        ip -6 route flush table $table_name >/dev/null 2>&1
        ip rule show | grep "${table_name}" | awk '{print $NF}' | xargs -r -L1 ip rule del table
        ip -6 rule show | grep "${table_name}" | awk '{print $NF}' | xargs -r -L1 ip -6 rule del table
        #ip route add default via $gateway dev $name table $table_name >/dev/null 2>&1
        echo "" >> $CONFIG_PATH/${name}.cfg
        sed -i "${ipv4_sed_index}a\\up ip route add default via $gateway dev $name table $table_name" $CONFIG_PATH/${name}.cfg
        let ipv4_sed_index++
        #echo "up ip route add default via $gateway dev $name table $table_name" >> $CONFIG_PATH/${name}.cfg


        get_mac_address $name
        mac=$mac_address_cache
        for ((i=1; i<=3; i++)); do
            all_ips=$(curl -f -s $MAC_URL/$mac/fixed_ips | sed 's/\///g')
            if [[ "$?" -eq 0 ]]; then
                break
            fi
            sleep 1
        done
        if [[ -z "$all_ips" ]]; then
            echo -e "\033[31m  can't get the fixed_ip list of $name from metadata server.\033[0m"
            exit
        fi
        first_ipv6=true
        echo "" >> $CONFIG_PATH/${name}.cfg
        echo "# route for every ip" >> $CONFIG_PATH/${name}.cfg
        for one_ip in ${all_ips[*]}
        do
            
            if [[ $one_ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
                    #echo ipv4
                    if [ $ONLY_CONFIG_IPv6 -eq 0 ]; then
                        sed -i "${ipv4_sed_index}a\\up ip route add $one_ip dev $name table $table_name" $CONFIG_PATH/${name}.cfg
                        let ipv4_sed_index++
                        sed -i "${ipv4_sed_index}a\\up ip rule add from $one_ip lookup $table_name" $CONFIG_PATH/${name}.cfg
                        let ipv4_sed_index++
                    fi
            else
                if [ $ONLY_CONFIG_IPv4 -eq 0 ]; then
                    if [ $first_ipv6 == true ]; then
                        gateway_v6=$(get_meta_value $name $one_ip "gateway")
                        echo "up ip -6 route add default via $gateway_v6 dev $name table $table_name" >> $CONFIG_PATH/${name}.cfg
                        first_ipv6=false
                    fi
                    echo "up ip -6 route add $one_ip dev $name table $table_name" >> $CONFIG_PATH/${name}.cfg
                    echo "up ip -6 rule add from $one_ip lookup $table_name" >> $CONFIG_PATH/${name}.cfg
                fi
            fi

            if is_eip_direct_enabled  "${mac}" "${one_ip}"; then
                local eip=$(curl -s "${MAC_URL}/${mac}/fixed_ips/${one_ip}/eip")
                if [[ $eip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
                    if [ $ONLY_CONFIG_IPv6 -eq 0 ]; then
                        sed -i "${ipv4_sed_index}a\\up ip route add $eip dev $name table $table_name" $CONFIG_PATH/${name}.cfg
                        let ipv4_sed_index++
                        sed -i "${ipv4_sed_index}a\\up ip rule add from $eip lookup $table_name" $CONFIG_PATH/${name}.cfg
                        let ipv4_sed_index++
                    fi
                else
                    if [ $ONLY_CONFIG_IPv4 -eq 0 ]; then
                        echo "up ip -6 route add $eip dev $name table $table_name" >> $CONFIG_PATH/${name}.cfg
                        echo "up ip -6 rule add from $eip lookup $table_name" >> $CONFIG_PATH/${name}.cfg
                    fi
                fi
                #echo -e "    add $one_ip to route table: $table_name \033[32m[OK]\033[0m"
            fi
            #ip route add $one_ip dev $name table $table_name >/dev/null 2>&1
        done

        let index+=10
    done
}

function config_eip_default_route() {
    local default_gateway
    local primary_nic="${PRIMARY_NIC}"
    primary_default_gateway=$(route  -n |awk '{if($1=="0.0.0.0")print $2}')
    default_gateway="${DEFAULT_EIP_GATEWAY}"

    if [[ -n "${DEFAULT_EIP_NIC}" && -n "${DEFAULT_EIP}" && -n "${default_gateway}" ]]; then
        echo -e "\033[31m  Default Route is setting to ${DEFAULT_EIP_NIC}....\033[0m"
        echo -e "\033[31m  You can run $0 uninstall to uninstall eip direction.\033[0m"
        ip route add 10.0.0.0/8 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route add 100.64.0.0/10 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route add 172.16.0.0/12 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route add 192.168.0.0/16 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route add 169.254.0.0/16 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route add 255.255.255.255 dev "${DEFAULT_EIP_NIC}" via "${default_gateway}"
        ip route del default
        ip route add default via "${default_gateway}" dev "${DEFAULT_EIP_NIC}" src "${DEFAULT_EIP}"
    elif [[ -z "${DEFAULT_EIP_NIC}" && -n "${DEFAULT_EIP}" ]]; then
        echo -e "\033[31m  Default Route set failed!!, No NIC for ${DEFAULT_EIP}\033[0m"
    elif [[ "${EIP_OPT}" = "uninstall" ]]; then
        ip route del default
        ip route add default via "${primary_default_gateway}" dev "${primary_nic}"
    fi
}

function parse_args() {
    if [[ "$#" = 0 ]]; then
        return 0
    elif [[ "$#" -gt 2 ]]; then
        echo "Error Parameter!!"
        return 1
    fi

    case "$1" in
        install)
            EIP_OPT="install"
            if [[ "$#" = 2 ]]; then
                DEFAULT_EIP=$2
            fi
            ;;
        uninstall)
            EIP_OPT="uninstall"
            ;;
        onlyipv4)
            echo "only config IPv4"  | logger -i -t 'bcc_elastic_net_debian_manual.sh'
            ONLY_CONFIG_IPv4=1
            ;;
        onlyipv6)
            echo "not config IPv6"  | logger -i -t 'bcc_elastic_net_debian_manual.sh'
            ONLY_CONFIG_IPv6=1
            ;;   
        help|*)
            echo "usage:"
            echo "  $0 [install/uninstall/help] [eip]"
            return 1
            ;;
    esac
    return 0
}

##################################################
# get distro
for file in issue issue.net lsb-release
do
    [[ ! -f /etc/$file ]] && continue
    grep -i -q "debian" /etc/$file
    if [ "$?" -eq 0 ]; then
        distro="debian"
    fi
done

if ! parse_args "$@"; then
    exit 1
fi

if [[ "$distro" == "debian" ]]; then
    version=$(cat /etc/debian_version | awk -F '.' '{print $1}')
    if [[ -z "$version" ]]; then
        echo -e "\033[31mCan't get your system's version.\033[0m"
        exit
    fi
    if [[ $EUID -ne 0 ]]; then
        echo -e "\033[31m please run this tool as root. \033[0m"
        exit 0
    fi
    curl -f -s http://169.254.169.254/2009-04-04/meta-data/create-time >/dev/null 2>&1
    if [[ $? -ne 0 ]]; then
        echo -e "\033[31m error occured when curl meta server. Please check network conncection.  \033[0m"
        exit 0
    fi
    echo -e "\033[32m  tool version: $SCRIPT_VERSION \033[0m"

    if [[ -f "$NET_CLOUD_CFG" ]]; then
        rm -f $NET_CLOUD_CFG
    fi

    echo "----------------------------------"
    if ! command -v xxd >/dev/null 2>&1 ; then
        echo "xxd will be installed."
        apt-get update >/dev/null 2>&1
        apt-get install -y xxd >/dev/null 2>&1
    fi
    disable_networkmanager $version
    disable_cloud_network
    modify_interfaces_file
    restrict_default_route
    
    PRIMARY_NIC=$(get_primary_nic)
    echo "  Begin to config network..."
    create_nics_config
    echo "  Begin to create route table..."
    add_route_table

    # restart network
    echo -n "  Restart network..."
    killall dhclient >/dev/null 2>&1
    if [[ "$version" == "7" ]]; then
        #nics=$(ls /sys/class/net/ | grep -v "lo")
        #for name in ${nics[*]}
        #do
        #    ifdown $name >/dev/null 2>&1 && ifup $name >/dev/null 2>&1
        #    if [ "$?" -eq 0 ]; then
        #        echo -e "  Restart network $name  \033[32m[OK]\033[0m"
        #    else
        #        echo -e "  Restart network $name  \033[31m[FAILED]\033[0m"
        #    fi
        #done
        service networking restart >/dev/null 2>&1
    else
        systemctl restart networking >/dev/null 2>&1
    fi
    if [ "$?" -eq 0 ]; then
        echo -e "  \033[32m[OK]\033[0m"
    else
        echo -e "  \033[31m[FAILED]\033[0m"
    fi
    echo "----------------------------------"
    # config eip default route
    echo "  Begin to set eip default route..."
    config_eip_default_route
    echo "----------------------------------"

else
    echo -e "\033[31mPlease run the script on Debian system.\033[0m"
fi
