#!/bin/bash
######################################################
#
# Author:	Ceasar Sun Chen-kai <ceasar@nchc.org.tw>
# License:	GPL
# Description: 	List IP addresses from a set of given subnet and count
# Usage:	ipcalc-list 192.168.21.13/24 -n 10 # to list ip from 192.168.21.13 ~ 192.168.21.22
# ChangeLog:	
#	* 20100603	First version
#	* 20100604	Fix fisrt character of binary type ip address is 0 within first ip session
#	* 20140321	Add option "-r"
######################################################
# Load DRBL setting and functions
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"

. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
[ -e /etc/drbl/drbl-ocs.conf ] && . /etc/drbl/drbl-ocs.conf
[ -e $DRBL_SCRIPT_PATH/sbin/ocs-functions ] && . $DRBL_SCRIPT_PATH/sbin/ocs-functions

######################################################
# Sub functions
######################################################

# Print usage
Usage(){
	echo "$0 : List legal IP addresses from a given start IP/subnet and count";
	echo "Usage: $0 <ipcalc_parameters> -c <count> [-b] [-v]";
	echo " -b, --binary	        Binary output for IP address"
	echo " -c,--count [COUNT]	Amount of IP address, positive number for ascending order, negative for descending, ex: 10, -5"
	echo " -r, --range <START_IP END_IP NETMASK>	Assign IP range and netmask"
	echo " -v, --verbose	        Verbose mode, if not enough IP address, show what we have"
	echo ""
	echo "Help options:";
	echo " -h, --help	Show this help message"
	echo "	"
	echo "Example:"
	echo "To list 20 ascending legal IP addresses in form 192.168.121.1/24."
	echo " $0 192.168.121.1/24 -c 20"
	echo "To list 3 legal descending IP addresses in form 172.16.1.32/16 and convert IP address as binary."
	echo " $0 172.16.1.32 255.255.0.0 -c -3 -b"
	echo "To list 10 descnding IP addresses in form 10.0.0.3/8. Nothing will be shown due to not enough addresses in the range"
	echo " $0 10.0.0.3/8 -c -10"
	echo "But use VERBOSE mode. Output: '10.0.0.3 10.0.0.2 10.0.0.1'"
	echo " $0 10.0.0.3/8 -c -10 -v"
	echo "To list all IP addresss form 172.16.0.1 to 172.16.1.46 with netmask 16. Output: '172.16.0.1 172.16.0.2 ... 172.16.1.45 172.16.1.46'"
	echo " $0 -r 172.16.0.1 172.16.1.46 255.255.0.0 (or:  $0 -r 172.16.0.1 172.16.1.46 16)"
	echo ""
	echo "ipcalc_parameters : <start_ip netmask> or <start_ip/netmask>"
	echo " <start_ip netmask> , ex: 192.168.11.1 255.255.255.0"
	echo "the same with "
	echo " <start_ip/netmask> , ex: 192.168.11.1/24"
}

######################################################
#Main 
######################################################
declare _OUTPUT_MODE _VERBOSE _COUNT _IPCALC_PARM _IP_START _IP_END _NETMASK
declare -a _IP_ARRAY

declare __VERSION='1.0.0'
declare _OUTPUT_MODE=d
declare _VERBOSE=0
declare _COUNT=1

[ $# -eq 0 ] && Usage && exit 1;
while [ $# -gt 0 ]; do
	case "$1" in
		-b|--binary)
			shift; _OUTPUT_MODE="b";
		        ;;
		-c|--count)
			shift;
			if [[ $1 =~ ^-*[[:digit:]]+$ ]]; then
				_COUNT=$1
			else
				echo "Illegal value for option \"-c\"." >&2
				Usage; exit 1;
			fi
			shift;
		        ;;
		-h|--help)
			Usage;
			exit;
		        ;;
		-r|--range)
			shift;
			if [[ $1 =~ ^[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}$ ]] ; then 
				_IP_START=$1 
			else
				echo "Illegal 'Start' IP format !?" >&2
				Usage; exit 1;
			fi
			shift;
			if [[ $1 =~ ^[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}$ ]] ; then 
				_IP_END=$1
			else
				echo "Illegal 'End' IP format !?" >&2
				Usage; exit 1;
			fi
			shift;
			if [[ $1 =~ ^[[:digit:]]{1,2}$ ]] && [ $1 -le 30 ] ; then 
				_NETMASK=$1 
			elif [[ $1 =~ ^[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}$ ]] ; then 
				_NETMASK=$1
			else 
				echo "Illegal netmask format !?" >&2
				Usage; exit 1;
			fi
			shift;
		        ;;
		-v|--verbose)
			shift; _VERBOSE=1;
		        ;;
		*)
			[ -n "$_IPCALC_PARM" ] && _IPCALC_PARM="$_IPCALC_PARM $1" || _IPCALC_PARM="$1";
			shift;
		        ;;
	esac
done

# chek if ipcalc_parameters format : '192.168.11.1 255.255.255.0' or '192.168.11.1/24'
# or not list a range of IPs

