Validate IP address Python

Validate an IP Address Using Python [Step-By-Step Guide]

In your Python program you might want to validate an IP address. This can be a requirement if you write OS level programs and not only.

To validate an IP address using Python you can use the ip_address() function of the ipaddress module. This works both for IPv4 and IPv6 addresses. You can also validate an IP address by using a custom function or a regular expression that verify the sets of numbers an IP address is made of.

Let the validation begin!

How Do I Validate an IP Address in Python?

The simplest way to validate if a string represents an IP address is by using the Python ipaddress module.

Let’s open the Python shell and see what the ipaddress.ip_address() function returns when we pass to it strings that represent a valid and an invalid IPv4 address.

The valid one first…

>>> ipaddress.ip_address("10.10.10.10")
IPv4Address('10.10.10.10') 

The ip_address() function returns an object of type IPv4Address, this means it’s able to translate the string into a valid IP address.

Now, let’s try with an invalid IP…

>>> ipaddress.ip_address("10.10.10.300")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/lib/python3.8/ipaddress.py", line 53, in ip_address
    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
ValueError: '10.10.10.300' does not appear to be an IPv4 or IPv6 address 

This time the ip_address() function raises a ValueError exception because the string we have passed doesn’t represent a valid IP address.

We can build a simple function that tells if an IP address is valid or not depending on the fact that a ValueError exception is raised by ipaddress.ip_address() for invalid IPs.

import ipaddress 

def validate_ip_address(address):
    try:
        ip = ipaddress.ip_address(address)
        print("IP address {} is valid. The object returned is {}".format(address, ip))
    except ValueError:
        print("IP address {} is not valid".format(address)) 

Notice how we use the string format() method to generate the success and failure messages.

Pass few IP addresses to this function to confirm if it works fine.

validate_ip_address("10.10.10.10")
validate_ip_address("10.10.10.01")
validate_ip_address("10.10.10.300")
validate_ip_address("10.260.10.300")
validate_ip_address("192.168.1.20")

[output]
IP address 10.10.10.10 is valid. The object returned is 10.10.10.10
IP address 10.10.10.01 is valid. The object returned is 10.10.10.1
IP address 10.10.10.300 is not valid
IP address 10.260.10.300 is not valid
IP address 192.168.1.20 is valid. The object returned is 192.168.1.20 

The function works well, also notice that in the second test from the string “10.10.10.01” we get back an object for “10.10.10.1”.

The module removes the leading zero in the fourth part of the IP address.

Note: you can also update the validate_ip_address() function to return True for a valid IP and False for an invalid IP instead of printing a message.

Validate an IP Address Using a Custom Function

Let’s do a bit of Python practice and see how we would write logic that verifies an IPv4 address without using the ipaddress module.

An IPv4 address has the following format:

a.b.c.d

Where a, b, c, d are four numbers between 0 and 255. We can use this specification to write our custom logic.

def validate_ip_address(address):
    parts = address.split(".")

    if len(parts) != 4:
        print("IP address {} is not valid".format(address))
        return False

    for part in parts:
        if not isinstance(int(part), int):
            print("IP address {} is not valid".format(address))
            return False

        if int(part) < 0 or int(part) > 255:
            print("IP address {} is not valid".format(address))
            return False
 
    print("IP address {} is valid".format(address))
    return True 

In this function we go through the following steps:

  • Split the address based on the dot character and store each part of the IP address into a list of strings.
  • Verify that the IP string is made of 4 numbers separated by dots (using the len() function).
  • For each number in the IP string do the checks below:
    • Verify that the number is an integer.
    • Check that the integer has a value between 0 and 255.

Execute our function against the same IP addresses used before:

validate_ip_address("10.10.10.10")
validate_ip_address("10.10.10.01")
validate_ip_address("10.10.10.300")
validate_ip_address("10.260.10.300")
validate_ip_address("192.168.1.20")

[output]
IP address 10.10.10.10 is valid
IP address 10.10.10.01 is valid
IP address 10.10.10.300 is not valid
IP address 10.260.10.300 is not valid
IP address 192.168.1.20 is valid 

