#!/bin/sh

# used for mac address randomization for VNET jails
__get_next_mac () {
    local _mac_base _mac_offset _calc_mac _mac _mac_postfix

    _mac_base="$(echo $1 | tr '[a-z]' '[A-Z]')"
    _mac_offset="$(echo "obase=16; $2" | bc)"
    _calc_mac=$(echo "obase=16; ibase=16; ${_mac_base} + ${_mac_offset}" | bc)
    _mac_postfix=$(echo ${_calc_mac} | awk '{print substr($0, length($0)-5, length($0))}')
    _mac="${mac_prefix}${_mac_postfix}"

    echo ${_mac}
}

# The devfs entry needed for bpf devices in a jail
__bpf_devfs () {
cat << 'EOT'

## IOCAGE -- Add DHCP to ruleset 4
[devfsrules_jail_dhcp=5]
add include $devfsrules_jail
add path 'bpf*' unhide
EOT
}

# Expects input: _nic _mac_offset _epair_leg _uuid
# outputs a single mac address
# Example: __validate_mac ${nic} ${_mac_offset} a ${_uuid}
__validate_mac () {
    local _nic _uuid _mac _mac_base _mac_offset \
          _mac_epair_a _mac_epair_b _return _dataset

    _nic="$1"
    _mac_offset="$2"
    _epair_leg="$3"
    _uuid="$4"
    _dataset="$5"
    _mac="$(__get_jail_prop ${_nic}_mac ${_uuid} $_dataset)"
    _mac_base="$(echo ${_uuid} | awk '{print substr($0, 0, 6)}')"
    _mac_epair_a="$(__get_next_mac ${_mac_base} ${_mac_offset})"
    _mac_epair_b="$(__get_next_mac ${_mac_base} ${_mac_offset}+1)"

    if [ ${_mac} = "none" ] ; then
        __set_jail_prop ${_nic}_mac="${_mac_epair_a},${_mac_epair_b}" \
            "${_uuid}" "${_dataset}"
    fi

    if [ ${_epair_leg} = "a" ] ; then
        _return="$(__get_jail_prop ${_nic}_mac ${_uuid} $_dataset|cut -f1 -d,)"
    elif [ ${_epair_leg} = "b" ] ; then
        _return="$(__get_jail_prop ${_nic}_mac ${_uuid} $_dataset|cut -f2 -d,)"
    fi

    echo ${_return}
}

