#
# Bash source file for IPV4 computing - netfunc v0.15
#
# (C) 2011-2012 Felix Hauri - felix@f-hauri.ch
# Licensed under terms of GPL v3. www.gnu.org

# after sourcing script:
# syntaxe: ipcalc xxx.xxx.xxx.xxx/yy or ipcalc xxx.xxx.xxx.xxx zzz.zzz.zzz.zzz
# where xxx is the basic ip address for compute, yy is the length or de mask
# and/or zzz is the mask in standard ipv4 notation.
# Compute and display network, broadcast, host/net and class address
# display all address both in standard ipv4 notation and binary.

# In order to prevent forks, last optional argument to functions is a
# variable name to be populated by function result.

# to be sourced!  Quit function list: source this or run : grep '()' 

shopt -s extglob
if [ "${BASH_SOURCE[0]}" ];then
    _srcFuncList="$(basename "${BASH_SOURCE[0]%%[_.]*}")FList"
    eval "$_srcFuncList() {
        if [[ \$# -gt 0 ]] ;then
	  sed '
            s/() *{ *#/            :/;ta;bb;:a;;s/^\(.\{12\}\) *:/\n - \1/p;
            :c;N;s/^.*\n *# /     /p;tc;:b;1,3s/^# //p;d
	    ' '${BASH_SOURCE[0]}'
	    echo
	else
	  sed '
	    s/() *{ *#/            :/;ta;bb;:a;;s/^\(.\{12\}\) *:/ - \1/p;
	    :b;1,3s/^# //p;d
	    ' '${BASH_SOURCE[0]}'
        fi
	printf ' - %-11s %s\n' $_srcFuncList 'List functions (this.) [-v]'
        }"
    [[ $1 == -q ]] || [[ $1 == --quiet ]] || "$_srcFuncList"
    unset _srcFuncList
fi
int2ip() { # integer to IPv4
    # int2ip [-v VARNAME] <integer> [<integer> ...]
    if [[ $1 == -v ]];then local -n _outar="$2";shift 2;else local _outar;fi
    _outar=()
    local _arg
    for _arg ;do
	printf -v _arg "%d.%d.%d.%d" \
               $((_arg>>24)) $((_arg>>16&255)) $((_arg>>8&255)) $((_arg&255))
	_outar+=("$_arg")
    done
    [[ ${_outar@A} != declare\ -a\ _outar=* ]] || printf '%s\n' "${_outar[@]}"
}
int2rev() { # 3 most significant bytes, for reverse DNS X.X.X.in-addr.arpa
    # int2rev [-v VARNAME] <integer>
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    printf -v _out "%d.%d.%d" $(($1>>8&255)) $(($1>>16&255)) $(($1>>24))
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
checkIsIp() { # throw an error if not valid IPv4
    # checkIsIp [-v VARNAME] <ip address>
    local _iPointer _i _a _vareq=()
    if [[ $1 == -v ]];then _vareq=(-v "$2");shift 2;fi
    for _i ;do
	case $_i in *[^0-9.]* ) return 1 ;; esac
	read -ra _a <<<"${_i//./ }"
	[ ${#_a[@]} -eq 4 ] || return 1
	for _iPointer in "${_a[@]}" ;do
            (( _iPointer == ( _iPointer & 255 ) ))  || return 2
	done
    done
    ip2int "${_vareq[@]}" "${@}"
}
ip2int() { # IPv4 to integer 32 bits value.
    # ip2int  [-v VARNAME] <ip address>
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    local _a
    read -ra _a <<<"${1//./ }"
    printf -v _out "%u" $(( _a[0] << 24 | _a[1] << 16 | _a[2] << 8 | _a[3] ))
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
mask2int() { # from mask len to integer value of IPv4 mask
    # mask2int [-v VARNAME] <int 0-32>
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    printf -v _out "%u" $(( ( ~0 ^ ~0<<32 ) ^ ( ~0 ^ ~0<<(32-$1) ) ))
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
int2mlen() { # compute mask len from integer value of IPv4 mask
    # int2mlen [-v VARNAME] <int 0-32>
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    local _mlen
    int2bin -v _mlen "$1"
    _mlen=${_mlen// } _mlen=${_mlen##*(1)}
    printf -v _out "%d" $((32-${#_mlen}))
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
be2int() { # Big Endian to INTeger, ( for reading proc/net/route )
    # be2int [-v VARNAME] <four byte hexa in big endian>
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    printf -v _out %u $(( 16#${1::2} | ( 16#${1:2:2} <<+8 )|
			      ( 16#${1:4:2} <<+16 )| ( 16#${1:6} <<+24 ) ))
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
int2uu() { # 1st run generate int2uu() to render bash's 64# from integer
    # int2uu [-v VARNAME] <integer>
    local string func
    # shellcheck disable=2016 # Expressions don't expand in single quotes,
    printf -v string '${array[$1>>%d&63]} ' {60..0..-6}
    IFS='' read -rd '' func <<-EOF
	() {
	    if [[ \$1 == -v ]] ;then
		local -n _out="\$2";shift 2
	    else
		local _out
	    fi
	    local array=({0..9} {a..z} {A..Z} @ _)
	    printf -v _out %s ${string/>>0}
	    _out=\${_out##*(0)}
	    [[ \${_out@A} != _out=* ]] || echo "\$_out" 
	}
	EOF
    eval "${FUNCNAME[0]}$func"
    ${FUNCNAME[0]} "$@"
}
int2bin() { # 1st run generate int2bin to render 32bits, mark mask with a dot.
    # int2bin [-v VARNAME] <integer>
    local string func
    printf -v func %8s ''
    # shellcheck disable=2016 # Expressions don't expand in single quotes,
    printf -v string '$((($1>>%d)&1)) ' {31..1}
    IFS='' read -rd '' func <<-EOF
	() {
	    if [[ \$1 == -v ]] ;then
		local -n _out="\$2";shift 2
	    else
		local _out
	    fi
	    local _p
	    printf -v _out \
		'${func// /%d} ${func// /%d} ${func// /%d} ${func// /%d}' \
		$string \$(( \$1 & 1 ))
	    [ "\$2" ] && _p=\$((\$2+(\$2>8)+(\$2>16)+(\$2>24))) &&
		printf -v _out %s.%s "\${_out:0:_p}" "\${_out:_p}"
	    [[ \${_out@A} != _out=* ]] || echo "\$_out"
	}
	EOF
    eval "${FUNCNAME[0]}$func"
    ${FUNCNAME[0]} "$@"
}
rangeIp() { # Generate array of integer representation of IPv4 from argument
    # int2bin <expr[,expr[,expr...]]>  [expr[,expr..]] # where ``expr'' is
    # 'xxx.xx.xx.xxx', 'xxx.xx.xx.xxx/masklen' or 'xxx.xx.xx.xxx-yyy.yy.yy.yyy'
    if [[ $1 == -v ]];then local -n _res="$2";shift 2;else local _res;fi
    local first last iFirst iLast i="$*" _out=()
    local fields delims
    i=${i// /,}
    read -ra fields <<<"${i//[,\/-]/ }"
    read -ra delims <<<"${i//[0-9.]/ }"
    first=${fields[0]}
    checkIsIp -v iFirst "$first" || return 1
    read -ra first <<<"${first//./ }"
    read -ra fields <<<"${fields[@]:1}"
    while [ "${fields[0]}" ] ;do
	read -ra last <<<"${fields[0]//./ }"
	case ${delims[0]} in
	    , )
		last[0]="${first[*]:0:4-${#last[@]}} ${last[*]}"
		_out[$iFirst]=""
		first=("${last// /.}")
		checkIsIp -v iFirst "${first[0]}" || return 2
		read -ra first <<<"${first//./ }"
		;;
	    - )
		last[0]="${first[*]:0:4-${#last[@]}} ${last[*]}"
		last[0]=${last// /.}
		checkIsIp -v iLast "${last[0]}" || return 3
		for ((i=iFirst;i<=iLast;i++));do
		    _out[i]=""
		done
		read -ra first <<<"${last//./ }"
		;;
	    / )
		mask2int -v iLast "${last[0]}"
		iFirst=$((iFirst & iLast))
		iLast=$((iFirst | ( iLast ^  ( ~0 ^ ~0<<32 ) ) ))
		for ((i=iFirst;i<=iLast;i++));do
		    _out[i]=""
		done
		int2ip -v first $iLast
		read -ra first <<<"${first//./ }"
		;;
	esac
	read -ra delims <<<"${delims[@]:1}"
	read -ra fields <<<"${fields[@]:1}"
    done
    _out[iFirst]=""
    _res=("${!_out[@]}")
    [[ ${_res@A} != declare\ -a\ _res=* ]] || echo "${_res[@]}"
}
_mPingTimeout=1.6
multiping() { # One ping on many target. Use integer representation of IPv4
    # multiping <integer> [integer [integer...]]
    if [[ $1 == -v ]] ;then
	local _var=: ip time i a intip
	# shellcheck disable=2178 # Incorrect handling of nameref
	local -n _out="$2"
	shift 2
	_out=()
    else
	local _var=''  _out=() ip time i a intip
    fi
    while read -r a _ _ ip _ _ time _;do
	[[ $a == 64 ]] &&
	    ip2int -v intip "${ip%:}"&& time=${time#*=}&& _out[intip]=${time// }
    done < <(
	for i ;do
	    int2ip -v ip "$i"
	    ping -nc 1 -W "$_mPingTimeout" "$ip" &
	    sleep .002
	done  2> >(grep -v 'ping broadcast' >&2)
	wait
    )
    if [ -z "$_var" ];then
	for i in "${!_out[@]}";do
	    int2ip -v a "$i"
	    printf "%-16s %8s\n" "$a" "${_out[i]:---}"
	done
    fi
}
declare -a _ipResInv
ip2hnam () { # Fork to `host` and store in _ipResInv, if not already stored.
    # ip2hnam [-v VARNAME] <ip address>
    local _len _int _lh _rh _var=""
    while [[ ${1::1} == - ]] ;do
	case ${1:1} in
	    l ) _len=$2 ; shift 2;;
	    v ) local -n _host=$2 ; _var=1 ; shift 2;;
	    * ) echo "Unknown option $1"; return 1;;
	esac
    done
    [[ -z "$_var" ]] && local _host
    
    ip2int -v _int "$1"
    if [[ -v _ipResInv[_int] ]] ;then
	_host=${_ipResInv[_int]}
    else
	read -ra _host < <(host "$1")
	if [[ ${_host[3]} == pointer ]]; then
            _host=("${_host[4]%%.*}")
	    _ipResInv[_int]="${_host[0]}"
	else
	    _host=("$1")
	fi
    fi
    
    [ "$_len" ] && [[ $_len -lt ${#_host} ]] &&
	if [[ $1 == "${_host[0]}" ]];then
	    _lh=$((_len/2-1)) _rh=$((_len-_lh-2))
	    _host=("${_host::$_lh}..${_host: -$_rh}")
	    return 1
	else
	    _rh=$((_len/2-1)) _lh=$((_len-_rh-2))
	    _host=("${_host::$_lh}..${_host: -$_rh}")
	fi
    [[ $1 == "${_host[0]}" ]] && return 1
    [[ ${_host@A} != _host=* ]] || echo "${_host[0]}"
}
declare -A _ipResolv
hname2ip () { # Fork to `host` and store in _ipResolv, if not already stored.
    # hname2ip [-v VARNAME] <host name>

    # shellcheck disable=2178 # Incorrect handling of nameref
    if [[ $1 == -v ]];then local -n _out="$2";shift 2;else local _out;fi
    local _host
    if [ -v _ipResolv["$1"] ];then
	_out="${_ipResolv["$1"]}"
    else
	while read -ra _host ;do
	    [[ ${_host[2]} == address ]] && \
		_out="${_host[3]}" _ipResolv["$1"]="${_host[3]}"
	done < <(host "$1")
    fi
    [ -v _out ] || return 1
    [[ ${_out@A} != _out=* ]] || echo "$_out"
}
isup () { # Monitor ping many targets simultaneously (hit any key to quit)
    # isup <hostname|ip address|ip address/masklen|ip address-ip address> [...]
    local _i _ip _ipRange _fmtStr _pings _times _names _ck _lck=-1 _now \
	  _elap _sleep _hdLne _lnCnt=0 _started
    _started=${EPOCHREALTIME/.}
    while [ "$1" ];do
        if rangeIp -v _ip "$1" ;then
	    for _i in "${_ip[@]}" ;do
		_ipRange[_i]=""
	      done
	elif checkIsIp -v _ip "$1" ;then
	    _ipRange[_ip]='' 
	else
	    hname2ip -v _ip "$1" && ip2int -v _ip "$_ip" && _ipRange[_ip]=""
	fi
	shift
    done
    _ipRange=("${!_ipRange[@]}")
    for _i in "${_ipRange[@]}" ;do
        int2ip -v _ip "$_i"
        ip2hnam -v _ip -l 7 "$_ip"
	printf -v _names[_i] %s "$_ip"
    done
    _fmtStr=${_names[*]}
    _fmtStr=" %9.4f %3d ${_fmtStr//+([^ ])/%8s}"
    # shellcheck disable=2059 # Format string was built
    printf -v _hdLne "${_fmtStr/.4f*3d/s %3s}" Elaps Cnt "${_names[@]}"
    while :;do
	_ck=0
	_now=${EPOCHREALTIME/.}
	unset _pings _times
	multiping -v _pings "${_ipRange[@]}"
	_elap=${EPOCHREALTIME/.}
	_sleep=$((1000000-_elap+_now))
	[ $_sleep -gt 0 ] && _sleep=00000$_sleep &&
	    printf -v _sleep "%f" ${_sleep::-6}.${_sleep: -6}
	_elap=00000$((_elap-_started))
	printf -v _elap "%f" ${_elap:0: -6}.${_elap: -6}
	for ((_i=0;_i<${#_ipRange[@]};_i++)) ; do
            _times[${_ipRange[$_i]}]=${_pings[${_ipRange[_i]}]:---}
	    [ "${_pings[_ipRange[_i]]}" ] && ((_ck+=2**_i))
	done
	if [ "$_lck" ] && [ $_lck -ne "$_ck" ] ;then
	    echo
	    if [ $((_lnCnt--)) -lt 1 ] ;then
		_lnCnt=${LINES:-24}
		echo "$_hdLne"
	    fi
	fi
	_lck=$_ck
	# shellcheck disable=2059 # Format string was built
	printf "\r$_fmtStr" "$_elap" "${#_pings[@]}" "${_times[@]}"
	[ "${_sleep//*-*}" ] || read -rt 0 -sn1 _ &&
		read -st ${_sleep//*-*/.1} -rn1 _ &&
		echo && break
    done
}
mping() { # quick ping scan IPv4 ranges and resolv host name of waked hosts
    # mping [-v VARNAME] <ip address|ip address/mask|ip address-ip address> ...
    local _list _i _nam _pings _ip _var=()
    [[ $1 == -v ]] && local -n _out="$2" && _out=() _var=(-v "$2") && shift 2
    if ! [[ $1 ]] ;then
	local _netList _net _loc
	getLocalNet -v _netList
	_netList=("${_netList[@]#*:}")
	_netList=("${_netList[@]%:*}")
	${FUNCNAME[0]} "${_var[@]}" ${_netList[@]/*\/0}
	return
    fi
    _i="$*"
    rangeIp -v _list "${_i// /,}"
    multiping -v _pings "${_list[@]}"
    for _i in "${!_pings[@]}";do
	int2ip -v _ip "$_i"
	ip2hnam -v _nam "$_ip"
	printf " - %-15s %-18s %s\n" "$_ip" "$_nam" "${_pings[_i]}"
	[[ ${_var[0]} == -v ]] && _out[$_i]=$_ip:$_nam
    done
}
case ${LANG:-$LC_ALL} in
    *.utf8|*.UTF8|*.utf-8|*.UTF-8) _nf_brds=($'\U2500' $'\U2501' $'\U2502' \
	$'\U2503' $'\U250F' $'\U252F' $'\U2513' $'\U2520' $'\U253C' $'\U2528' \
	$'\U2517' $'\U2537' $'\U251B' $'\U25B8');;
    *)	_nf_brds=('-' '-' '|' '|' '+'{,,,,,,,,} '>') ;;
esac
_ipC_fmtPrint(){ # format print lines for ipcalc
    local __ip __bin __dist __crt=''
    int2ip -v __ip "$1"
    int2bin -v __bin "$1" "$_mlen"
    __dist=$(($1-_base))
    (($1-_int)) || __crt="${_nf_brds[13]}"
    shift
    printf '%s%1s%-12s%s%-15s%s%11s%s%s%s\n' "${_nf_brds[3]}" "$__crt" "$*" \
	   "${_nf_brds[2]}" "$__ip" "${_nf_brds[2]}" "#$__dist" \
	   "${_nf_brds[2]}" "${__bin/./${_nf_brds[2]}}" "${_nf_brds[3]}"
}
ipcalc() { # compute and render network submited as [ip mask] or [ip/masklen]
    # ipcalc [<ip address/mask>|<ip address> <netmask>]
    local _int _ip _mask _mlen _imask _base _bin _broadcast _class \
	  _crt _bl _sl _igate _gwnam _hstnam
    if ! [[ $1 ]] ;then
	local _netList _net _loc
	getLocalNet -v _netList
	for _net in "${_netList[@]}";do
	    IFS=: read -ra _loc <<<"$_net:"
	    printf '\e[1mInterface: %s\e[0m\n' "${_loc[0]}"
	    # shellcheck disable=SC2154 # ${_gw} is referenced but not assigned
	    ${FUNCNAME[0]} "${_loc[@]:1:2}" Local ${_gw+Gateway}
	done
	return
    fi
    checkIsIp -v _int "${1%/*}" || { echo "Not an IP: '$1'"; return 1;}
    if [[ $1 != "${1#*/}" ]] ;then
	_mlen=${1#*/}
	case $_mlen in
	    *[^0-9]* | '' )  echo "Not a mask len: '${1#*/}'"; return 2;;
	esac
	mask2int -v _imask "$_mlen"
	int2ip -v _mask "$_imask"
    elif [ "$2" ];then
	checkIsIp -v _imask "$2" || { echo "Not an IP: '$2'"; return 3;}
	int2bin -v _mlen "$_imask" ''
	_mlen=${_mlen// }
	_mlen=${_mlen%%*(0)}
	_mask=$2
	case $_mlen in *0* ) echo "Not a mask: '$2'"; return 4;; esac
	_mlen=${#_mlen}
	shift
    else
	echo "Mask missing."
	return 5
    fi
    if [[ "$3" ]] ;then _hstnam="$3"
    else ip2hnam -v _hstnam "${1%/*}" || _hstnam=Host
    fi
    if [[ "$2" ]] ;then
	checkIsIp -v _igate "$2" || { echo "Not an IP: '$2'"; return 6;}
	if [[ "$4" ]] ;then _gwnam="$4"
	else ip2hnam -v _gwnam -l 12 "$2" || _gwnam=Gateway
	fi
    fi
    _base=$((_int & _imask))
    _broadcast=$(( _int | ( _imask ^ ( ( 1<<32 ) -1 )) ))

    printf -v _sl '%*s' 36 ''
    _bl=${_sl// /"${_nf_brds[1]}"}   _sl=${_sl// /"${_nf_brds[0]}"}
    printf '%b%s%b%s%b%s%b%s%b%s%b\n' "${_nf_brds[4]}" "${_bl:23}" \
	   "${_nf_brds[5]}" "${_bl:21}" "${_nf_brds[5]}" "${_bl:25}" \
	   "${_nf_brds[5]}" "${_bl::_mlen+(_mlen-1)/8}" "${_nf_brds[5]}" \
	   "${_bl:: 35 - _mlen - (_mlen-1) / 8 }" "${_nf_brds[6]}"

    _ipC_fmtPrint $_base Network base

    int2bin -v _bin "$_imask" "$_mlen"
    printf '%s %-12s%s%-15s%s/%-10s%s%s%s\n' "${_nf_brds[3]}" Mask \
	   "${_nf_brds[2]}" "$_mask" "${_nf_brds[2]}" "$_mlen" \
	   "${_nf_brds[2]}" "${_bin/./${_nf_brds[2]}}" "${_nf_brds[3]}"
    _ipC_fmtPrint $_broadcast Broadcast
    
    int2bin -v _class $_base ''
    _class=${_class%%0*}
    _crt=$(( _broadcast - _base - 1 ))
    (( _crt < 1 )) && _crt=1
    printf -v _class \\%03o $((65+${#_class}))
    printf '%s %-12s%s%-15s%s%-11b%s%*s%s%*s%s\n'  "${_nf_brds[3]}" Host/net \
	   "${_nf_brds[2]}" $_crt "${_nf_brds[2]}" "Class $_class" \
	   "${_nf_brds[2]}" $((_mlen+(_mlen-1)/8)) '' "${_nf_brds[2]}" \
	   $((35-_mlen-(_mlen-1)/8)) ''  "${_nf_brds[3]}"
    
    if [ $((_broadcast - _base)) -gt 1 ];then
	printf '%b%s%b%s%b%s%b%s%b%s%b\n' "${_nf_brds[7]}" "${_sl:23}" \
	       "${_nf_brds[8]}" "${_sl:21}" "${_nf_brds[8]}" "${_sl:25}" \
	       "${_nf_brds[8]}" "${_sl::_mlen+(_mlen-1)/8}" "${_nf_brds[8]}" \
	       "${_sl:: 35 - _mlen - (_mlen-1) / 8 }" "${_nf_brds[9]}"
	if (( _igate == ( 1+ _base ))) ;then
	       _ipC_fmtPrint "$_igate" "$_gwnam"
	else
	    _ipC_fmtPrint $((_base+1)) First host
	fi
	(( ( ( _base + 1 ) < _igate ) & ( _igate < _int ) )) &&
	    _ipC_fmtPrint "$_igate" "$_gwnam"
	(( ( ( _base + 1 ) < _int ) & ( _int < ( _broadcast - 1 ) ) )) &&
	    _ipC_fmtPrint "$_int" "$_hstnam"
	(( (  _int < _igate ) & ( _igate < ( _broadcast - 1 ) ) )) &&
	    _ipC_fmtPrint "$_igate" "$_gwnam"
	if (( _igate == ( _broadcast -1))) ;then
 	    _ipC_fmtPrint "$_igate" "$_gwnam"
	else
 	    _ipC_fmtPrint $((_broadcast-1)) Last host
	fi
    fi
    printf '%b%s%b%s%b%s%b%s%b%s%b\n' "${_nf_brds[10]}" "${_bl:23}" \
	   "${_nf_brds[11]}" "${_bl:21}" "${_nf_brds[11]}" "${_bl:25}" \
	   "${_nf_brds[11]}"  "${_bl::_mlen+(_mlen-1)/8}" "${_nf_brds[11]}" \
	   "${_bl:: 35 - _mlen - (_mlen-1) / 8 }" "${_nf_brds[12]}"
}
# shellcheck disable=SC2154 # ${headline[@],,} is referenced but not assigned
getLocalNet() { # Get local config from /proc/net/ pseudo files
    # getLocalNet [-v VARNAME]

    # shellcheck disable=SC2178 # _outar is still an array
    if [[ $1 == -v ]];then local -n _outar="$2";shift 2;else local _outar;fi
    _outar=()
    local headline _oIP _oMsk _oGate _tag _ip _lip _lnet _ints _imsk _base _oIf _gates
    while read -r _tag _ip;do
	case $_tag in
	    "+--" )	IFS=' /' read -r _ _ip _tag _ <<<"$_ip"
			[[ $_tag == 2 ]] && mask2int -v _lnet "$_ip";;
	    \|-- )	_lip=$_ip ;;
	    Local: )	break ;;
	    /32 )	if [[ $_ip == host\ LOCAL ]] &&
			       [[ $_lip != 127.0.0.* ]];then
			    ip2int -v _ints[${#_ints[@]}] "$_lip"
			    mask2int -v _imsk[${#_imsk[@]}] "${_lnet:-32}"
			fi
			;;
	esac
    done < /proc/net/fib_trie
    { read -ra headline
      local "${headline[@],,}"
      while read -r "${headline[@],,}";do
	  if [[ $gateway == 00000000 ]] ;then
	      be2int -v _base "$destination"
	      be2int -v _mask "$mask"
	      for _tag in "${!_ints[@]}" ;do
		  (( (_ints[_tag] & _mask ) == _base )) &&
		      _oIP[_tag]=${_ints[_tag]} _oMsk[_tag]="$_mask" \
			  _oIf[_tag]="$iface"
	      done
	  elif [[ $destination == 00000000 ]] && [[ $destination == "$mask" ]]
	  then
	      be2int -v _ip "$gateway"
	      _gates[_ip]="$iface"
	  else
	      be2int -v _base "$destination"
	      be2int -v _mask "$mask"
	      be2int -v _ip "$gateway"
	      for _tag in "${!_ints[@]}" ;do
		  (( (_ints[_tag] & _mask ) == _base  )) &&
		      _oIP[_tag]=${_ints[_tag]} _oMsk[_tag]="$_mask" \
			  _oIf[_tag]="$iface" _gates[_ip]="$iface"
	      done
	  fi
      done
      for _ip in "${!_gates[@]}" ;do
	  for _tag in "${!_oIP[@]}" ;do
	      (( (_oIP[_tag] & _oMsk[_tag] ) == (_ip & _oMsk[_tag] ) )) &&
		  [[ ${_oIf[_tag]}  == "${_gates[_ip]}" ]] &&
		  _oGate[_tag]=$_ip
	  done
      done
    } < /proc/net/route
    for _tag in "${!_oIP[@]}" ;do
	int2ip -v _ip "${_oIP[_tag]}"
	int2mlen -v _lip "${_oMsk[_tag]}"
	[[  "${_oGate[_tag]}" ]] &&
	    int2ip -v gateway  "${_oGate[_tag]}" ||
		gateway=''	
        printf -v _outar[${#_outar[@]}] "%s:%s/%d:%s" "${_oIf[_tag]}" \
	       "$_ip" "$_lip" "$gateway"
    done	
    [[ ${_outar@A} != declare\ -a\ _outar=* ]] || printf '%s\n' "${_outar[@]}"
}
commonNet() { # compute smallest common network for many IP/net
    # commonNet <ip address> <ip address> [ip address]...
    local int bin1 bin2 mlen ips
    rangeIp -v int "$@"
    int2bin -v bin1 "${int[0]}"
    int2bin -v bin2 "${int[@]: -1}"
    int2ip -v ips "${int[0]}" "${int[@]: -1}"
    bin1=${bin1// } bin2=${bin2// }
    for ((mlen=32; mlen; mlen--)) ;do
        [[ ${bin1::mlen} == "${bin2::mlen}" ]] && break
    done
    ipcalc "${ips[0]}/$mlen" "${ips[1]}" IP\ {min,max}
}
_reCidrAddIpFromTo() { # add CIDR in whole array as=([intIP start]="intIP end")
    if [[ $1 == -v ]];then local -n _wList="$2";shift 2;else local _wList;fi
    local _start _end _i
    for _i in "${!_wList[@]}" ;do
	[[ $1 -ge $((_i+1)) ]] && [[ $1 -le $((_wList[_i]+1)) ]] && _start=$_i
        [[ $2 -ge $((_i-1)) ]] && [[ $2 -le $((_wList[_i]-1)) ]] && _end=$_i
    done
    if [[ $_start ]] ;then
	if [[ $_end ]] ;then
	    if [[ $_start -lt $_end ]] ;then
		_wList[_start]=${_wList[_end]}
		unset "_wList[_end]"
	    fi
	else
	    if [[ $1 -lt $_start ]];then
		_wList[$1]=${_wList[_start]}
		unset "_wList[_start]"
		_start=$1
	    fi
	    [[ $2 -gt ${_wList[_start]} ]] && _wList[_start]=$2
	fi
    else
	if [[ $_end ]] ;then
	    if [[ $1 -lt $_end ]];then
		_wList[$1]=${_wList[_end]}
		unset "_wList[_end]"
		_end=$1
	    fi
	    [[ $2 -gt ${_wList[_end]} ]] && _wList[_end]=$2
	else
	    _wList[$1]=$2
	fi
    fi
}
_reCidrAddCidr() { #  Format 1 CIDR for adding to reCidrListe

    # shellcheck disable=2034 # Incorrect handling of nameref
    if [[ $1 == -v ]];then local -n _wAr="$2";shift 2;else local _wAr;fi
    local mask=${1#*/} ip=${1%/*} imask
    [[ ${mask//*.*} ]] || mask=32
    mask2int -v imask $mask
    ip2int -v ip "${ip}"
    _reCidrAddIpFromTo -v _wAr $((ip&imask)) $((ip | ((~0 ^ ~0<<+32) ^ imask)))
}
_reCidrComputeMask() { # Internal compute CIDR from limits (start-end)
    if [[ $1 == -v ]];then local -n _arr="$2";shift 2;else local _arr;fi
    local _out
    local -i mask imask
    while [ "$1" ];do
        mask=32
        while mask2int -v imask $((mask-1)) &&
		[[ $1 == $(( ($1>>(33-mask))<<(33-mask) )) ]] &&
		[[ $2 -ge $((  $1 | ((~0 ^ ~0<<+32) ^ imask) )) ]] ;do
            mask+=-1
        done
        int2ip -v _out "$1"
        _arr+=("$_out/$mask")
        mask2int -v imask $mask
        if [[ $2 -gt $(( $1 | ((~0 ^ ~0<<+32) ^ imask) )) ]] ;then
            set -- $(( 1+($1 | ((~0 ^ ~0<<+32) ^ imask)) )) "$2"
        else
            shift 2
        fi
    done
    [[ ${_arr@A} != declare\ -a\ _arr=* ]] || echo "${_arr[*]}"
}
reCIDR() { # Compute shortened list of CIDR for argument or STDIN
    # reCIDR <ip address/masklen> [ip address/masklen]...
    # reCIDR <list_of_IP.txt
    if [[ $1 == -v ]];then local -n _resar="$2";shift 2;else local _resar;fi
    local _line _oneip _cidr _last _ipList=()
    _resar=()
    if [[ $# -gt 0 ]] ;then
	for _oneip ;do
            _reCidrAddCidr -v _ipList "$_oneip"
	done
    else
	while read -r _line;do
	    for _oneip in $_line;do
		_reCidrAddCidr -v _ipList "$_oneip"
	    done
	done
    fi
    _last=-1;
    for _cidr in "${!_ipList[@]}";do
	if [[ $_cidr -gt $_last ]] ;then
            _reCidrComputeMask -v _resar "$_cidr" "${_ipList[_cidr]}"
            _last=${_ipList[_cidr]}
	fi
    done
    [[ ${_resar@A} != declare\ -a\ _resar=* ]] || printf "%s\n" "${_resar[@]}"  
}
