openwrt/package/base-files/files/bin/ipcalc.sh
Philip Prindeville 0037100e12 base-files: ipcalc.sh handle start and range being empty strings
If we're being paranoid and quote all the arguments to ipcalc.sh,
it's possible to pass in empty start and range arguments.  This
should be handled the same as their being absent.

Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com>
Link: https://github.com/openwrt/openwrt/pull/15946
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2024-07-21 17:31:59 +02:00

144 lines
3.0 KiB
Bash
Executable File

#!/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 "error: address $IP inside range $START..$END" >&2
exit 1
fi
echo "START=$START"
echo "END=$END"
exit 0