# Expects $1 for action and $2 for UUID
__networking () {
    local _action _uuid _jid _mac_epair_a mac_epair_b \
          _mac_offset _dataset _dhcp _dhcp_string _bridge_ifaces \
          _bridge_exists _bridge_inuse

    _action="$1"
    _uuid="$2"
    _dataset="$3"
    _jid="$(jls -j ioc-${_uuid} jid)"
    _dhcp="$(__get_jail_prop dhcp ${_uuid} ${_dataset})"
    local ip4="$(__get_jail_prop ip4_addr ${_uuid} $_dataset)"
    local ip6="$(__get_jail_prop ip6_addr ${_uuid} $_dataset)"
    local defaultgw="$(__get_jail_prop defaultrouter ${_uuid} $_dataset)"
    local defaultgw6="$(__get_jail_prop defaultrouter6 ${_uuid} $_dataset)"
    local nics="$(__get_jail_prop interfaces ${_uuid} $_dataset\
               |awk 'BEGIN { FS = "," } ; { print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10 }')"
    #local ip4_list="$(echo $ip4 | sed 's/,/ /g')"
    local ip6_list="$(echo $ip6 | sed 's/,/ /g')"

    local createbridge="$(__get_jail_prop createbridge ${_uuid})"
    local keepbridge="$(__get_jail_prop keepbridge ${_uuid})"

    # Get the default and current interfaces specified
    local default_iface="$(__get_default_iface)"
    local cur_ip4_iface=$(echo "$ip4_addr" | cut -d '|' -f 1)
    local cur_ip6_iface=$(echo "$ip6_addr" | cut -d '|' -f 1)
    _bridge_ifaces="$(netstat -iWn | egrep 'bridge0|bridge1' | \
                    awk '{ print $1 }')"

    # Change the DEFAULT tag to correct iface
    nics=$(echo $nics | sed "s/DEFAULT/$default_iface/g")
    if [ ${_action} = "start" ] ; then
        _mac_offset=0

        # Check to see if any bridges exist, otherwise create them
        # we add the default interface as a member so the jails can reach the
        # world.
        if [ ! -z "${_bridge_ifaces}" ] ; then
            for _b in ${_bridge_ifaces} ; do
                if [ "${_b}" != "bridge0" ] ; then
                    _bridge_exists="$(echo ${_bridge_ifaces} | grep bridge0)"
                    if [ -z "${_bridge_exists}" -a "${createbridge}" == "on" ] ; then
                        ifconfig bridge0 create addm "${default_iface}"
                    else
                        continue
                    fi
                elif [ "${_b}" != "bridge1" ] ; then
                    _bridge_exists="$(echo ${_bridge_ifaces} | grep bridge1)"
                    if [ -z "${_bridge_exists}" -a "${createbridge}" == "on" ] ; then
                        ifconfig bridge1 create
                    else
                        continue
                    fi
                fi
            done
        else
            ifconfig bridge0 create addm "${default_iface}"
            ifconfig bridge1 create
        fi

        for i in $nics ; do
            local nic=$(echo $i | awk 'BEGIN { FS = ":" } ; { print $1 }')
            local bridge=$(echo $i | awk 'BEGIN { FS = ":" } ; { print $2 }')
            local memberif=$(\ifconfig $bridge | grep member | head -n1 | \
                           cut -d' ' -f2)
            local brmtu=$(\ifconfig $memberif | head -n1 |cut -d' ' -f6)
            epair_a=$(\ifconfig epair create)
            epair_b=$(echo $epair_a | sed s/a\$/b/)
            ifconfig ${epair_a} name ${nic}:${_jid} mtu $brmtu
            _mac_epair_a="$(__validate_mac ${nic} ${_mac_offset} a ${_uuid} $_dataset)"
            ifconfig ${nic}:${_jid} link ${_mac_epair_a}
            ifconfig ${nic}:${_jid} description "associated with jail: ${_uuid}"
            ifconfig $epair_b vnet ioc-${2}
            jexec ioc-${2} ifconfig $epair_b name $nic mtu $brmtu
            _mac_epair_b="$(__validate_mac ${nic} ${_mac_offset} b ${_uuid} $_dataset)"
            jexec ioc-${2} ifconfig ${nic} link ${_mac_epair_b}
            ifconfig $bridge addm ${nic}:${_jid} up
            ifconfig ${nic}:${_jid} up
            let _mac_offset="_mac_offset + 2" > /dev/null
        done

        echo "$ip4_addr" | grep -q "AUTOIP4"

        if [ "$ip4" != "none" -a $? -ne 0 ] ; then
            local oIFS=$IFS
            local IFS=","
            for i in $ip4 ; do
                local IFS=$oIFS
                iface=$(echo $i |awk 'BEGIN { FS = "|" } ; { print $1 }' | \
                      sed "s/DEFAULT/$default_iface/g")
                ip="$(echo $i |awk 'BEGIN { FS = "|" } ; { print $2 }')"
                jexec ioc-${2} ifconfig $iface $ip up
                local IFS=","
            done
            local IFS=$oIFS
        fi

        if [ "$ip6" != "none" ] ; then
            for i in $ip6_list ; do
                iface=$(echo $i |awk 'BEGIN { FS = "|" } ; { print $1 }' | \
                      sed "s/DEFAULT/$default_iface/g")
                ip="$(echo $i |awk 'BEGIN { FS = "|" } ; { print $2 }')"
                jexec ioc-${2} ifconfig $iface inet6 $ip up
            done
        fi

        if [ "$defaultgw" != "none" ] ; then
            jexec ioc-${2} route add default $defaultgw > /dev/null
        fi

        if [ "$defaultgw6" != "none" ] ; then
            jexec ioc-${2} route add -6 default "${defaultgw6}" >/dev/null
        fi

        # Check if the user specified they wanted to use an automatic IP4 address
        echo "$ip4_addr" | grep -q "AUTOIP4"
        if [ $? -eq 0 ] ; then
            __get_autoip4 $_dataset

            if [ -z "$cur_ip4_iface" -o "$cur_ip4_iface" == "DEFAULT" ] ; then
                new_iface="$default_iface"
                __set_jail_prop "ip4_addr=DEFAULT|${auto_ip4}" "${_uuid}" \
                    "${_dataset}"
            else
                new_iface="$cur_ip4_iface"
                __set_jail_prop "ip4_addr=$new_iface|${auto_ip4}" "${_uuid}" \
                    "${_dataset}"
            fi
            jexec ioc-${2} ifconfig $new_iface ${auto_ip} up
        fi

        if [ "${_dhcp}" != "off" ] ; then
            _dhcp_string="$(fgrep -xq \
                         "ifconfig_vnet0=\"DHCP\"" \
                         ${iocroot}/jails/${_uuid}/root/etc/rc.conf \
                         ; echo $?)"

            if [ "${_dhcp_string}" != "0" ] ; then
                echo "" >> \
                     ${iocroot}/jails/${_uuid}/root/etc/rc.conf
                echo "ifconfig_vnet0=\"DHCP\"" >> \
                     ${iocroot}/jails/${_uuid}/root/etc/rc.conf
            fi

            jexec ioc-${2} service dhclient start vnet0 > /dev/null 2>&1
        fi
    fi

    if [ ${_action} = "stop" ] ; then
        for if in $nics ; do
            local nic="$(echo $if | cut -f 1 -d:)"
            ifconfig ${nic}:${_jid} destroy
        done

        _bridge_inuse="$(netstat -iWn | grep '^vnet' | \
                       awk '{ print $1 }')"
        # If no vnet interfaces are using the bridges, let us clean up after
        # ourselves
        if [ -z "${_bridge_inuse}" -a "${keepbridge}" == "off" ] ; then
            ifconfig bridge0 destroy > /dev/null 2>&1
            ifconfig bridge1 destroy > /dev/null 2>&1
        fi
    fi
}

