ETOOBUSY 🚀 minimal blogging for the impatient
Netmask expansion in Perl/Shell
TL;DR
Need to transform a netmask from the “number of bits” to the “full quad-dotted form”? Here’s a trick.
Sometimes you get an IPv4 Address in the form A.B.C.D/X
, where X
represents the number of leading bits in the netmask that are turned on in
the mask.
A netmask in IPv4 can have between 0 and 32 (both included) leading bits up, i.e.:
0 -> 00000000000000000000000000000000
1 -> 10000000000000000000000000000000
2 -> 11000000000000000000000000000000
3 -> 11100000000000000000000000000000
4 -> 11110000000000000000000000000000
5 -> 11111000000000000000000000000000
6 -> 11111100000000000000000000000000
7 -> 11111110000000000000000000000000
8 -> 11111111000000000000000000000000
9 -> 11111111100000000000000000000000
10 -> 11111111110000000000000000000000
11 -> 11111111111000000000000000000000
12 -> 11111111111100000000000000000000
13 -> 11111111111110000000000000000000
14 -> 11111111111111000000000000000000
15 -> 11111111111111100000000000000000
16 -> 11111111111111110000000000000000
17 -> 11111111111111111000000000000000
18 -> 11111111111111111100000000000000
19 -> 11111111111111111110000000000000
20 -> 11111111111111111111000000000000
21 -> 11111111111111111111100000000000
22 -> 11111111111111111111110000000000
23 -> 11111111111111111111111000000000
24 -> 11111111111111111111111100000000
25 -> 11111111111111111111111110000000
26 -> 11111111111111111111111111000000
27 -> 11111111111111111111111111100000
28 -> 11111111111111111111111111110000
29 -> 11111111111111111111111111111000
30 -> 11111111111111111111111111111100
31 -> 11111111111111111111111111111110
32 -> 11111111111111111111111111111111
Now that we’re at it, the above expansion was generated by this command line trick:
$ perl -e 'for (0..32) {printf "%2d -> %s\n", $_, substr "1" x $_ . "0" x 32, 0, 32}'
We iterate from 0
to 32
and generate a string with the given number of
leading 1
s, followed by a number of 0
s that make it possible to reach 32
bits overall. This is obtained by always appending 32 0
s (so that we are
sure to cover all cases) and then cutting the string to only keep the first
32 digits.
To generate our netmask we can use the code below:
$ NBITS=29
$ perl -le 'print join ".", unpack "C4", pack "N", 0xFFFFFFFF<<(32-shift)' $NBITS
255.255.255.248
The trick is to read it from right to left:
-
first, we build an integer corresponding to the netmask. We start from
0xFFFFFFFF
(i.e. all bits are set to1
) and then shift it to the left by 32 minus the number of1
s that we have to keep (32-shift
) -
then, we turn this integer into a packed representation of a 32-bits integer (
pack "N", ...
) -
the, we turn this packed representation back to four unsigned chars (
unpack "C4", ...
) -
last, we merge these unsigned integers with dots (
join ".", ...
).
This can be turned into a shell function and a shell script, of course:
#/bin/sh
# usage: netmask <n-bits>
netmask() {
perl -le 'print join ".", unpack "C4", pack "N", 0xFFFFFFFF<<(32-shift)' "$1"
}
! grep -- '4f77114a2f49ae89c876b4a6c229d2ca' "$0" >/dev/null 2>&1 \
|| netmask "$@"
Local version - also as snippet in GitLab.
And this is all for today!