The output is correct.

Validate an IP Address Using a Regex

An IP address can be validated using a regular expression (regex).

Regular expressions provide specific expressions to match patterns (e.g. four consecutive numbers with three digits).

Here is the pattern we can use:

  • ^ represents the beginning of the string we want to match.
  • $ represents the end of the string.
  • \d{1,3} is an integer with 1 to 3 digits.
  • \. matches a single dot.
^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$ 

Open the Python shell and verify this regular expression against a couple of IP addresses.

>>> address = "10.10.10.10"
>>> re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", address)
<re.Match object; span=(0, 11), match='10.10.10.10'>
>>> 
>>> address = "10.10.10.300"
>>> re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", address)
<re.Match object; span=(0, 12), match='10.10.10.300'>
>>> 
>>> address = "10.10.10.3000"
>>> re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", address)
>>>

The first one is a valid IP (10.10.10.10) and its matched by the regular expression.

The second IP is also matched by the regular expression even if it contains the number 300 in the fourth part.

That’s because we match integers with 1 to 3 digits. This means that after using the regular expression we also have to verify if the specific number part of the IP address has a value lower than 255.

The third IP is not matched by the expression because the fourth part contains 4 digits (3000).

Now, write the function that uses this regular expression and also verifies that each part has a value between 0 and 255.

Firstly we want to convert the re.Match object returned by the re.match() function. To do that we will use the bool() function.

>>> address = "10.10.10.10"
>>> match = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", address)
>>> print(bool(match))
True 
>>> 
>>> address = "10.10.10.3000"
>>> match = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", address)
>>> print(bool(match))
False 

When converted to a boolean the object returned by re.match() is True if the string passed to it matches the pattern. It’s false otherwise.

So, let’s start by returning False in our function if the string (in this case the IP address) doesn’t match the pattern for the format of an IP address.

