IPv4 (Internet Protocol version 4) is a unique 32-bit address that identifies a machine’s network interface. It has four 8-bit numbers (0-255) separated by dots like this: 190.168.1.0.
In the above visual representation, you can see that we represent IPv4 addresses in 8-bit integers, also known as octets.
IPv6 (Internet Protocol version 6) is a unique 128-bit address that identifies a machine’s network interface. It has four 128-bit numbers divided into eight groups of four hexadecimal digits separated by colons. For example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.
The main intention behind creating IPv6 is to supplement and eventually replace IPv4 since there are now over a billion devices and there are only 4.3 approximately unique IPv4 addresses.
Validating IPv4 and IPv6 Addresses
Here are two ways to validate them:
- Using ipaddress module
- Using regular expression (re) module
Why do we need to validate IPv4 and IPv6 addresses you might ask? Well, we need to validate them because it prevents potential security vulnerabilities. If your network is using invalid ip addresses, there is a grave danger of security breaches which lead to hacking or significant data loss.
Method 1: Using the ipaddress module
The “ipaddress” is an external built-in Python module that provides the ip_address() function. We can use this function to determine whether the input ip address is of version 4 or 6.
We will create a custom function that uses the if-else condition to determine whether the IP address is IPv4, IPv6, or invalid.
from ipaddress import ip_address def validIP(IP: str) -> str: try: ip = ip_address(IP) return "IPv4" if ip.version == 4 else "IPv6" except ValueError: return "Invalid" test_cases = [ "192.168.0.1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "256.32.555.5", "250.32:555.5", "fe80::1ff:fe23:4567:890a", "2001:0db8:85a3:0000:0000:8a2e:0370:7334:7334", "2001:db8::85a3::7334", "::1", "::ffff:192.0.2.128", "2001:db8::", "::192.0.2.256" ] for ip in test_cases: print(f"{ip} is {validIP(ip)}")
In this code, we defined a custom function called “validIPAddress()” that accepts test ip addresses and checks if it is a valid ipv4 or ipv6 address. I did my best to try out different variations of ipv4 and ipv6 addresses to verify that we wrote a robust custom function that was accurately able to find out validity.
If you pass an invalid ipv4 and/or ipv6 address, it will return “Invalid”.
If you are looking for standards-compliant validation that handles all valid IPv6 formats, including compressed forms, then the “ipaddress” module is the best and most reliable approach. However, it might become slower compared to the likes of “regex,” which we will learn about in the next section.
Keep in mind that “ipaddress” module is introduced in Python 3.3. So, if you are using a Python version older than 3.3, then you will have no other choice except to use regular expressions.
Output
192.168.0.1 is IPv4 2001:0db8:85a3:0000:0000:8a2e:0370:7334 is IPv6 256.32.555.5 is Invalid 250.32:555.5 is Invalid fe80::1ff:fe23:4567:890a is IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334:7334 is Invalid 2001:db8::85a3::7334 is Invalid ::1 is IPv6 ::ffff:192.0.2.128 is IPv6 2001:db8:: is IPv6 ::192.0.2.256 is Invalid
Method 2: Using “regex” module
The regex module “re” is the well known module in Python that provides “re.match()” function. What we will do is write a regex pattern for ipv4 and ipv6 addresses and match them with input ip addresses. If it returns true, it is valid; if it does not, it returns false, and we will label it “invalid”.
Here is the program that demonstrates what I have been telling you:
import re def validIP(IP: str) -> str: # Precise IPv4 pattern ipv4_pattern = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' # Precise IPv6 pattern ipv6_pattern = r'^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' # Determining whether the input # IP address is valid ipv4 or ipv6 if re.match(ipv4_pattern, ip): return "IPv4" elif re.match(ipv6_pattern, ip): return "IPv6" else: return "Invalid" test_cases = [ "192.168.0.1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "256.32.555.5", "250.32:555.5", "fe80::1ff:fe23:4567:890a", "2001:0db8:85a3:0000:0000:8a2e:0370:7334:7334", "2001:db8::85a3::7334", "::1", "::ffff:192.0.2.128", "2001:db8::", "::192.0.2.256" ] for ip in test_cases: print(f"{ip} is {validIP(ip)}")
As you can see in the above program, the regex pattern for ipv6 is complicated, but it is by far the solid pattern that will never pass an invalid ipv6 address.
This approach is fast and reliable when performance is crucial and you are dealing with large numbers of addresses to validate, but there is a big learning curve for regular expressions. You must have advanced knowledge of regex in order to create such a complex pattern.
That’s all!