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 to `1`) and then shift it to the left by 32 minus the number of `1`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