#!/usr/local/bin/cbsd
#v12.2.0
# Detect first available IPv6 from ippool's
MYARG=""
MYOPTARG="cleanup dhcpd_helper ip4pool lease_time lock pass"
MYDESC="Detect first available IPv4 from pools"
ADDHELP="

${H3_COLOR}Description${N0_COLOR}:

This script consistently suggests free IP addresses if the user does not specify
a static address. Can pass a request for any external script (see dhcpd.conf config).
Works with 'nodeippool' variable ranges by default (see 'cbsd initenv-tui').

 ${UNDERLINE}Return codes${N0_COLOR}:

   0 - IP found
   1 - Unknown error
   2 - All pools are exhausted

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}cleanup=${N0_COLOR}      - flush leasetime for cleanup= list, e.g:
                 cleanup=\"10.0.0.1 10.0.0.2\";
 ${N2_COLOR}dhcpd_helper=${N0_COLOR} - <path_to_executable> overwrite dhcpd_helper settings
                 from dhcpd.conf;
 ${N2_COLOR}ip4pool=${N0_COLOR}      - use alternative pool, comma-separated if multiple
                 valid value sample:
                   ip4pool=\"192.168.0.0/24\"
                   ip4pool=\"192.168.0.5-10\"
                   ip4pool=\"10.0.0.3 10.0.0.4 10.0.0.250 10.0.0.251\"
                   ip4pool=\"10.0.0.2 10.0.0.3 10.0.0.200-204 192.168.0.20/29\";
 ${N2_COLOR}lease_time=${N0_COLOR}   - lock/lease for X seconds to avoid race/collisions
                 on concurrent request, default is: 30;
 ${N2_COLOR}lock=${N0_COLOR}         - set to '0' to prevent recursive/lock
                 (race/collisions protection);

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd dhcpd
 # cbsd dhcpd ip4pool=\"192.168.0.5-10\"
 # cbsd dhcpd dhcpd_helper=\"/root/bin/myhelper\"

${H3_COLOR}See also${N0_COLOR}:

 # cbsd initenv-tui --help
 # cat ~cbsd/etc/defaults/dhcpd.conf
 External helper sample: https://www.convectix.com/en/13.0.x/wf_ipam_ssi.html

"

. ${subrdir}/nc.subr
lock=1
pass=
lease_time=30
cleanup=
dhcpd_helper=
. ${cbsdinit}

[ -n "${dhcpd_helper}" ] && odhcpd_helper="${dhcpd_helper}"

# dhcpd_helper?
readconf dhcpd.conf

