Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions files/bin/ipcalc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/sh

. /lib/functions/ipv4.sh

PROG="$(basename "$0")"

# wrapper to convert an integer to an address, unless we're using
# decimal output format.
# hook for library function
_ip2str() {
local var="$1" n="$2"
assert_uint32 "$n" || exit 1

if [ "$decimal" -ne 0 ]; then
export -- "$var=$n"
elif [ "$hexadecimal" -ne 0 ]; then
export -- "$var=$(printf "%x" "$n")"
else
ip2str "$@"
fi
}

usage() {
echo "Usage: $PROG [ -d | -x ] address/prefix [ start limit ]" >&2
exit 1
}

decimal=0
hexadecimal=0
if [ "$1" = "-d" ]; then
decimal=1
shift
elif [ "$1" = "-x" ]; then
hexadecimal=1
shift
fi

if [ $# -eq 0 ]; then
usage
fi

case "$1" in
*/*.*)
# data is n.n.n.n/m.m.m.m format, like on a Cisco router
str2ip ipaddr "${1%/*}" || exit 1
str2ip netmask "${1#*/}" || exit 1
netmask2prefix prefix "$netmask" || exit 1
shift
;;
*/*)
# more modern prefix notation of n.n.n.n/p
str2ip ipaddr "${1%/*}" || exit 1
prefix="${1#*/}"
assert_uint32 "$prefix" || exit 1
if [ "$prefix" -gt 32 ]; then
printf "Prefix out of range (%s)\n" "$prefix" >&2
exit 1
fi
prefix2netmask netmask "$prefix" || exit 1
shift
;;
*)
# address and netmask as two separate arguments
str2ip ipaddr "$1" || exit 1
str2ip netmask "$2" || exit 1
netmask2prefix prefix "$netmask" || exit 1
shift 2
;;
esac

# we either have no arguments left, or we have a range start and length
if [ $# -ne 0 ] && [ $# -ne 2 ]; then
usage
fi

# complement of the netmask, i.e. the hostmask
hostmask=$((netmask ^ 0xffffffff))
network=$((ipaddr & netmask))
broadcast=$((network | hostmask))
count=$((hostmask + 1))

_ip2str IP "$ipaddr"
_ip2str NETMASK "$netmask"
_ip2str NETWORK "$network"

echo "IP=$IP"
echo "NETMASK=$NETMASK"
# don't include this-network or broadcast addresses
if [ "$prefix" -le 30 ]; then
_ip2str BROADCAST "$broadcast"
echo "BROADCAST=$BROADCAST"
fi
echo "NETWORK=$NETWORK"
echo "PREFIX=$prefix"
echo "COUNT=$count"

# if there's no range, we're done
[ $# -eq 0 ] && exit 0
[ -z "$1$2" ] && exit 0

if [ "$prefix" -le 30 ]; then
lower=$((network + 1))
else
lower="$network"
fi

start="$1"
assert_uint32 "$start" || exit 1
start=$((network | (start & hostmask)))
[ "$start" -lt "$lower" ] && start="$lower"
[ "$start" -eq "$ipaddr" ] && start=$((start + 1))

if [ "$prefix" -le 30 ]; then
upper=$(((network | hostmask) - 1))
elif [ "$prefix" -eq 31 ]; then
upper=$((network | hostmask))
else
upper="$network"
fi

range="$2"
assert_uint32 "$range" || exit 1
end=$((start + range - 1))
[ "$end" -gt "$upper" ] && end="$upper"
[ "$end" -eq "$ipaddr" ] && end=$((end - 1))

if [ "$start" -gt "$end" ]; then
echo "network ($NETWORK/$prefix) too small" >&2
exit 1
fi

_ip2str START "$start"
_ip2str END "$end"

if [ "$start" -le "$ipaddr" ] && [ "$ipaddr" -le "$end" ]; then
echo "warning: address $IP inside range $START..$END" >&2
fi

echo "START=$START"
echo "END=$END"

exit 0
Loading