1. Overview

Validating an Internet Protocol (IP) address in networking can serve a useful role in automation such as when allocating resources based on IP addresses. It can also help prevent errors and security exploitation that relies on manipulating IP addresses.

In this tutorial, we’ll explore how to validate both an IPv4 and IPv6 address in Linux.

2. Validating an IPv4 Address

An IPv4 address consists of 32 bits which are divided into four segments or bytes. We often represent these segments in dotted decimal notation for better readability. Therefore, an IP address takes on the form A.B.C.D, where the integer values A, B, C, and D fall in the range 0-255.

Importantly, the validation procedure we apply doesn’t allow for leading zeros in any segment that exceeds a single digit. Therefore, addresses such as or are considered invalid. This prevents ambiguity and ensures that parsers won’t treat any segment incorrectly as octal. Additionally, it avoids the problem of dealing with a variable number of leading zeros.

Let’s explore how we can validate an IPv4 address in Linux.

2.1. Using a Bash Script

One approach to validating an IPv4 address consists of three subtasks:

  1. validate that the address has the form A.B.C.D where A, B, C, and D are integers consisting of up to three digits in the range 0-9
  2. if any of A, B, C, or D consists of more than one digit, check that the first digit is not a zero
  3. check that the values of A, B, C, and D are each less than or equal to 255

We can implement this procedure in a Bash script named valid_ipv4.sh:

$ cat valid_ipv4.sh
#!/usr/bin/env bash

valid_ipv4() {
    local ip="$1"
    err_msg='IP address is invalid'
    [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || { echo "$err_msg"; return 1; }
    for i in ${ip//./ }; do
        [[ "${#i}" -gt 1 && "${i:0:1}" == 0 ]] && { echo "$err_msg"; return 1; }
        [[ "$i" -gt 255 ]] && { echo "$err_msg"; return 1; }
    echo 'IP address is valid'

The script defines a function named valid_ipv4() whose first argument is saved in the ip variable. Then, the variable is matched against the ^([0-9]{1,3}\.){3}[0-9]{1,3}$ regex pattern using the [[]] construct:

  1. ^([0-9]{1,3}\.){3} means that the pattern starts with a one to three-digit integer, followed by a dot. We also repeat the captured group within parentheses three times
  2. [0-9]{1,3}$ means that the regex ends with an integer consisting of one to three digits

If the ip variable doesn’t match the regex, we print a message indicating IP address is invalid, and the function exits. Otherwise, we split the four segments in the ip variable using ${ip//./ } which globally transforms each dot into a space character.

Then, we iterate through the four segments to perform checks over the segments:

  • exceeds one digit and starts with a leading zero
  • greater than 255

If a segment meets one of these conditions, we print an IP address is invalid message and exit the function. Otherwise, we consider the IP address to be valid.

Next, we source the script to make the function available:

$ . ./valid_ipv4.sh

Finally, we test the function against a few examples:

$ valid_ipv4
IP address is valid
$ valid_ipv4
IP address is invalid
$ valid_ipv4 325.42.5.17
IP address is invalid

We see that the last two IPv4 addresses are invalid.

2.2. Using a Single Regex Pattern

Another approach to validating an IPv4 address is to use a single regex pattern which can define both the IP address form and the range of values its segments can assume.

To validate against the regex pattern, we can use the grep command:

$ echo '' | grep -Eo '^(([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]))$'
$ echo '' | grep -Eo '^(([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]))$'
$ echo '325.42.5.17' | grep -Eo '^(([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]))$'

The -E option enables extended regex, while the -o option returns the exact regex match, if any.

We can break down the regex into several parts:

  1. [1-9]?[0-9] checks for the range 0-99
  2. 1[0-9][0-9] checks for the range 100-199
  3. 2([0-4][0-9]|5[0-5]) checks for the range 200-255

A segment can consist of any of the three patterns above. We also check that the first three segments are followed by a dot and that the last one is not.

Using grep, the IPv4 address gets printed only if there’s a match. Therefore, we see that the last two examples aren’t valid IPv4 addresses.

3. Validating an IPv4 or IPv6 Address in Python

The ipaddress module in Python allows us to validate both IPv4 and IPv6 addresses. IPv6 addresses are particularly more difficult to validate because they can take on compressed forms with zeros omitted, or may include embedded IPv4 addresses within.

Therefore, while it’s still possible to validate an IPv6 address in Bash, the program logic might easily become convoluted. Additionally, while we can validate an IPv6 address against a single regular expression, the regex pattern is very lengthy and not easy to read.

Instead, let’s use the ipaddress module in a Python script named isipvalid.py to validate both IPv4 and IPv6 addresses:

$ cat isipvalid.py
#!/usr/bin/env python3

import ipaddress, sys

    ip = ipaddress.ip_address(sys.argv[1])
    print(f'{ip} is an IPv{ip.version} address')
except ValueError:
    print('Invalid IP address')

We first import the ipaddress module, in addition to the sys module to access the script’s first argument via sys.argv[1]. Then, we try to call the ip_address method over the argument and save the result in the ip variable. We also print the value of this variable along with its IP version using an f-string. In case the argument isn’t a valid IP address, then we handle the ValueError exception by printing a message indicating Invalid IP address.

Next, we grant the script execute permissions via chmod:

$ chmod +x isipvalid.py

Finally, we test the script against several IPv4 and IPv6 addresses:

$ ./isipvalid.py is an IPv4 address
$ ./isipvalid.py
IP is invalid
$ ./isipvalid.py 325.42.5.17
IP is invalid
$ ./isipvalid.py 2345:1425:2BA2:0000:0000:0567:5673:23b5
2345:1425:2ba2::567:5673:23b5 is an IPv6 address
$ ./isipvalid.py 2001:db8::1
2001:db8::1 is an IPv6 address
$ ./isipvalid.py ::
:: is an IPv6 address
$ ./isipvalid.py ::H231
IP is invalid

Notably, out of all the examples, we see that, 325.42.5.17, and ::H231 are invalid IP addresses.

4. Conclusion

In this article, we explored several ways to validate IP addresses in Linux. In particular, we saw methods for validating an IPv4 address using either a Bash script or a single regular expression. We also explored how to validate both IPv4 and IPv6 addresses using the ipaddress module in Python.