#
# ipv4_to_ip10 ipv4 ip10
#	Function converts IPv4 address to decimal address. $1 must be IPv4
#	address. $2 must be name of variable to save decimal address.
#
ipv4_to_ip10()
{
	local __ipv4 __ip10
	local _ip1 _ip2 _ip3 _ip4 _oct

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	__ipv4="$1"
	__ip10="$2"

	_ip1=${__ipv4%.*.*.*}
	_ip2=${__ipv4%.*.*}; _ip2=${_ip2#*.}
	_ip3=${__ipv4#*.*.}; _ip3=${_ip3%.*}
	_ip4=${__ipv4#*.*.*.}

	for _oct in "${_ip1}" "${_ip2}" "${_ip3}" "${_ip4}"; do
		if [ -z "${_oct}" -o "${_oct}" = "${__ipv4}" ]; then
			return 1
		fi

		if is_number "${_oct}"; then
			return 1
		fi

		if [ "${_oct}" -lt 0 -o "${_oct}" -gt "$(($pow8 - 1))" ]; then
			return 1
		fi
	done

	eval eval \${__ip10}=$((${_ip1} << 24 | ${_ip2} << 16 | ${_ip3} << 8 | ${_ip4}))

	return 0
}

#
# ip10_to_ipv4 ip10 ipv4
#	Function converts decimal address to IPv4 address. $1 must be decimal
#	address. $2 must be name of variable to save IPv4 address.
#
ip10_to_ipv4()
{
	local __ip10 __ipv4
	local _ip1 _ip2 _ip3 _ip4

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	__ip10="$1"
	__ipv4="$2"

	if is_number "${__ip10}"; then
		return 1
	fi

	if [ "${__ip10}" -lt 0 -o "${__ip10}" -gt "$(($pow32 - 1))" ]; then
		return 1
	fi

	_ip1=$((((${__ip10} >> 24)) & 0xFF))
	_ip2=$((((${__ip10} >> 16)) & 0xFF))
	_ip3=$((((${__ip10} >> 8)) & 0xFF))
	_ip4=$((${__ip10} & 0xFF))

	eval eval \${__ipv4}="${_ip1}.${_ip2}.${_ip3}.${_ip4}"

	return 0
}

#
# is_ipv4_belong_net ipv4 network/mask
#	Function returns 0 if $1 address belongs $2 network, otherwise return 1.
#	$1 must be IPv4 address. $2 must be IPv4 network in CIDR notation.
#
is_ipv4_belong_net()
{
	local _ipv4 _network _mask _ip_net
	local _ip10 _mask10 _ip_net10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_ipv4="$1"
	_net="$2"

	_network="${_net%/*}"
	_mask="${_net#*/}"

	if is_number "${_mask}"; then
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	if ! ipv4_to_ip10 "${_ipv4}" "_ip10"; then
		return 1
	fi

	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	_ip_net10=$((${_ip10} & ${_mask10}))
	if ! ip10_to_ipv4 "${_ip_net10}" "_ip_net"; then
		return 1
	fi

	if [ "${_ip_net}" != "${_network}" ]; then
		return 1
	fi

	return 0
}

#
# get_netmask network/mask netmask
#	Function generates IPv4 netmask from IPv4 network in CIDR notation.
#	$1 must be IPv4 network in CIDR notation. $2 must be name of variable
#	to save netmask.
#
get_netmask()
{
	local _net _netmask _network _mask _hosts _mask_tmp
	local _mask10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_net="$1"
	_netmask="$2"

	_network="${_net%/*}"
	_mask="${_net#*/}"

	if is_number "${_mask}"; then
		echo "NOT NUMBER: ${_mask}"
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	if ! ip10_to_ipv4 "${_mask10}" "_mask_tmp"; then
		return 1
	fi

	eval eval \${_netmask}="${_mask_tmp}"

	return 0
}

#
# get_network ipv4/mask network/mask
#	Function generates IPv4 network in CIDR notation from IPv4 address
#	with mask in CIDR notation. $1 must be IPv4 address with mask in CIDR
#	notation. $2 must be name of variable to save network.
#
get_network()
{
	local _ipv4mask _network _ipv4 _mask _hosts _network_tmp
	local _ip10 _mask10 _network10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_ipv4mask="$1"
	_network="$2"

	_ipv4="${_ipv4mask%/*}"
	_mask="${_ipv4mask#*/}"

	if is_number "${_mask}"; then
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	if ! ipv4_to_ip10 "${_ipv4}" "_ip10"; then
		return 1
	fi
	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	_network10=$((${_ip10} & ${_mask10}))
	if ! ip10_to_ipv4 "${_network10}" "_network_tmp"; then
		return 1
	fi

	eval eval \${_network}="${_network_tmp}/${_mask}"

	return 0
}

#
# get_minhost network/mask minhost
#	Function generates minimum host for network. $1 must be IPv4 network
#	in CIDR notation. $2 must be name of variable to save minimum host.
#
get_minhost()
{
	local _net _minhost _network _mask _hosts _minhost_tmp
	local _network10 _mask10 _minhost10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_net="$1"
	_minhost="$2"

	_network="${_net%/*}"
	_mask="${_net#*/}"

	if is_number "${_mask}"; then
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	# Network with /31 mask has minimum host equal to network
	if [ "${_mask}" -eq 31 ]; then
		eval eval \${_minhost}="${_network}"
		return 0
	fi

	# Network with /32 mask has no minimum host
	if [ "${_mask}" -eq 32 ]; then
		return 0
	fi

	if ! ipv4_to_ip10 "${_network}" "_network10"; then
		return 1
	fi
	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	_minhost10=$((${_network10} + 1))

	if ! ip10_to_ipv4 "${_minhost10}" "_minhost_tmp"; then
		return 1
	fi

	eval eval \${_minhost}="${_minhost_tmp}"

	return 0
}

#
# get_maxhost network/mask maxhost
#	Function generates maximum host for network. $1 must be IPv4 network
#	in CIDR notation. $2 must be name of variable to save maximum host.
#
get_maxhost()
{
	local _reserve _net _maxhost _network _mask _hosts _maxhost_tmp
	local _network10 _mask10 _maxhost10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_reserve=2

	_net="$1"
	_maxhost="$2"

	_network="${_net%/*}"
	_mask="${_net#*/}"

	if is_number "${_mask}"; then
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	if [ "${_mask}" -ge 31 ]; then
		_reserve=1
	fi

	if ! ipv4_to_ip10 "${_network}" "_network10"; then
		return 1
	fi
	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	_maxhost10=$((${_network10} + ${_hosts} - ${_reserve}))

	if ! ip10_to_ipv4 "${_maxhost10}" "_maxhost_tmp"; then
		return 1
	fi

	eval eval \${_maxhost}="${_maxhost_tmp}"

	return 0
}

#
# get_broadcast network/mask broadcast
#	Function generates broadcast address for network. $1 must be IPv4
#	network in CIDR notation. $2 must be name of variable to save
#	broadcast address.
#
get_broadcast()
{
	local _net _broadcast _network _mask _hosts _broadcast_tmp
	local _network10 _mask10 _broadcast10

	if [ $# -ne 2 -o -z "$1" -o -z "$2" ]; then
		return 1
	fi

	_net="$1"
	_broadcast="$2"

	_network="${_net%/*}"
	_mask="${_net#*/}"

	if is_number "${_mask}"; then
		return 1
	fi

	if [ "${_mask}" -lt 0 -o "${_mask}" -gt 32 ]; then
		return 1
	fi

	# Network with /31 or /32 mask has no broadcast address
	if [ "${_mask}" -ge 31 ]; then
		return 0
	fi

	if ! ipv4_to_ip10 "${_network}" "_network10"; then
		return 1
	fi
	_hosts=$((1 << $((32 - ${_mask}))))
	_mask10=$(($pow32 - ${_hosts}))
	_broadcast10=$((${_network10} + ${_hosts} - 1))

	if ! ip10_to_ipv4 "${_broadcast10}" "_broadcast_tmp"; then
		return 1
	fi

	eval eval \${_broadcast}="${_broadcast_tmp}"

	return 0
}

# Export network calc result
# $netmask ( 255.255.255.0 )
# ${cidr_mask} ( 24 )
# $network ( 10.0.0.0/24 )
# $minhost ( 10.0.0.1 )
# $maxhost (  10.0.0.254 )
# $broadcast ( 10.0.0.255 )
# $numhosts ( 254 )
init_network()
{
	local _ip _hosts _reserve _s1 _s2 _s3 _s4
	local _is_cidr _is_range _pos _s4s _s4e

	netmask=
	cidr_mask=
	network=
	minhost=
	maxhost=
	broadcast=

	[ $# -ne 1 ] && err 1 "dhcpd: init_network: usage: $0 ip.ip.ip.ip/mask"

	_ip=${1%/*}
	cidr_mask=${1#*/}

	_is_range=0	# has x.x.x.a-z ?
	_is_cidr=0	# has /prefix

	if [ "${_ip}" = "${1}" -o "${cidr_mask}" = "${1}" ]; then
		_is_cidr=0
	else
		_is_cidr=1
	fi

	sqllistdelimer="."
	sqllist "${_ip}" _s1 _s2 _s3 _s4
	sqllistdelimer=

	strpos --str="${_s4}" --search="-"
	_pos=$?
	if [ ${_pos} -eq 0 ]; then
		_is_range=0
	else
		_s4s=${_s4%-*}
		_s4e=${_s4#*-}
		[ -z "${_s4s}" -o -z "${_s4e}" ] && err 1 "dhcpd: init_network: last octet range error: ${_s4}"
		_is_range=1
	fi

#	[ ${_is_cidr} -eq 0 -a ${_is_range} -eq 0 ] && err 1 "dhcpd: init_network: usage: $0 ip.ip.ip.ip/mask or ip.ip.ip.ip1-ip2"

	if [ ${_is_cidr} -eq 1 ]; then
		if ! get_netmask "${_ip}/${cidr_mask}" "netmask"; then
			err 1 "dhcpd: init_network: bad netmask ${_ip}/${cidr_mask}"
		fi

		if ! get_network "${_ip}/${cidr_mask}" "network"; then
			err 1 "dhcpd: init_network: bad IP ${_ip}/${cidr_mask}"
		fi
		get_minhost "${network}" "minhost"
		get_maxhost "${network}" "maxhost"
		get_broadcast "${network}" "broadcast"
		_hosts=$((1 << $((32 - ${cidr_mask}))))
		_reserve=2
		if [ "${cidr_mask}" -ge 31 ]; then
			_reserve=0
		fi
		numhosts=$(( _hosts - _reserve ))
	elif [ ${_is_range} -eq 1 ]; then
		minhost="${_s1}.${_s2}.${_s3}.${_s4s}"
		maxhost="${_s1}.${_s2}.${_s3}.${_s4e}"
		numhosts=$(( _s4e - _s4s ))
	else
		# just IP sequence: 10.0.0.1 10.0.0.5 10.0.0.100 ..
		minhost="${_s1}.${_s2}.${_s3}.${_s4}"
		maxhost="${_s1}.${_s2}.${_s3}.${_s4}"
		numhosts=1
	fi

	return 0
}

LOCKFILE="${ftmpdir}/dhcpd.lock"
LEASE_FILE="${tmpdir}/dhcpd.lease"
# list of locked/skip IPS
LOCKFILE_SKIPLIST=

[ -n "${odhcpd_helper}" ] && dhcpd_helper="${odhcpd_helper}"

if [ "${dhcpd_helper}" != "internal" ]; then
	cbsdlogger NOTICE ${CBSD_APP}: use external dhcpd_helper: ${dhcpd_helper}
	[ ! -x "${dhcpd_helper}" ] && log_err 1 "${N1_COLOR}${CBSD_APP}: external helper not executable: ${N2_COLOR}${dhcpd_helper}${N0_COLOR}"

	# rebuild arg list ( + add pass )
	# Pass '"' as \" in cmd
	INIT_IFS="${IFS}"
	IFS="~"
	cmd="$@"
	IFS="${INIT_IFS}"
	cmd=$( while [ -n "${1}" ]; do
		IFS="~"
		strpos --str="${1}" --search="="
		_pos=$?
		if [ ${_pos} -eq 0 ]; then
			# not params=value form
			#printf "${1} "         # (printf handles -args (with dashes)
			#echo -n "${1} "
			shift
			continue
		fi
		_arg_len=$( strlen ${1} )
		_pref=$(( _arg_len - _pos ))
		ARG=$( substr --pos=0 --len=${_pos} --str="${1}" )
		VAL=$( substr --pos=$(( ${_pos} +2 )) --len=${_pref} --str="${1}" )
		if [ -z "${ARG}" -o -z "${VAL}" ]; then
			shift
			continue
		fi
		printf "${ARG}='${VAL}' "
		shift
	done )

	cbsd_lock 60 ${LOCKFILE} ${dhcpd_helper} ${cmd}
	ret=$?
	# should never happen
	exit ${ret}
fi

if [ -n "${cleanup}" ]; then
	[ ! -r ${LEASE_FILE} ] && exit 0
	for i in ${cleanup}; do
		${ARP_CMD} -nd ${i} > /dev/null 2>&1
		${SED_CMD} -i${SED_DELIMER}'' "/${i}|/d" ${LEASE_FILE}
	done
	exit 0
fi


# we need the atomicity of the operation to exclude
# the simultaneous selection of the same free IP
# use file as lock and temp database in
# <ip>:<end_lease_time>
# <ip>:<end_lease_time>
if [ -z "${pass}" ]; then
	if [ "${lock}" = "1" ]; then
		# rebuild arg list ( + add pass )
		# Pass '"' as \" in cmd
		INIT_IFS="${IFS}"
		IFS="~"
		cmd="$@"
		IFS="${INIT_IFS}"
		while [ -n "${1}" ]; do
			IFS="~"
			strpos --str="${1}" --search="="
			_pos=$?
			if [ ${_pos} -eq 0 ]; then
				# not params=value form
				#printf "${1} "         # (printf handles -args (with dashes)
				#echo -n "${1} "
				shift
				continue
			fi
			_arg_len=$( strlen ${1} )
			_pref=$(( _arg_len - _pos ))
			ARG=$( substr --pos=0 --len=${_pos} --str="${1}" )
			VAL=$( substr --pos=$(( ${_pos} +2 )) --len=${_pref} --str="${1}" )
			if [ -z "${ARG}" -o -z "${VAL}" ]; then
				shift
				continue
			fi
			#printf "${ARG}='${VAL}' "
			shift
		done
		cbsd_lock 60 ${LOCKFILE} /usr/local/bin/cbsd dhcpd ${cmd} pass=1
		ret=$?
		# should never happen
		exit ${ret}
	fi
fi

[ -n "${ip4pool}" ] && nodeippool=$( echo ${ip4pool} | ${TR_CMD} "," " " )

# prune/purge old records
if [ -r ${LEASE_FILE} ]; then
	${TRUNCATE_CMD} -s0 ${LEASE_FILE}.swap
	cur_time=$( ${DATE_CMD} +%s )
	eval $( ${CAT_CMD} ${LEASE_FILE} | while read items; do
		p1=${items%%|*}
		p2=${items##*|}
		[ -z "${p1}" -o -z "${p2}" ] && continue
		if is_number "${p2}"; then
			continue
		fi
		if [ ${p2} -gt ${cur_time} ]; then
			# still valid
			echo "${items}" >> ${LEASE_FILE}.swap
			if [ -z "${LOCKFILE_SKIPLIST}" ]; then
				LOCKFILE_SKIPLIST="${p1}"
			else
				LOCKFILE_SKIPLIST="${LOCKFILE_SKIPLIST} ${p1}"
			fi
		fi
	echo "LOCKFILE_SKIPLIST=\"${LOCKFILE_SKIPLIST}\""
	done )
	${MV_CMD} ${LEASE_FILE}.swap ${LEASE_FILE}
fi

### MAIN
pow32=$((1 << 32))
pow24=$((1 << 24))
pow16=$((1 << 16))
pow8=$((1 << 8))

# use args network instead of global CBSD settings/variable
#if [ -n "${ip4pool}" ]; then
#	nodeippool=$( echo ${ip4pool} | ${TR_CMD} "," " " )
#fi

[ -z "${nodeippool}" ] && err 1 "${N1_COLOR}no nodeippool${N0_COLOR}"
[ "${nodeippool}" = "0" ] && err 1 "${N1_COLOR}no nodeippool${N0_COLOR}"
[ "${nodeippool}" = "(null)" ] && err 1 "${N1_COLOR}no nodeippool${N0_COLOR}"

# Multiple network loop
for tmpnet in ${nodeippool}; do

	init_network "${tmpnet}"

	# start octets
	s1=
	s2=
	s3=
	s4=

	# end octets
	e1=
	e2=
	e3=
	e4=

	sqllistdelimer="."
	sqllist "${minhost}" s1 s2 s3 s4
	sqllist "${maxhost}" e1 e2 e3 e4
	sqllistdelimer=

	# bhyve doesn't have ip4_addr anymore, selectom from jails only
	#existing_ipjail=$( cbsdsqlro local SELECT DISTINCT ip4_addr FROM bhyve WHERE ip4_addr != \'0\' AND ip4_addr != \'DHCP\' UNION SELECT DISTINCT ip4_addr FROM jails WHERE ip4_addr != \'0\' AND ip4_addr != \'DHCP\' | ${XARGS_CMD} )
	existing_ipjail=$( cbsdsqlro local SELECT DISTINCT ip4_addr FROM jails WHERE ip4_addr != \'0\' AND ip4_addr != \'DHCP\' | ${TR_CMD} "," " " | ${XARGS_CMD} )

	skip_ip="${nodeip}"

	# prepare skip ip list
	for i in ${existing_ipjail}; do
		ipwmask ${i}
		[ -z "${IWM}" ] && continue
		iptype ${IWM}
		[ $? -eq 1 ] && skip_ip="${skip_ip} ${IWM}"
	done

	found=0

	[ -z "${s1}" -o -z "${e2}" ] && break

	# assign wanted octet by starting octet
	for w1 in $( ${SEQ_CMD} ${s1} ${e1} ); do
		[ -z "${s2}" -o -z "${e2}" ] && break
		for w2 in $( ${SEQ_CMD} ${s2} ${e2} ); do
			[ -z "${s3}" -o -z "${e3}" ] && break
			for w3 in $( ${SEQ_CMD} ${s3} ${e3} ); do
				# last wanted octet loop
				[ -z "${s4}" -o -z "${e4}" ] && break
				for w4 in $( ${SEQ_CMD} ${s4} ${e4} ); do
					skip=0
					tmpip="${w1}.${w2}.${w3}.${w4}"
					iptype ${tmpip} >/dev/null 2>&1
					[ $? -ne 1 ] && continue
					for n in ${skip_ip} ${LOCKFILE_SKIPLIST}; do
						[ "${n}" = "${tmpip}" ] && skip=1
					done
					[ ${skip} -eq 1 ] && continue
					# regulate via conf ?
					${ARP_CMD} -dn ${tmpip} > /dev/null 2>&1
					case "${platform}" in
						Linux)
							checkip ip="${tmpip}" check=1
							_ret=$?
							if [ ${_ret} -eq 2 ]; then
								# found
								_ret=1
							else
								_ret=0
							fi
							;;
						DragonFly)
							checkip ip="${tmpip}" check=1
							_ret=$?
							if [ ${_ret} -eq 2 ]; then
								# found
								_ret=1
							else
								_ret=0
							fi
							;;
						*)
							${miscdir}/chk_arp_byip --pingnum=1 --pingtimeout=0.001 --ip=${tmpip}
							_ret=$?
							;;
					esac
					case ${_ret} in
						0)
							found=1
							#break 4
							break
							;;
						*)
							continue
							;;
					esac
				done # w4
				[ ${found} -eq 1 ] && break
			done # w3
			[ ${found} -eq 1 ] && break
		[ ${found} -eq 1 ] && break
		done # w2
	[ ${found} -eq 1 ] && break
	done # w1
[ ${found} -eq 1 ] && break
done # multiple network

if [ -n "${tmpip}" -a ${found} -eq 1 ]; then
	if [ -n "${mycidr}" ]; then
		cbsdlogger NOTICE ${CBSD_APP}: found next available IP: ${tmpip}/${mycidr}
		echo "${tmpip}/${mycidr}"
	else
		cbsdlogger NOTICE ${CBSD_APP}: found next available IP: ${tmpip}
		echo ${tmpip}
	fi
else
	cbsdlogger WARNING ${CBSD_APP}: no free IP: all pools are exhausted? ${nodeippool}
	# looks like all pools are exhausted
	exit 2
fi

cur_time=$( ${DATE_CMD} +%s )
lease_time_end=$(( cur_time + lease_time ))
echo "${tmpip}|${lease_time_end}" >> ${LEASE_FILE}

exit 0
