4f48e0a79271fb12c98b91ffc3c37462e93684d1
[openwrt/openwrt.git] / package / base-files / files / bin / ipcalc.sh
1 #!/bin/sh
2
3 . /lib/functions/ipv4.sh
4
5 PROG="$(basename "$0")"
6
7 # wrapper to convert an integer to an address, unless we're using
8 # decimal output format.
9 # hook for library function
10 _ip2str() {
11 local var="$1" n="$2"
12 assert_uint32 "$n" || exit 1
13
14 if [ "$decimal" -ne 0 ]; then
15 export -- "$var=$n"
16 elif [ "$hexadecimal" -ne 0 ]; then
17 export -- "$var=$(printf "%x" "$n")"
18 else
19 ip2str "$@"
20 fi
21 }
22
23 usage() {
24 echo "Usage: $PROG [ -d | -x ] address/prefix [ start limit ]" >&2
25 exit 1
26 }
27
28 decimal=0
29 hexadecimal=0
30 if [ "$1" = "-d" ]; then
31 decimal=1
32 shift
33 elif [ "$1" = "-x" ]; then
34 hexadecimal=1
35 shift
36 fi
37
38 if [ $# -eq 0 ]; then
39 usage
40 fi
41
42 case "$1" in
43 */*.*)
44 # data is n.n.n.n/m.m.m.m format, like on a Cisco router
45 str2ip ipaddr "${1%/*}" || exit 1
46 str2ip netmask "${1#*/}" || exit 1
47 shift
48 ;;
49 */*)
50 # more modern prefix notation of n.n.n.n/p
51 str2ip ipaddr "${1%/*}" || exit 1
52 prefix="${1#*/}"
53 assert_uint32 "$prefix" || exit 1
54 if [ "$prefix" -gt 32 ]; then
55 printf "Prefix out of range (%s)\n" "$prefix" >&2
56 exit 1
57 fi
58 prefix2netmask netmask "$prefix" || exit 1
59 shift
60 ;;
61 *)
62 # address and netmask as two separate arguments
63 str2ip ipaddr "$1" || exit 1
64 str2ip netmask "$2" || exit 1
65 shift 2
66 ;;
67 esac
68
69 # we either have no arguments left, or we have a range start and length
70 if [ $# -ne 0 ] && [ $# -ne 2 ]; then
71 usage
72 fi
73
74 if ! bitcount prefix "$netmask"; then
75 printf "Invalid netmask (%s)\n" "$netmask" >&2
76 exit 1
77 fi
78
79 # complement of the netmask, i.e. the hostmask
80 hostmask=$((netmask ^ 0xffffffff))
81 network=$((ipaddr & netmask))
82 broadcast=$((network | hostmask))
83 count=$((hostmask + 1))
84
85 _ip2str IP "$ipaddr"
86 _ip2str NETMASK "$netmask"
87 _ip2str NETWORK "$network"
88
89 echo "IP=$IP"
90 echo "NETMASK=$NETMASK"
91 # don't include this-network or broadcast addresses
92 if [ "$prefix" -le 30 ]; then
93 _ip2str BROADCAST "$broadcast"
94 echo "BROADCAST=$BROADCAST"
95 fi
96 echo "NETWORK=$NETWORK"
97 echo "PREFIX=$prefix"
98 echo "COUNT=$count"
99
100 # if there's no range, we're done
101 [ $# -eq 0 ] && exit 0
102
103 if [ "$prefix" -le 30 ]; then
104 lower=$((network + 1))
105 else
106 lower="$network"
107 fi
108
109 start="$1"
110 assert_uint32 "$start" || exit 1
111 start=$((network | (start & hostmask)))
112 [ "$start" -lt "$lower" ] && start="$lower"
113 [ "$start" -eq "$ipaddr" ] && start=$((start + 1))
114
115 if [ "$prefix" -le 30 ]; then
116 upper=$(((network | hostmask) - 1))
117 else
118 upper="$network"
119 fi
120
121 range="$2"
122 assert_uint32 "$range" || exit 1
123 end=$((start + range - 1))
124 [ "$end" -gt "$upper" ] && end="$upper"
125 [ "$end" -eq "$ipaddr" ] && end=$((end - 1))
126
127 if [ "$start" -gt "$end" ]; then
128 echo "network ($NETWORK/$prefix) too small" >&2
129 exit 1
130 fi
131
132 _ip2str START "$start"
133 _ip2str END "$end"
134
135 if [ "$start" -le "$ipaddr" ] && [ "$ipaddr" -le "$end" ]; then
136 echo "error: address $IP inside range $START..$END" >&2
137 exit 1
138 fi
139
140 echo "START=$START"
141 echo "END=$END"
142
143 exit 0