#!/bin/bash

_local_net_adapter=($(ip -4 a | awk -F: '$2 !~ /lo|docker/{print $2}' | xargs))
declare -a _local_net_cidrs

# @get_local_cidr
#
function get_local_cidr() {
    # num_of_adapter=$(echo ${#_local_net_adapter[@]})
    for adap in ${_local_net_adapter[@]}; do
        adap_cidr=$(ip -4 a | sed -n -r "/${adap}/{/inet/{s/inet(.*)\sbrd(.*)/\1/g;p}}" | xargs)
        _local_net_cidrs[${#_local_net_cidrs[@]}]=$adap_cidr
    done
}

# @and
# Tools function. Do AND operation.
function and() {
    while true; do
        case $1 in
        -i | --ibase)
            ibase=$2
            shift 2
            ;;
        --str1)
            str1=$2
            shift 2
            ;;
        --str2)
            str2=$2
            shift 2
            ;;
        -o | --obase)
            obase=$2
            shift 2
            ;;
        *)
            break
            ;;
        esac
    done
    bit1=$(echo "ibase=$ibase;obase=2;$str1" | bc)
    bit2=$(echo "ibase=$ibase;obase=2;$str2" | bc)
    len1=${#bit1}
    len2=${#bit2}
    if [ "$len1" -gt "$len2" ]; then
        len=$len1
        bit2=$(printf "%0${len}d" $bit2)
    elif [ "$len1" -lt "$len2" ]; then
        len=$len2
        n=$((32 - ${len1}))
        for ((i = 1; i <= $n; i++)); do
            bit1="0${bit1}"
        done
    else
        len=$len1
    fi

    for ((i = 1; i <= $len; i++)); do
        b1=${bit1:0-$i:1}
        b2=${bit2:0-$i:1}
        if [ $b1 -eq 1 ] && [ $b2 -eq 1 ]; then
            b=1
        else
            b=0
        fi
        result=$(echo "$b""$result")
    done
    result=$(echo "obase=$obase;ibase=2;$result" | bc)
    echo $result
}

# @get_cidr_and_result
#
function get_cidr_and_result() {
    assigned_addr_cidr=$1

    assigned_addr_network=$(ipcalc -n $assigned_addr_cidr | awk -F'=' '{print $NF}')
    assigned_addr_netmask=$(ipcalc -m $assigned_addr_cidr | awk -F'=' '{print $NF}')
    assigned_addr_network_bin=$(echo $assigned_addr_network | awk -f 'scripts/dec_to_bin.awk')
    assigned_addr_netmask_bin=$(echo $assigned_addr_netmask | awk -f 'scripts/dec_to_bin.awk')

    assigned_and_result=$(and --ibase 2 --obase 2 --str1 $assigned_addr_network_bin --str2 $assigned_addr_netmask_bin)

    echo $assigned_and_result
}

# @is_docker_net_private
# Check if docker0、docker_gwbridge、macrowing is private.
function is_docker_net_private() {
    _net_addr_array=($(echo $1 | awk -F '.' 'END{for(i=1;i<=NF;i++){print $i}}'))
    if [ ${_net_addr_array[0]} == 10 ]; then
        log_info "Assigned $1 address $1 is private."
        return 0
    elif [ ${_net_addr_array[0]} == 172 -a $(and -i 10 -o 10 --str1 ${_net_addr_array[1]} --str2 240) == 16 ]; then
        log_info "Assigned address $1 is private."
        return 0
    elif [ ${_net_addr_array[0]} == 192 -a ${_net_addr_array[1]} == 168 ]; then
        log_info "Assigned address $1 is private."
        return 0
    else
        log_error "Assigned address: $1 is not private. Please check you configuration."
        exit 11
    fi
}

function is_docker_net_conflict_with_physical() {
    get_local_cidr
    for _local_cidr in ${_local_net_cidrs[@]}; do
        log_info "Check network: $1 and pysical network: $_local_cidr"
        docker_network_contains_check $1 $_local_cidr
    done
}

# @docker_network_contains_check
# Compare whether the two network segments contain
function docker_network_contains_check() {
    prefix1=$(ipcalc -p $1 | awk -F'=' '{print $NF}')
    prefix2=$(ipcalc -p $2 | awk -F'=' '{print $NF}')
    network_addr1=$(ipcalc -n $1 | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
    network_addr2=$(ipcalc -n $2 | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
    host_addr1=$(echo $1 | awk -F'/' '{print $1}')
    host_addr2=$(echo $2 | awk -F'/' '{print $1}')
    if [[ "$prefix1" -eq "$prefix2" && "$network_addr1" -eq "$network_addr2" ]]; then
        log_error "network: $1 is conflict network: $2"
        exit
    elif [[ "$prefix1" -gt "$prefix2" ]]; then
        if [[ "$network_addr1" -eq "$network_addr2" ]]; then
            log_error "network: $1 is conflict network: $2"
            exit
        else
            n1=$(ipcalc -n "$host_addr1/$prefix2" | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
            n2=$(ipcalc -n "$host_addr2/$prefix2" | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
            if [[ "$n1" -eq "$n2" ]]; then
                log_error "network: $2 is contains network: $1"
                exit
            fi
        fi
    elif [[ "$prefix1" -lt "$prefix2" ]]; then
        if [[ "$network_addr1" -eq "$network_addr2" ]]; then
            log_error "network: $1 is conflict network: $2"
            exit
        else
            n1=$(ipcalc -n "$host_addr1/$prefix1" | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
            n2=$(ipcalc -n "$host_addr2/$prefix1" | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
            if [[ "$n1" -eq $n2 ]]; then
                log_error "network: $1 is contains network: $2"
                exit
            fi
        fi
    fi
}

function docker0_ip_check(){
    ip=$(echo $DOCKER0_NETWORK | awk -F/ '{print $1}' | awk -f 'scripts/dec_to_bin.awk')
    network=$(ipcalc -n $DOCKER0_NETWORK | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
    boardcast=$(ipcalc -b $DOCKER0_NETWORK | awk -F'=' '{print $NF}' | awk -f 'scripts/dec_to_bin.awk')
    if [ ${ip}x == ${network}x ];then
        log_error "配置的DOCKER0_NETWORK IP地址是配置网段的网络地址，不能使用，请更改配置"
        exit
    fi
    if [ ${ip}x == ${boardcast}x ];then
        log_error "配置的DOCKER0_NETWORK IP地址是配置网段的广播地址，不能使用，请更改配置"
        exit
    fi
}

function dockergw_pool_check(){
    prefix=$(ipcalc -p $DOCKER_GWBRIDGE_NETWORK | awk -F'=' '{print $NF}')
    if [ $prefix -ge 24 ];then
        log_error "DOCKER_GWBRIDGE_NETWORK 的子网掩码必须小于 24"
        exit
    fi
}

# @net_precheck
# Check operation entry function.
function net_precheck() {
    docker0_ip_check
    dockergw_pool_check

    ## 检查网络是否是私有的
    is_docker_net_private $DOCKER0_NETWORK
    is_docker_net_private $DOCKER_GWBRIDGE_NETWORK
    #is_docker_net_private $MACROWING_NETWORK

    ## 检查网络是否和物理网络冲突
    log_info "Check DOCKER0_NETWORK and pysical network"
    is_docker_net_conflict_with_physical $DOCKER0_NETWORK
    log_info "Check DOCKER_GWBRIDGE_NETWORK and pysical network"
    is_docker_net_conflict_with_physical $DOCKER_GWBRIDGE_NETWORK
    #log_info "Check MACROWING_NETWORK and pysical network"
    #is_docker_net_conflict_with_physical $MACROWING_NETWORK

    log_info "Check DOCKER0_NETWORK: $DOCKER0_NETWORK and DOCKER_GWBRIDGE_NETWORK: $DOCKER_GWBRIDGE_NETWORK"
    docker_network_contains_check $DOCKER0_NETWORK $DOCKER_GWBRIDGE_NETWORK
    #log_info "Check DOCKER0_NETWORK: $DOCKER0_NETWORK and MACROWING_NETWORK: $MACROWING_NETWORK"
    #docker_network_contains_check $DOCKER0_NETWORK $MACROWING_NETWORK 
    #log_info "Check DOCKER0_NETWORK: $DOCKER_GWBRIDGE_NETWORK and PHSICAL_NETWORK: $MACROWING_NETWORK"
    #docker_network_contains_check $DOCKER_GWBRIDGE_NETWORK $MACROWING_NETWORK
}

