How do you determine if an IP address is private, in Python?

In Python, what is the best way to determine if an IP address (e.g., '127.0.0.1' or '10.98.76.6') is on a private network? The code does not sound difficult to write. But there may be more edge cases than are immediately apparent, and there's IPv6 support to consider, etc. Is there an existing library that does it?


Solution 1:

Since Python 3.3 there is an ipaddress module in the stdlib that you can use.

>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1').is_private
True

If using Python 2.6 or higher I would strongly recommend to use a backport of this module.

Solution 2:

Check out the IPy module. If has a function iptype() that seems to do what you want:

>>> from IPy import IP
>>> ip = IP('127.0.0.0/30')
>>> ip.iptype()
'PRIVATE'

Solution 3:

You can check that yourself using https://www.rfc-editor.org/rfc/rfc1918 and https://www.rfc-editor.org/rfc/rfc3330. If you have 127.0.0.1 you just need to & it with the mask (lets say 255.0.0.0) and see if the value matches any of the private network's network address. So using inet_pton you can do: 127.0.0.1 & 255.0.0.0 = 127.0.0.0

Here is the code that illustrates that:

from struct import unpack
from socket import AF_INET, inet_pton

def lookup(ip):
    f = unpack('!I',inet_pton(AF_INET,ip))[0]
    private = (
        [ 2130706432, 4278190080 ], # 127.0.0.0,   255.0.0.0   https://www.rfc-editor.org/rfc/rfc3330
        [ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 https://www.rfc-editor.org/rfc/rfc1918
        [ 2886729728, 4293918720 ], # 172.16.0.0,  255.240.0.0 https://www.rfc-editor.org/rfc/rfc1918
        [ 167772160,  4278190080 ], # 10.0.0.0,    255.0.0.0   https://www.rfc-editor.org/rfc/rfc1918
    ) 
    for net in private:
        if (f & net[1]) == net[0]:
            return True
    return False

# example
print(lookup("127.0.0.1"))
print(lookup("192.168.10.1"))
print(lookup("10.10.10.10"))
print(lookup("172.17.255.255"))
# outputs True True True True

another implementation is to compute the int values of all private blocks:

from struct import unpack
from socket import AF_INET, inet_pton

lookup = "127.0.0.1"
f = unpack('!I',inet_pton(AF_INET,lookup))[0]
private = (["127.0.0.0","255.0.0.0"],["192.168.0.0","255.255.0.0"],["172.16.0.0","255.240.0.0"],["10.0.0.0","255.0.0.0"])
for net in private:
    mask = unpack('!I',inet_aton(net[1]))[0]
    p = unpack('!I',inet_aton(net[0]))[0]
    if (f & mask) == p:
        print lookup + " is private"

Solution 4:

This is the fixed version of the regex approach suggested by @Kurt including the fix recommended by @RobEvans

  • ^127.\d{1,3}.\d{1,3}.\d{1,3}$

  • ^10.\d{1,3}.\d{1,3}.\d{1,3}$

  • ^192.168.\d{1,3}.\d{1,3}$

  • ^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$

     def is_ip_private(ip):
    
         # https://en.wikipedia.org/wiki/Private_network
    
         priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
         priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
         priv_20 = re.compile("^192\.168\.\d{1,3}.\d{1,3}$")
         priv_16 = re.compile("^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$")
    
         res = priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip) or priv_16.match(ip)
         return res is not None
    

This will not 100.x.x.x range which is used internally in kubernetes

Solution 5:

A few days after asking this question, I found out about this Google project, ipaddr-py, which appears to have some of the same functionality with respect to determining if an address is private (is_rfc1918). Apparently this will be standard in Python 3.1.