views:

1447

answers:

6

Given an ip address (say 192.168.0.1), how do I check if it's in a network (say 192.168.0.0/24) in Python?

Are there general tools in Python for ip address manipulation? Stuff like host lookups, ip adddress to int, network address with netmask to int and so on? Hopefully in the standard Python library for 2.5.

+1  A: 

I don't know of anything in the standard library, but PySubnetTree is a Python library that will do subnet matching.

RichieHindle
A: 

This article shows you can do it with socket and struct modules without too much extra effort. I added a little to the article as follows:

import socket,struct

def makeMask(n):
    "return a mask of n bits as a long integer"
    return (2L<<n-1) - 1

def dottedQuadToNum(ip):
    "convert decimal dotted quad string to long integer"
    return struct.unpack('L',socket.inet_aton(ip))[0]

def networkMask(ip,bits):
    "Convert a network address to a long integer" 
    return dottedQuadToNum(ip) & makeMask(bits)

def addressInNetwork(ip,net):
   "Is an address in a network"
   return ip & net == net

address = dottedQuadToNum("192.168.1.1")
networka = networkMask("10.0.0.0",24)
networkb = networkMask("192.168.0.0",24)
print (address,networka,networkb)
print addressInNetwork(address,networka)
print addressInNetwork(address,networkb)

This outputs:

False
True

If you just want a single function that takes strings it would look like this:

import socket,struct

def addressInNetwork(ip,net):
   "Is an address in a network"
   ipaddr = struct.unpack('L',socket.inet_aton(ip))[0]
   netaddr,bits = net.split('/')
   netmask = struct.unpack('L',socket.inet_aton(netaddr))[0] & ((2L<<int(bits)-1) - 1)
   return ipaddr & netmask == netmask
Dave Webb
I guess there might be big and little endian problems here, but since both network and ip address resolution happens on the same computer, both number should have the same error and at least be in the same endian order.
Staale
Good point, although I don't think there are any "problems" as such. The key thing is that stuct.unpack and socket.inet_aton both default to the system endianness so the long you end up with will always be OK and match the masks created by the makeMask function. Once you have longs it doesn't matter which order the bytes are checked when you use the bitwise operators.
Dave Webb
AFAIK inet_aton defaults to network byte order, which is big endian. In fact your code depends on inet_aton having endianness *opposite* to the system one, so that the first bytes of ip address (e.g. 10 in "10.0.0.0") align with the least significant bytes of the mask (e.g 0xff in 0x000000ff).
Rafał Dowgird
Additionally, struct.unpack('L',socket.inet_aton(ip))[0]will fail on architectures where 'L' unpacks to something different than 4 bytes, regardless of endianness.
Rafał Dowgird
Note that this won't work with IPv6 addresses.
Tony Meyer
+12  A: 

I like to use netaddr for that:

from netaddr import CIDR, IP

if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
    print "Yay!"
nosklo
+1  A: 

ipaddr is a nice library as well.

Noah Campbell
+4  A: 

Using ipaddr (in the Python stdlib since Python2.7/3.1):

>>> from ipaddr import IP
>>> IP('192.168.0.1') in IP('192.168.0.0/24')
True
phihag
ipaddr is no longer part of python stdlib (added in 2.7a1, 3.1b1; removed in 2.7a1, 3.1rc2)
mykhal
A: 
Marc Leurent