if [ -n "$_IP_START" -a  -n "$_IP_END" ] ; then
	real_bin_start_ip="$(drbl-ipcalc $_IP_START/0 | grep -E "^Address:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
	real_dec_start_ip="$(LC_ALL=C echo "ibase=2; obase=A; $real_bin_start_ip" | bc)"
	real_bin_end_ip="$(drbl-ipcalc $_IP_END/0 | grep -E "^Address:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
	real_dec_end_ip="$(LC_ALL=C echo "ibase=2; obase=A; $real_bin_end_ip" | bc)"

	[ "$(expr $real_dec_end_ip - $real_dec_start_ip)" -ge 0 ] && _COUNT="$(expr $real_dec_end_ip - $real_dec_start_ip + 1)" || _COUNT="$(expr $real_dec_end_ip - $real_dec_start_ip - 1)"
	_IPCALC_PARM="${_IP_START}/${_NETMASK}"
	_VERBOSE=1
fi
#echo "_IPCALC_PARM='$_IPCALC_PARM' ; _COUNT='$_COUNT' "

if [ -z "$(echo $_IPCALC_PARM | grep -E '^[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}\/[[:digit:]]{1,2}$')" ] && [ -z "$(echo $_IPCALC_PARM | grep -E '^[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}[[:space:]]+[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}.[[:digit:]]{1,3}$')" ] && [ -z "$_IP_START" -o  -z "$_IP_END" ]; then
	echo "Illegal parameters for ipcalc!" >&2
	Usage; exit 1;
fi

# Add "1" on first string for solve if the fisrt session of ip is 0xxxxxxx.~
# 
# Exit when it gave an invild netmask
[[ -n "$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^INVALID MASK")" ]] && echo "Illegal parameters for netmask:'$_IPCALC_PARM'" >&2 &&  exit 1;

bin_start_ip=1"$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^Address:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
dec_start_ip="$(LC_ALL=C echo "ibase=2; obase=A; $bin_start_ip" | bc)"
bin_max_ip=1"$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^HostMax:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
dec_max_ip="$(LC_ALL=C echo "ibase=2; obase=A; $bin_max_ip" | bc)"
bin_min_ip=1"$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^HostMin:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
dec_min_ip="$(LC_ALL=C echo "ibase=2; obase=A; $bin_min_ip" | bc)"

bin_network_ip=1"$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^Network:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
dec_network_ip="$(LC_ALL=C echo "ibase=2; obase=A; $bin_network_ip" | bc)"
bin_broadcast_ip=1"$(LC_ALL=C drbl-ipcalc $_IPCALC_PARM  | grep -E "^Broadcast:" | awk -F" " '{print $3 $4 }'| sed -e "s/\.//g")"
dec_broadcast_ip="$(LC_ALL=C echo "ibase=2; obase=A; $bin_broadcast_ip" | bc)"

#echo "$_OUTPUT_MODE, $_VERBOSE, $_COUNT, '$_IPCALC_PARM', $dec_start_ip, $dec_min_ip, $dec_max_ip";

if [[ $_COUNT =~ ^[[:digit:]]+$ ]]; then
	for ((i=0; i<$_COUNT; i++)) ;do
		dec_current_ip=$(expr $dec_start_ip + $i)
		if [ $dec_max_ip -ge $dec_current_ip ] && [ $dec_min_ip -le $dec_current_ip ] && [ $dec_network_ip -ne $dec_current_ip ] && [ $dec_broadcast_ip -ne $dec_current_ip ]; then
			_tmp_bin_ip="$(LC_ALL=C echo "ibase=A; obase=2; $dec_current_ip" | bc)"
			_IP_ARRAY[${#_IP_ARRAY[*]}]=${_tmp_bin_ip:1:32}
		elif [ $_VERBOSE = 0 ] ; then
			_IP_ARRAY=() ; echo "Not enough IP addresses to output!"  >&2
			exit 1;
		fi		
	done
else 
	for ((i=0; i>$_COUNT; i--)) ;do
		dec_current_ip=$(expr $dec_start_ip + $i)
		if [ $dec_max_ip -ge $dec_current_ip ] && [ $dec_min_ip -le $dec_current_ip ]  && [ $dec_network_ip -ne $dec_current_ip ] && [ $dec_broadcast_ip -ne $dec_current_ip ]; then
			_tmp_bin_ip=`echo "ibase=A; obase=2; $dec_current_ip" | bc`
			_IP_ARRAY[${#_IP_ARRAY[*]}]=${_tmp_bin_ip:1:32}
		elif [ $_VERBOSE = 0 ] ; then
			_IP_ARRAY=() ;  echo "Not enough IP addresses to output!"  >&2
			exit 1;
		fi		
	done
fi 

for ((i=0; i<${#_IP_ARRAY[*]}; i++)) ;do
	[ $_OUTPUT_MODE = 'b' ] && p1=${_IP_ARRAY[$i]:0:8} ||  p1="$(echo "ibase=2; obase=A; ${_IP_ARRAY[$i]:0:8}" | bc )"
	[ $_OUTPUT_MODE = 'b' ] && p2=${_IP_ARRAY[$i]:8:8} ||  p2="$(echo "ibase=2; obase=A; ${_IP_ARRAY[$i]:8:8}" | bc )"
	[ $_OUTPUT_MODE = 'b' ] && p3=${_IP_ARRAY[$i]:16:8} ||  p3="$(echo "ibase=2; obase=A; ${_IP_ARRAY[$i]:16:8}" | bc )"
	[ $_OUTPUT_MODE = 'b' ] && p4=${_IP_ARRAY[$i]:24:8} ||  p4="$(echo "ibase=2; obase=A; ${_IP_ARRAY[$i]:24:8}" | bc )"
	echo $p1.$p2.$p3.$p4
done

exit 0;