__get_autoip4 () {
    local _autobase _autostart _autoend _autoip _freeip _dataset _jails _juuid \
          _jip4

    _dataset="$2"
    unset _auto_ip4
    _autobase=$(netstat -f inet -nrW | grep '^default' |\
              awk '{ print $2 }' | cut -d '.' -f 1-3)
    _autostart=$(__get_jail_prop ip4_autostart default)
    _autoend=$(__get_jail_prop ip4_autoend default)
    _autosubnet=$(__get_jail_prop ip4_autosubnet default)
    _jails=$(__find_jail ALL | \
           egrep -v "${pool}/iocell/releases/[0-9].?\>")

    # Do some basic sanity checking
    if [ -z "$_autostart" ] ; then
        __die "AUTOIP4 is specified, but ip4_autostart is not set!"
    fi
    if [ -z "$_autoend" ] ; then
        __die "AUTOIP4 is specified, but ip4_autoend is not set!"
    fi
    if [ -z "$_autosubnet" -o "$_autosubnet" = "none" ] ; then
        _autosubnet="24"
    fi

    # Now loop through all the jails, and find a free IP we can use
    # that fits within the autostart/autoend ranges
    _autoip=$_autostart
    while :
    do
        _freeip="YES"
        _nextaddr="${_autobase}.${_autoip}"
        # Is this address in use?
        for _jail in ${_jails} ; do
            # _jail is actually a dataset, so fulluuid needs to be faked.
            _juuid="$(__check_name "name" ${_jail} 2> /dev/null)"
            _jip4="$(__get_jail_prop ip4_addr ${_juuid} ${_dataset})"
            _jip4=$(echo ${_jip4} | cut -d '|' -f 2 | cut -d '/' -f 1)
            if [ "${_jip4}" = "${_nextaddr}" ] ; then
                _freeip="NO"
                break
            fi
        done

        # On to the next entry to try
        if [ "${_freeip}" = "NO" ] ; then
            _autoip=$(expr ${_autoip} + 1)
            continue
        fi

        # Sanity check
        if [ ${_autoip} -gt ${_autoend} ] ; then
            __die "IP4 Automatic Address exhaustion"
        fi
        if [ ${_autoip} -gt 254 ] ; then
            __die" IP4 Address outside of bounds"
        fi
        break
    done

    # Set our new ip address
    auto_ip4="${_nextaddr}/${_autosubnet}"
}

__stop_legacy_networking () {
    local _fulluuid _dataset _ip4_addr _ip6_addr _default_iface _iface _ip4


    _fulluuid="$1"
    _dataset="$2"
    _ip4_addr="$(__get_jail_prop ip4_addr ${_fulluuid} ${_dataset})"
    _ip6_addr="$(__get_jail_prop ip6_addr ${_fulluuid} ${_dataset})"

    # Get the default and current interfaces specified
    _default_iface="$(__get_default_iface)"

    # Change the DEFAULT tag to correct iface
    _ip4_addr=$(echo ${_ip4_addr} | sed "s/DEFAULT|/${_default_iface}|/g")
    _ip6_addr=$(echo ${_ip6_addr} | sed "s/DEFAULT|/${_default_iface}|/g")

    if [ "${_ip4_addr}" != "none" ] ; then
        local oIFS=$IFS
        local IFS=','
        for _ip4 in ${_ip4_addr} ; do
            _iface="$(echo ${_ip4} | \
                awk 'BEGIN { FS = "|" } ; { print $1 }')"
            _ip4="$(echo ${_ip4} | \
                awk 'BEGIN { FS = "|" } ; { print $2 }' | \
                awk 'BEGIN { FS = "/" } ; { print $1 }' | \
                awk 'BEGIN { FS = " " } ; { print $1 }')"

            ifconfig ${_iface} ${_ip4} -alias
        done
        local IFS=$oIFS
    fi

    if [ "${_ip6_addr}" != "none" ] ; then
        local oIFS=$IFS
        local IFS=','
        for _ip6 in ${_ip6_addr} ; do
            _iface="$(echo ${_ip6} | \
                         awk 'BEGIN { FS = "|" } ; { print $1 }')"
            _ip6="$(echo ${_ip6} | \
                       awk 'BEGIN { FS = "|" } ; { print $2 }' | \
                       awk 'BEGIN { FS = "/" } ; { print $1 }' | \
                       awk 'BEGIN { FS = " " } ; { print $1 }')"
            ifconfig ${_iface} inet6 ${_ip6} -alias
        done
        local IFS=$oIFS
    fi
}
