summaryrefslogtreecommitdiffstats
path: root/package/base-files/files/bin/ipcalc.sh
blob: ae7a5c9598814f37cd487c4f240ee6ad0a717d6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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

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