def validate_ip_address(address):
    match = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", address)

    if bool(match) is False:
        print("IP address {} is not valid".format(address)
        return False

    return True 

And complete the function by verifying that each number has a value between 0 and 255 the way we have done in the previous section:

import re 

def validate_ip_address(address):
    match = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", address)

    if bool(match) is False:
        print("IP address {} is not valid".format(address))
        return False

    for part in address.split("."):
        if int(part) < 0 or int(part) > 255:
            print("IP address {} is not valid".format(address))
            return False

    print("IP address {} is valid".format(address))
    return True 

This is the output when you execute this function against some IP addresses.

validate_ip_address("10.10.10.10")
validate_ip_address("10.10.10.01")
validate_ip_address("10.10.10.300")
validate_ip_address("10.260.10.3000")
validate_ip_address("192.168.1.20") 

[output]
IP address 10.10.10.10 is valid
IP address 10.10.10.01 is valid
IP address 10.10.10.300 is not valid
IP address 10.260.10.3000 is not valid
IP address 192.168.1.20 is valid 

How to Check If an IP is of Type IPv4 or IPv6 Using Python

A simple way to check if an IP is of type IPv4 or IPv6 is to use the Python ipaddress module.

When you pass an IP address in string format to the ipaddress.ip_address() function a new object is created.

The object is either of type ipaddress.IPv4Address or ipaddress.IPv6Address. Use the isinstance() built-in function to verify the type of object created.

import ipaddress

def get_ip_type(address):
    try:
        ip = ipaddress.ip_address(address)

        if isinstance(ip, ipaddress.IPv4Address):
            print("{} is an IPv4 address".format(address))
        elif isinstance(ip, ipaddress.IPv6Address):
            print("{} is an IPv6 address".format(address))
    except ValueError:
        print("{} is an invalid IP address".format(address))

As we have done before we use try except to print a message in case the IP address is invalid.

Let’s call our function…

get_ip_type("192.168.23.34")
get_ip_type("2001:0db8:75a2:0000:0000:8a2e:0340:5625")
get_ip_type("257.168.23.34")

[output]
192.168.23.34 is an IPv4 address
2001:0db8:75a2:0000:0000:8a2e:0340:5625 is an IPv6 address
257.168.23.34 is an invalid IP address

Nice! 😀

How to Check If an IP Address is in a Given Subnet

The Python ipaddress module allows to check if an IP address is part of a specific subnet.

Firstly, let’s get all the IP addresses in the network 192.168.1.0/28.

The ipaddress module provides the ip_network() function that returns an IPv4Network or IPv6Network object depending on the type of IP address passed to the function.

If you cast an object returned by the ip_network() function to a list you get back a list of all the IPs (IPv4Address or IPv6Address objects) that belong to the subnet.

>>> list(ipaddress.ip_network("192.168.1.0/28"))
[IPv4Address('192.168.1.0'), IPv4Address('192.168.1.1'), IPv4Address('192.168.1.2'), IPv4Address('192.168.1.3'), IPv4Address('192.168.1.4'), IPv4Address('192.168.1.5'), IPv4Address('192.168.1.6'), IPv4Address('192.168.1.7'), IPv4Address('192.168.1.8'), IPv4Address('192.168.1.9'), IPv4Address('192.168.1.10'), IPv4Address('192.168.1.11'), IPv4Address('192.168.1.12'), IPv4Address('192.168.1.13'), IPv4Address('192.168.1.14'), IPv4Address('192.168.1.15')]

Now that we know this we can create a function that returns True if an IP address belong to a subnet and False otherwise.

Start by creating a function that goes through the IP addresses in the network 192.168.1.0/28 using a Python for loop:

import ipaddress

def verify_ip_subnet(ip_address, subnet_address):
    for address in ipaddress.ip_network(subnet_address):
        print(address)


verify_ip_subnet("192.168.1.8", "192.168.1.0/28")

The output is:

192.168.1.0
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.6
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.10
192.168.1.11
192.168.1.12
192.168.1.13
192.168.1.14
192.168.1.15

And now we will return True if any of the IP addresses in the subnet matches the IP address passed to the function as first argument.

def verify_ip_subnet(ip_address, subnet_address):
    for address in ipaddress.ip_network(subnet_address):
        if str(address) == ip_address:
            return True

    return False

You can use assert statements to test this function…

assert verify_ip_subnet("192.168.1.8", "192.168.1.0/28")
assert verify_ip_subnet("192.168.1.200", "192.168.1.0/28")

[output]
Traceback (most recent call last):
  File "day3_ip_belong_to_subnet.py", line 15, in <module>
    assert verify_ip_subnet("192.168.1.200", "192.168.1.0/28")
AssertionError

The assert for IP 192.168.1.200 fails because the IP doesn’t belong to subnet 192.168.1.0/28.

We don’t see any exception for IP 192.168.1.8 because it’s part of the subnet and assert doesn’t print any messages if the condition tested is True.

Convert IP Addresses to Other Formats For Validation

In some cases you might need to convert an IP address generated using the ipaddress module into other formats before performing any validation.

To convert an IP address into a string you can use the str() function.

Note: remember to import the ipaddress module first otherwise you will see a NameError exception when you try to use the module.

>>> str(ipaddress.IPv4Address('192.168.1.100'))
'192.168.1.100'

To convert an IP address into integer you can use the int() function.

>>> int(ipaddress.IPv4Address('192.168.1.100'))
3232235876

To convert an IP address from integer to a bytes object you can use the v4_int_to_packed() function.

>>> ipaddress.v4_int_to_packed(3232235876)
b'\xc0\xa8\x01d'

A similar function, v6_int_to_packed(), applies to IPv6 addresses.

Conclusion

In this tutorial we went through a very simple way to perform IP address validation using the Python ipaddress library.

We have also seen how to use a custom function and regular expressions for IP validation.

Finally we have seen how to verify if a specific IP address belongs to a subnet.

Which method are you going to use in your program?

Share knowledge with your friends!

Leave a Reply

Your email address will not be published. Required fields are marked *