#!/bin/bash
# 
# Bash source file for IPV4 computing
#
# (C) 2011-2012 Felix Hauri - felix@f-hauri.ch
# Licensed under terms of LGPL v3. www.gnu.org

# after sourcing script:
# syntaxe: ipcal xxx.xxx.xxx.xxx/yy or ipcal 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.

# compute 32bit int from ipv4 address
ip2int() {
    _i4_oifs="$IFS"
    IFS="."
    set -- $1
    echo $(( ($1<<24) + ($2<<16) + ($3<<8) + $4))
    IFS="$_i4_oifs"
    unset _i4_oifs
}

# split 32bit int into four 8bit decimal separed by dot.
int2ip() {
    echo $(($1>>24)).$(($1>>16&255)).$(($1>>8&255)).$(($1&255))
}

# compute 32bit int from a single int meaning mask length
mask2int() {
    echo $(( ((1<<32)-1)^((1<<(32-$1))-1) ))
}

# compute binary from 32bit int for display as four bytes
# display a dot as mask length mark
int2bin() {
    _i4_int=""
    for ((_i4_i=32;_i4_i--;));do
	_i4_int=$_i4_int$(( ( $1 >> _i4_i ) & 1 ))
	[ "$2" ] && [ $((32-_i4_i)) -eq $2 ] && _i4_int=$_i4_int'.'
	[ $((_i4_i%8)) -eq 0 ] && _i4_int=$_i4_int' '
    done
    echo $_i4_int
    unset _i4_int _i4_i
}

# compute mask length from 32bit int.
masklen() {
    set -- $(int2bin $1)
    set -- ${1%%0*}${2%%0*}${3%%0*}${4%%0*}
    echo ${#1}
}

# main
ipcal() {
    _i4_host=$(ip2int ${1%/*})
    _i4_mlen=${1#*/}
    if [ "$2" ];then
        _i4_mlen=$(masklen $(ip2int $2))
      fi
    _i4_mask=$(mask2int $_i4_mlen)
    _i4_netw=$((_i4_host & _i4_mask))
    _i4_bcst=$(( _i4_host | ( _i4_mask ^ ( (1<<32) -1))))
    _i4_clas="$(int2bin $_i4_host $_i4_mlen)"
    printf "%-12s %-20s %s\n" Host $(int2ip $_i4_host) "$_i4_clas" 
    _i4_clas=${_i4_clas%%0*} 
    printf "%-12s %-15s = %2d %s\n" Mask \
	$(int2ip $_i4_mask) $_i4_mlen "$(int2bin $_i4_mask $_i4_mlen)"
    printf "%-12s %-20s %s\n" Network \
	$(int2ip $_i4_netw) "$(int2bin $_i4_netw $_i4_mlen)"
    printf "%-12s %-20s %s\n" Broadcast \
	$(int2ip $_i4_bcst) "$(int2bin $_i4_bcst $_i4_mlen)"
    if [ $((_i4_netw+1)) -lt $_i4_bcst ];then
	printf "%-12s %-20d Class \10$((1+${#_i4_clas}))\n" Host/net \
	    $((_i4_bcst-_i4_netw-1))
	printf "%-12s %-20s %s\n" "First host" \
	    $(int2ip $((_i4_netw+1))) "$(int2bin $((_i4_netw+1)) $_i4_mlen)"
	printf "%-12s %-20s %s\n" "Last host" \
	    $(int2ip $((_i4_bcst-1))) "$(int2bin $((_i4_bcst-1)) $_i4_mlen)"
    else
	if [ "$_i4_netw" == "$_i4_bcst" ] ;then
	    printf "%-12s %-20d Class \10$((1+${#_i4_clas}))\n" Host/net 1
	else
	    printf "%-12s %-20d Class \10$((1+${#_i4_clas}))\n" Host/net 2
	fi
    fi
    unset _i4_bcst _i4_mask _i4_netw _i4_host _i4_mlen _i4_clas
}

export -f ip2int int2ip mask2int int2bin masklen ipcal
