
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 18, 2024
The subnet mask, or netmask, is used to divide an IP address into a network portion and a host portion. In binary notation, the mask generally consists of a series of ones followed by zeros, where the ones cover the network bits of an IP address. The number of bits corresponding to the network portion is known as the netmask length.
In this tutorial, we’ll explore how to extract the subnet mask in Bash on a Debian system. We’ll also see how to convert the netmask length given in Classless Inter-Domain Routing (CIDR) notation to dotted decimal format.
We can use the now-deprecated ifconfig command to obtain information about the IP address and netmask of a network interface:
$ sudo ifconfig enp2s0
enp2s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.103 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::a0b0:72d0:65cc:f141 prefixlen 64 scopeid 0x20<link>
...
In this case, the name of the network interface is enp2s0, and its IPv4 address is 192.168.0.103. The netmask is also visible as 255.255.255.0, which means in binary that the first three octets correspond to the network portion and the last octet to the host portion. That is, the binary representation of the mask takes on the form 11111111;11111111;11111111;00000000 where octets are separated by a semicolon.
We can extract the netmask address directly by piping the output to awk:
$ sudo ifconfig enp2s0 | awk '/netmask/ {print $4}'
255.255.255.0
The awk command in this case searches for the netmask keyword and prints the fourth field of the line where the keyword is found.
Alternatively, we can use the ip command to show information about a network interface:
$ ip -o -4 address show enp2s0
2: enp2s0 inet 192.168.0.103/24 brd 192.168.0.255 scope global dynamic noprefixroute enp2s0\ valid_lft 84800sec preferred_lft 84800sec
The -o flag used with ip is to print concise information for each interface on one line. The -4 flag is for showing only the IPv4 information. In this case, we see that the IP address and netmask length are provided in CIDR notation as 192.168.0.103/24. Since an IPv4 address is 32 bits long, we can deduce that the netmask in binary notation consists of 24 ones followed by 8 zeros.
We can also extract the address given in CIDR notation using awk:
$ ip -o -4 address show enp2s0 | awk '{print $4}'
192.168.0.103/24
We use awk to print the fourth field of the line of information provided for the enp2s0 interface.
We can also use the nmcli command to extract the IP address and netmask length in CIDR notation:
$ nmcli device show enp2s0 | awk '/IP4.ADDRESS/ {print $2}'
192.168.0.103/24
We use nmcli to show information about the enp2s0 network device. Then, we pipe the result to awk to print the second field of the line where the IP4.ADDRESS pattern is matched.
We can convert the netmask in CIDR notation to dotted decimal form using Bash scripting:
$ cat extract_netmask.sh
valid_ipv4() {
local ip="${1}"
[[ "${ip}" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]
return "$?"
}
valid_cidr() {
local ip_cidr="${1}"
local status=1
if [[ "${ip_cidr}" =~ ^[^/]*/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
ip=$(echo "${ip_cidr}" | cut -d '/' -f 1)
valid_ipv4 "${ip}" && status=0
fi
return "${status}"
}
bin2ip() {
local binary_sequence="${1}"
echo "obase=10; ibase=2; ${binary_sequence}" | bc | xargs | tr ' ' '.'
}
cidr2netmask() {
local ip_cidr="${1}"
valid_cidr "${ip_cidr}" || return 1
netmask_length="$(echo "${ip_cidr}" | cut -d '/' -f 2)"
host_length="$((32-netmask_length))"
ones="$(head -c "${netmask_length}" /dev/zero | tr '\0' '1')"
zeros="$(head -c "${host_length}" /dev/zero | tr '\0' '0')"
sequence="$(sed -E 's/(.{8})(.{8})(.{8})(.{8})/\1;\2;\3;\4/' <<< "${ones}${zeros}")"
bin2ip "${sequence}"
}
The script consists of four functions. Let’s delve into what each function does.
The first function, named valid_ipv4(), validates whether an address is an IPv4 address using a single regular expression.
The second function, named valid_cidr(), builds on the first function to validate whether an IPv4 address is in CIDR notation. In particular, it performs several steps:
This way, the function returns with an exit value of 0, indicating success, only if the IP address is a valid IPv4 address and the netmask length is a valid integer in the range 0 to 32.
The third function, bin2ip(), converts sequences of binary bits separated by a semicolon, such as 10000000;00101010;00000101;00000100 into dotted decimal format such as 128.42.5.4.
In particular, we first use the bc command to perform the conversion from base 2 to base 10. Then, we pipe the output to xargs so that it prints on a single line. Finally, we use the tr command to convert spaces into dots.
Finally, the fourth function, cidr2netmask(), uses the previous three to validate an IPv4 address in CIDR notation and compute the netmask address. It implements a number of steps, combining our previous code:
In the generation steps, we use head -c over the /dev/zero device to generate a stream of null characters, and then we pipe these to tr to convert them into ones or zeros as required.
Let’s source the extract_netmask.sh script:
$ . ./extract_netmask.sh
Now, we can test the valid_ipv4() function:
$ valid_ipv4 128.42.5.4 && echo 'valid' || echo 'invalid'
valid
Likewise, we can test the valid_cidr() function:
$ valid_cidr 128.42.5.4/21 && echo 'valid' || echo 'invalid'
valid
Next, we test the bin2ip() function:
$ bin2ip '10000000;00101010;00000101;00000100'
128.42.5.4
Finally, let’s test the main cir2netmask() function:
$ cidr2netmask 128.42.5.4/21
255.255.248.0
Subsequently, we see that the netmask length of 21 is converted to 255.255.248.0.
In this article, we explored different methods for extracting the netmask using network commands such as ifconfig, ip, and nmcli. We also learned how to convert the netmask of an IPv4 address given in CIDR notation to dotted decimal format.