How can I check if an ip is in a network in Python?
Asked Answered
R

30

141

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.

Resnick answered 4/5, 2009 at 8:59 Comment(0)
K
28

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
Kinshasa answered 4/5, 2009 at 9:21 Comment(10)
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.Resnick
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.Kinshasa
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).Atul
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.Atul
Continuing on Rafal's comment, to get this to work on a 64-bit Python interpreter, replace the line in question with: return struct.unpack('<L',socket.inet_aton(ip))[0]Collenecollet
I think your solution has a serious bug: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True (I converted 'L' to '<L' in my 64bit os)Carmacarmack
CAUTION: This solution has a serious bug: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> TrueCarmacarmack
This answer has a bug. See answer: #819855Interment
I think that people are adding two solutions: If the IP can be pinged and is currently assigned to a client, or if the IP is in the network's IP rangeLay
This answer has two fatal bugs: 1. others has pointed out, its logic is error at statement ip & net == net, which will return true when ip='11.0.1.1' and net='10.0.0.0/8'; 2. it works only on little-endian machine even fix the first bug. It's incompatible on big-endian machine. The resolution is to always use function ntohl() (in python, it is socket.ntohl()) to convert the integer returned by inet_aton(). And after use ntohl(), we need to compare the n bits at the beginning rather than the n bits at the ending.Lineberry
O
203

Using ipaddress (in the stdlib since 3.3, at PyPi for 2.6/2.7):

>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24')
True

If you want to evaluate a lot of IP addresses this way, you'll probably want to calculate the netmask upfront, like

n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)

Then, for each address, calculate the binary representation with one of

a = int(ipaddress.ip_address('192.0.43.10'))
a = struct.unpack('!I', socket.inet_pton(socket.AF_INET, '192.0.43.10'))[0]
a = struct.unpack('!I', socket.inet_aton('192.0.43.10'))[0]  # IPv4 only

Finally, you can simply check:

in_network = (a & mask) == netw
Ochre answered 17/6, 2009 at 0:15 Comment(4)
Beware, python-ipaddr behaved quite slow for us, so it may be unsuitable for some cases where a lot of comparsions are required frequently. YMMV, so benchmark yourself.Lundgren
In some versions, you may need to supply a unicode string instead of a Python str type, like ipaddress.ip_address(u'192.168.0.1') in ipaddress.ip_network(u'192.168.0.0/24') .Where
I was concerned that this method was iterating over the list of addresses in the network, but the ipaddress module overrides __contains__ method to do it in an efficient way by comparing the integer representations of the network and broadcast addresses, so rest assured if that was your worry.Yepez
To check if a network contains a network, you can use overlaps from ipaddress, e.g. ipaddress.ip_network('192.168.0.16/28').overlaps(ipaddress.ip_network('192.168.0.0/24'))Tolyl
D
175

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!"

As arno_v pointed out in the comments, new version of netaddr does it like this:

from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
    print "Yay!"
Dogs answered 4/5, 2009 at 13:35 Comment(3)
>>> netaddr.all_matching_cidrs("192.168.0.1", ["192.168.0.0/24","212.11.64.0/19"] ) [IPNetwork('192.168.0.0/24')]Disconnected
Or in the new version: from netaddr import IPNetwork, IPAddress IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24")Woodson
Note that this approach, although nice API, requires installation of third-party module. Whereas the other answer, which discusses builtin ipaddress module, does not.Luge
G
64

For python3

import ipaddress
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/16')

Output :

False
True
Gschu answered 23/7, 2018 at 6:4 Comment(1)
The standard lib uses clever bitwise checking so this is the most optimal solution here. github.com/python/cpython/blob/3.8/Lib/ipaddress.py#L690Milestone
M
31

Using Python >= 3.7 ipaddress:

import ipaddress

address = ipaddress.ip_address("192.168.0.1")
network = ipaddress.ip_network("192.168.0.0/16")

print(network.supernet_of(ipaddress.ip_network(f"{address}/{address.max_prefixlen}")))

Explanation

You can think of an IP Address as a Network with the largest possible netmask (/32 for IPv4, /128 for IPv6)

Checking whether 192.168.0.1 is in 192.168.0.0/16 is essentially the same as checking whether 192.168.0.1/32 is a subnet of 192.168.0.0/16

Milestone answered 26/12, 2019 at 7:16 Comment(5)
...not sure why this answer is not at the top (yet).Overly
Thanks for this helpful answer of the constraint Python >= 3.7? I've seen in the doc that it has been introduced in version 3.3.Extenuate
even easier, all built in: if IPv4Address('192.0.2.6') in IPv4Network('192.0.2.0/28')Rajah
@Rajah Yeah I've commented that under petertc's answer https://mcmap.net/q/159401/-how-can-i-check-if-an-ip-is-in-a-network-in-pythonMilestone
@Rajah - Although right, checking ip_address in ip_network will be more time consumingShoshonean
K
28

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
Kinshasa answered 4/5, 2009 at 9:21 Comment(10)
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.Resnick
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.Kinshasa
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).Atul
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.Atul
Continuing on Rafal's comment, to get this to work on a 64-bit Python interpreter, replace the line in question with: return struct.unpack('<L',socket.inet_aton(ip))[0]Collenecollet
I think your solution has a serious bug: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True (I converted 'L' to '<L' in my 64bit os)Carmacarmack
CAUTION: This solution has a serious bug: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> TrueCarmacarmack
This answer has a bug. See answer: #819855Interment
I think that people are adding two solutions: If the IP can be pinged and is currently assigned to a client, or if the IP is in the network's IP rangeLay
This answer has two fatal bugs: 1. others has pointed out, its logic is error at statement ip & net == net, which will return true when ip='11.0.1.1' and net='10.0.0.0/8'; 2. it works only on little-endian machine even fix the first bug. It's incompatible on big-endian machine. The resolution is to always use function ntohl() (in python, it is socket.ntohl()) to convert the integer returned by inet_aton(). And after use ntohl(), we need to compare the n bits at the beginning rather than the n bits at the ending.Lineberry
R
15

This code is working for me on Linux x86. I haven't really given any thought to endianess issues, but I have tested it against the "ipaddr" module using over 200K IP addresses tested against 8 different network strings, and the results of ipaddr are the same as this code.

def addressInNetwork(ip, net):
   import socket,struct
   ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
   netstr, bits = net.split('/')
   netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
   mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
   return (ipaddr & mask) == (netaddr & mask)

Example:

>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False
Refluent answered 16/12, 2010 at 20:16 Comment(1)
Nice and fast. No need for a library for a few simple logic operations.Copyist
I
10

Wherever possible I'd recommend the built in ipaddress module. It's only available in Python 3 though, but it is super easy to use, and supports IPv6. And why aren't you using Python 3 yet anyway, right?


The accepted answer doesn't work ... which is making me angry. Mask is backwards and doesn't work with any bits that are not a simple 8 bit block (eg /24). I adapted the answer, and it works nicely.

    import socket,struct
    
    def addressInNetwork(ip, net_n_bits):  
      ipaddr = struct.unpack('!L', socket.inet_aton(ip))[0]
      net, bits = net_n_bits.split('/')
      netaddr = struct.unpack('!L', socket.inet_aton(net))[0]
      netmask = (0xFFFFFFFF >> int(bits)) ^ 0xFFFFFFFF
      return ipaddr & netmask == netaddr

here is a function that returns a dotted binary string to help visualize the masking.. kind of like ipcalc output.

    def bb(i):
     def s = '{:032b}'.format(i)
     def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]

eg:

screen shot of python

Iridic answered 5/6, 2015 at 21:38 Comment(0)
D
8

I'm not a fan of using modules when they are not needed. This job only requires simple math, so here is my simple function to do the job:

def ipToInt(ip):
    o = map(int, ip.split('.'))
    res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
    return res

def isIpInSubnet(ip, ipNetwork, maskLength):
    ipInt = ipToInt(ip)#my test ip, in int form

    maskLengthFromRight = 32 - maskLength

    ipNetworkInt = ipToInt(ipNetwork) #convert the ip network into integer form
    binString = "{0:b}".format(ipNetworkInt) #convert that into into binary (string format)

    chopAmount = 0 #find out how much of that int I need to cut off
    for i in range(maskLengthFromRight):
        if i < len(binString):
            chopAmount += int(binString[len(binString)-1-i]) * 2**i

    minVal = ipNetworkInt-chopAmount
    maxVal = minVal+2**maskLengthFromRight -1

    return minVal <= ipInt and ipInt <= maxVal

Then to use it:

>>> print isIpInSubnet('66.151.97.0', '66.151.97.192',24) 
True
>>> print isIpInSubnet('66.151.97.193', '66.151.97.192',29) 
True
>>> print isIpInSubnet('66.151.96.0', '66.151.97.192',24) 
False
>>> print isIpInSubnet('66.151.97.0', '66.151.97.192',29) 

That's it, this is much faster than the solutions above with the included modules.

Dumond answered 29/4, 2015 at 17:41 Comment(1)
{TypeError}'map' object is not subscriptable. You need a o = list(o) after o = map(int, ip.split('.'))Mantinea
T
7

Not in the Standard library for 2.5, but ipaddr makes this very easy. I believe it is in 3.3 under the name ipaddress.

import ipaddr

a = ipaddr.IPAddress('192.168.0.1')
n = ipaddr.IPNetwork('192.168.0.0/24')

#This will return True
n.Contains(a)
Thwart answered 25/9, 2013 at 21:42 Comment(0)
B
6

I tried Dave Webb's solution but hit some problems:

Most fundamentally - a match should be checked by ANDing the IP address with the mask, then checking the result matched the Network address exactly. Not ANDing the IP address with the Network address as was done.

I also noticed that just ignoring the Endian behaviour assuming that consistency will save you will only work for masks on octet boundaries (/24, /16). In order to get other masks (/23, /21) working correctly I added a "greater than" to the struct commands and changed the code for creating the binary mask to start with all "1" and shift left by (32-mask).

Finally, I added a simple check that the network address is valid for the mask and just print a warning if it is not.

Here's the result:

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]
    ipaddr_masked = ipaddr & (4294967295<<(32-int(bits)))   # Logical AND of IP address and mask will equal the network address if it matches
    if netmask == netmask & (4294967295<<(32-int(bits))):   # Validate network address is valid for mask
            return ipaddr_masked == netmask
    else:
            print "***WARNING*** Network",netaddr,"not valid with mask /"+bits
            return ipaddr_masked == netmask
Budgie answered 10/5, 2012 at 12:57 Comment(2)
This appears to work reliably on 64-bit ('L' fails as the value is 32-bit) and returns the values in a sensible order (ipaddr will be 0xC0A80001 for 192.168.0.1). It also copes with "192.168.0.1/24" as a netmask for "192.168.0.1" (not standard, but possible and easily correctable)Adagio
Works perfectly on Python 2.4Estipulate
S
5

As of Python 3.7, you can use subnet_of and supernet_of helper methods, which are part of the standard library:

To just test against a single IP, you can just use the subnet mask /32 which means "only this IP address" as a subnet, or you can pass the IP address to IPv4Nework or IPv6Nework constructors and they will return a subnet value for you.

So for your example:

from ipaddress import IPv4Network, IPv4Address

# Store IP Address as variable
>>> myip = IPv4Address('192.168.0.1')
>>> myip
IPv4Address('192.168.0.1')

# This treats the IP as a subnet
>>> myip_subnet = IPv4Network(myip)
>>> myip_subnet
IPv4Network('192.168.0.1/32')

# The other subnet to test membership against
>>> other_subnet = IPv4Network('192.168.0.0/24')
>>> other_subnet
IPv4Network('192.168.0.0/24')

# Now we can test
>>> myip_subnet.subnet_of(other_subnet)
True

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.

In Python 3, there's the ipaddress module which has tools for IPv4 and IPv6 manipulation. You can convert them to an int, by casting, i.e. int(IPv4Address('192.168.0.1')). Lots of other useful functions in the ipaddress module for hosts, etc.

Scenography answered 18/3, 2021 at 20:56 Comment(1)
This is the best answer for Python 3 and checking if one subnet is fully covered by (contained in) another subniet.Telemark
A
4

Marc's code is nearly correct. A complete version of the code is -

def addressInNetwork3(ip,net):
    '''This function allows you to check if on IP belogs to a Network'''
    ipaddr = struct.unpack('=L',socket.inet_aton(ip))[0]
    netaddr,bits = net.split('/')
    netmask = struct.unpack('=L',socket.inet_aton(calcDottedNetmask(int(bits))))[0]
    network = struct.unpack('=L',socket.inet_aton(netaddr))[0] & netmask
    return (ipaddr & netmask) == (network & netmask)

def calcDottedNetmask(mask):
    bits = 0
    for i in xrange(32-mask,32):
        bits |= (1 << i)
    return "%d.%d.%d.%d" % ((bits & 0xff000000) >> 24, (bits & 0xff0000) >> 16, (bits & 0xff00) >> 8 , (bits & 0xff))

Obviously from the same sources as above...

A very Important note is that the first code has a small glitch - The IP address 255.255.255.255 also shows up as a Valid IP for any subnet. I had a heck of time getting this code to work and thanks to Marc for the correct answer.

Ankylosaur answered 29/3, 2011 at 3:13 Comment(1)
Tried and tested. From all the socket/struct examples on this page this is the only correct oneAbmho
Q
4

Relying on the "struct" module can cause problems with endian-ness and type sizes, and just isn't needed. Nor is socket.inet_aton(). Python works very well with dotted-quad IP addresses:

def ip_to_u32(ip):
  return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)

I need to do IP matching on each socket accept() call, against a whole set of allowable source networks, so I precompute masks and networks, as integers:

SNS_SOURCES = [
  # US-EAST-1
  '207.171.167.101',
  '207.171.167.25',
  '207.171.167.26',
  '207.171.172.6',
  '54.239.98.0/24',
  '54.240.217.16/29',
  '54.240.217.8/29',
  '54.240.217.64/28',
  '54.240.217.80/29',
  '72.21.196.64/29',
  '72.21.198.64/29',
  '72.21.198.72',
  '72.21.217.0/24',
  ]

def build_masks():
  masks = [ ]
  for cidr in SNS_SOURCES:
    if '/' in cidr:
      netstr, bits = cidr.split('/')
      mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
      net = ip_to_u32(netstr) & mask
    else:
      mask = 0xffffffff
      net = ip_to_u32(cidr)
    masks.append((mask, net))
  return masks

Then I can quickly see if a given IP is within one of those networks:

ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
  if ip & mask == net:
    # matched!
    break
else:
  raise BadClientIP(ipstr)

No module imports needed, and the code is very fast at matching.

Quintessence answered 6/11, 2017 at 6:32 Comment(1)
What's this cached_masks referred to??Leung
I
3

The choosen answer has a bug.

Following is the correct code:

def addressInNetwork(ip, net_n_bits):
   ipaddr = struct.unpack('<L', socket.inet_aton(ip))[0]
   net, bits = net_n_bits.split('/')
   netaddr = struct.unpack('<L', socket.inet_aton(net))[0]
   netmask = ((1L << int(bits)) - 1)
   return ipaddr & netmask == netaddr & netmask

Note: ipaddr & netmask == netaddr & netmask instead of ipaddr & netmask == netmask.

I also replace ((2L<<int(bits)-1) - 1) with ((1L << int(bits)) - 1), as the latter seems more understandable.

Interment answered 22/4, 2014 at 21:11 Comment(4)
I think the mask conversion ((2L<<int(bits)-1) - 1) is correct. e.g. if the mask is 16, it should be "255.255.0.0" or 65535L, but ((1L << int(bits)) - 1) will get 32767L, which is not right.Sarmentose
@Chris.Q, ((1L << int(bits)) - 1) gives 65535L on my system, with bits set to 16!!Interment
Also, for bits set to 0, ((2L<<int(bits)-1) - 1) is raising error.Interment
Yes, actually no value other than /0, /8, /16, /32 is working properly.Interment
D
2

from netaddr import all_matching_cidrs

>>> from netaddr import all_matching_cidrs
>>> all_matching_cidrs("212.11.70.34", ["192.168.0.0/24","212.11.64.0/19"] )
[IPNetwork('212.11.64.0/19')]

Here is the usage for this method:

>>> help(all_matching_cidrs)

Help on function all_matching_cidrs in module netaddr.ip:

all_matching_cidrs(ip, cidrs)
    Matches an IP address or subnet against a given sequence of IP addresses and subnets.

    @param ip: a single IP address or subnet.

    @param cidrs: a sequence of IP addresses and/or subnets.

    @return: all matching IPAddress and/or IPNetwork objects from the provided
    sequence, an empty list if there was no match.

Basically you provide an ip address as the first argument and a list of cidrs as the second argument. A list of hits are returned.

Disconnected answered 6/9, 2011 at 17:33 Comment(0)
H
2
#This works properly without the weird byte by byte handling
def addressInNetwork(ip,net):
    '''Is an address in a network'''
    # Convert addresses to host order, so shifts actually make sense
    ip = struct.unpack('>L',socket.inet_aton(ip))[0]
    netaddr,bits = net.split('/')
    netaddr = struct.unpack('>L',socket.inet_aton(netaddr))[0]
    # Must shift left an all ones value, /32 = zero shift, /0 = 32 shift left
    netmask = (0xffffffff << (32-int(bits))) & 0xffffffff
    # There's no need to mask the network address, as long as its a proper network address
    return (ip & netmask) == netaddr 
Hyson answered 7/4, 2012 at 8:36 Comment(1)
The code did not work correctly on 64-bit OS, because of incorrect netmask values. I've taken a liberty to fix that.Lundgren
V
2

Here is a class I wrote for longest prefix matching:

#!/usr/bin/env python

class Node:
def __init__(self):
    self.left_child = None
    self.right_child = None
    self.data = "-"

def setData(self, data): self.data = data
def setLeft(self, pointer): self.left_child = pointer
def setRight(self, pointer): self.right_child = pointer
def getData(self): return self.data
def getLeft(self): return self.left_child
def getRight(self): return self.right_child

def __str__(self):
        return "LC: %s RC: %s data: %s" % (self.left_child, self.right_child, self.data)


class LPMTrie:      

def __init__(self):
    self.nodes = [Node()]
    self.curr_node_ind = 0

def addPrefix(self, prefix):
    self.curr_node_ind = 0
    prefix_bits = ''.join([bin(int(x)+256)[3:] for x in prefix.split('/')[0].split('.')])
    prefix_length = int(prefix.split('/')[1])
    for i in xrange(0, prefix_length):
        if (prefix_bits[i] == '1'):
            if (self.nodes[self.curr_node_ind].getRight()):
                self.curr_node_ind = self.nodes[self.curr_node_ind].getRight()
            else:
                tmp = Node()
                self.nodes[self.curr_node_ind].setRight(len(self.nodes))
                tmp.setData(self.nodes[self.curr_node_ind].getData());
                self.curr_node_ind = len(self.nodes)
                self.nodes.append(tmp)
        else:
            if (self.nodes[self.curr_node_ind].getLeft()):
                self.curr_node_ind = self.nodes[self.curr_node_ind].getLeft()
            else:
                tmp = Node()
                self.nodes[self.curr_node_ind].setLeft(len(self.nodes))
                tmp.setData(self.nodes[self.curr_node_ind].getData());
                self.curr_node_ind = len(self.nodes)
                self.nodes.append(tmp)

        if i == prefix_length - 1 :
            self.nodes[self.curr_node_ind].setData(prefix)

def searchPrefix(self, ip):
    self.curr_node_ind = 0
    ip_bits = ''.join([bin(int(x)+256)[3:] for x in ip.split('.')])
    for i in xrange(0, 32):
        if (ip_bits[i] == '1'):
            if (self.nodes[self.curr_node_ind].getRight()):
                self.curr_node_ind = self.nodes[self.curr_node_ind].getRight()
            else:
                return self.nodes[self.curr_node_ind].getData()
        else:
            if (self.nodes[self.curr_node_ind].getLeft()):
                self.curr_node_ind = self.nodes[self.curr_node_ind].getLeft()
            else:
                return self.nodes[self.curr_node_ind].getData()

    return None

def triePrint(self):
    n = 1
    for i in self.nodes:
        print n, ':'
        print i
        n += 1

And here is a test program:

n=LPMTrie()
n.addPrefix('10.25.63.0/24')
n.addPrefix('10.25.63.0/16')
n.addPrefix('100.25.63.2/8')
n.addPrefix('100.25.0.3/16')
print n.searchPrefix('10.25.63.152')
print n.searchPrefix('100.25.63.200')
#10.25.63.0/24
#100.25.0.3/16
Vermiculation answered 1/8, 2013 at 23:34 Comment(0)
M
2

previous solution have a bug in ip & net == net. Correct ip lookup is ip & netmask = net

bugfixed code:

import socket
import 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 addressInNetwork(ip,net,netmask):
   "Is an address in a network"
   print "IP "+str(ip) + " NET "+str(net) + " MASK "+str(netmask)+" AND "+str(ip & netmask)
   return ip & netmask == net

def humannetcheck(ip,net):
        address=dottedQuadToNum(ip)
        netaddr=dottedQuadToNum(net.split("/")[0])
        netmask=makeMask(long(net.split("/")[1]))
        return addressInNetwork(address,netaddr,netmask)


print humannetcheck("192.168.0.1","192.168.0.0/24");
print humannetcheck("192.169.0.1","192.168.0.0/24");
Mendelevium answered 3/2, 2014 at 12:43 Comment(0)
T
1

Thank you for your script!
I have work quite a long on it to make everything working... So I'm sharing it here

  • Using netaddr Class is 10 times slower than using binary conversion, so if you'd like to use it on a big list of IP, you should consider not using netaddr class
  • makeMask function is not working! Only working for /8,/16,/24
    Ex:

    bits = "21" ; socket.inet_ntoa(struct.pack('=L',(2L << int(bits)-1) - 1))
    '255.255.31.0' whereas it should be 255.255.248.0

    So I have used another function calcDottedNetmask(mask) from http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
    Ex:


#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
  • Another problem is the process of matching if an IP belongs to a network! Basic Operation should be to compare (ipaddr & netmask) and (network & netmask).
    Ex: for the time being, the function is wrong

#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!

So my new addressInNetwork function looks-like:


#!/usr/bin/python
import socket,struct
def addressInNetwork(ip,net):
    '''This function allows you to check if on IP belogs to a Network'''
    ipaddr = struct.unpack('=L',socket.inet_aton(ip))[0]
    netaddr,bits = net.split('/')
    netmask = struct.unpack('=L',socket.inet_aton(calcDottedNetmask(bits)))[0]
    network = struct.unpack('=L',socket.inet_aton(netaddr))[0] & netmask
    return (ipaddr & netmask) == (network & netmask)

def calcDottedNetmask(mask):
    bits = 0
    for i in xrange(32-int(mask),32):
        bits |= (1 > 24, (bits & 0xff0000) >> 16, (bits & 0xff00) >> 8 , (bits & 0xff))


And now, answer is right!!


#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
False

I hope that it will help other people, saving time for them!

Telega answered 6/7, 2010 at 17:10 Comment(1)
the current version of the above code gives a traceback in the last line, that you can "|=" an int and a tuple.Refluent
T
1

Relating to all of the above, I think socket.inet_aton() returns bytes in network order, so the correct way to unpack them is probably

struct.unpack('!L', ... )
Tipi answered 11/1, 2013 at 14:39 Comment(0)
T
1
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('/')
    netaddr = struct.unpack('!L',socket.inet_aton(netaddr))[0]
    netmask = ((1<<(32-int(bits))) - 1)^0xffffffff
    return ipaddr & netmask == netaddr & netmask
print addressInNetwork('10.10.10.110','10.10.10.128/25')
print addressInNetwork('10.10.10.110','10.10.10.0/25')
print addressInNetwork('10.10.10.110','10.20.10.128/25')

$ python check-subnet.py
False
True
False

Truckle answered 17/2, 2016 at 14:50 Comment(1)
Can you explain what are you adding to the already given answers?Airdrie
A
0

From various sources above, and from my own research, this is how I got subnet and address calculation working. These pieces are enough to solve the question and other related questions.

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

    @staticmethod
    def numToDottedQuad(n):
        "convert long int to dotted quad string"
        return socket.inet_ntoa(struct.pack('>L', n))

    @staticmethod
    def makeNetmask(mask):
        bits = 0
        for i in xrange(32-int(mask), 32):
            bits |= (1 << i)
        return bits

    @staticmethod
    def ipToNetAndHost(ip, maskbits):
        "returns tuple (network, host) dotted-quad addresses given"
        " IP and mask size"
        # (by Greg Jorgensen)
        n = iptools.dottedQuadToNum(ip)
        m = iptools.makeMask(maskbits)
        net = n & m
        host = n - mask
        return iptools.numToDottedQuad(net), iptools.numToDottedQuad(host)
Autolithography answered 20/8, 2015 at 20:35 Comment(0)
F
0

There is an API that's called SubnetTree available in python that do this job very well. This is a simple example :

import SubnetTree
t = SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")
print("10.0.1.3" in t)

This is the link

Fullblown answered 21/10, 2015 at 16:14 Comment(0)
O
0

Here is my code

# -*- coding: utf-8 -*-
import socket


class SubnetTest(object):
    def __init__(self, network):
        self.network, self.netmask = network.split('/')
        self._network_int = int(socket.inet_aton(self.network).encode('hex'), 16)
        self._mask = ((1L << int(self.netmask)) - 1) << (32 - int(self.netmask))
        self._net_prefix = self._network_int & self._mask

    def match(self, ip):
        '''
        判断传入的 IP 是不是本 Network 内的 IP
        '''
        ip_int = int(socket.inet_aton(ip).encode('hex'), 16)
        return (ip_int & self._mask) == self._net_prefix

st = SubnetTest('100.98.21.0/24')
print st.match('100.98.23.32')
Osculation answered 17/4, 2017 at 7:44 Comment(0)
A
0

If you do not want to import other modules you could go with:

def ip_matches_network(self, network, ip):
    """
    '{:08b}'.format(254): Converts 254 in a string of its binary representation

    ip_bits[:net_mask] == net_ip_bits[:net_mask]: compare the ip bit streams

    :param network: string like '192.168.33.0/24'
    :param ip: string like '192.168.33.1'
    :return: if ip matches network
    """
    net_ip, net_mask = network.split('/')
    net_mask = int(net_mask)
    ip_bits = ''.join('{:08b}'.format(int(x)) for x in ip.split('.'))
    net_ip_bits = ''.join('{:08b}'.format(int(x)) for x in net_ip.split('.'))
    # example: net_mask=24 -> compare strings at position 0 to 23
    return ip_bits[:net_mask] == net_ip_bits[:net_mask]
Anikaanil answered 10/10, 2017 at 8:45 Comment(0)
L
0

I tried one subset of proposed solutions in these answers.. with no success, I finally adapted and fixed the proposed code and wrote my fixed function.

I tested it and works at least on little endian architectures--e.g.x86-- if anyone likes to try on a big endian architecture, please give me feedback.

IP2Int code comes from this post, the other method is a fully (for my test cases) working fix of previous proposals in this question.

The code:

def IP2Int(ip):
    o = map(int, ip.split('.'))
    res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
    return res


def addressInNetwork(ip, net_n_bits):
    ipaddr = IP2Int(ip)
    net, bits = net_n_bits.split('/')
    netaddr = IP2Int(net)
    bits_num = int(bits)
    netmask = ((1L << bits_num) - 1) << (32 - bits_num)
    return ipaddr & netmask == netaddr & netmask

Hope useful,

Lontson answered 19/7, 2018 at 19:32 Comment(0)
E
0

Here is the solution using netaddr package

from netaddr import IPNetwork, IPAddress


def network_has_ip(network, ip):

    if not isinstance(network, IPNetwork):
        raise Exception("network parameter must be {0} instance".format(IPNetwork.__name__))

    if not isinstance(ip, IPAddress):
        raise Exception("ip parameter must be {0} instance".format(IPAddress.__name__))

    return (network.cidr.ip.value & network.netmask.value) == (ip.value & network.netmask.value)
Elma answered 10/2, 2020 at 10:41 Comment(0)
P
0

To avoid having builtin or third party modules change their syntax over time, I created my own that does this. I'm using this as an importable module. I hope this helps someone:


def subnet_lookup(subnet: str, netmask: str, ip_address: str):
    """
    :param subnet: subnet to test against (as string)
    :param netmask: mask of subnet
    :param ip_address: ip to test against subnet and mask

    :return True if a match; False if not a match

    Steps:

    1) convert entire subnet into one binary word
    2) convert entire mask into one binary word
    3) determine bcast from comparing subnet and mask
    4) convert entire ip_address into one binary word
    5) convert entire subnet into decimal
    6) convert entire bcast into decimal
    7) convert entire ip_address into decimal
    8) determine if ip_address falls between subnet and bcast using range(); returns True if yes, False if no
    """

    def convert_whole_to_bin(whole):
        ip_dec_list = whole.split(".")
        ip_bin_str = ""

        for ip in ip_dec_list:
            binary = dec_to_bin(int(ip))
            ip_bin_str += binary

        return ip_bin_str

    def dec_to_bin(decimal_octet: int):
        binary = bin(decimal_octet).replace("0b", "")

        return binary.rjust(8, '0')

    def split_binary_into_list(binary_octet: str):
        bin_list = []
        for s in binary_octet:
            bin_list.append(s)

        return bin_list

    def determine_bcast(subnet, netmask):
        subnet_split = split_binary_into_list(subnet)
        netmask_split = split_binary_into_list(netmask)
        bcast_list = []

        for subnet, mask in zip(subnet_split, netmask_split):
            if mask != '0':
                bcast_list.append(subnet)

            else:
                bcast_list.append('1')

        bcast_bin = "".join(bcast_list)

        return bcast_bin

    def bin_to_dec(binary_single_word: str):
        decimal = int(binary_single_word, 2)

        return decimal

    def subnet_lookup(ip_address, subnet, bcast):

        return ip_address in range(subnet, bcast + 1)

    # 1) convert entire subnet into one binary word
    subnet_single_bin = convert_whole_to_bin(whole=subnet)

    # 2) convert entire mask into one binary word
    mask_single_bin = convert_whole_to_bin(whole=netmask)

    # 3) determine bcast from comparing subnet and mask
    bcast_single_bin = determine_bcast(subnet=subnet_single_bin, netmask=mask_single_bin)

    # 4) convert entire ip_address into one binary word
    ip_address_single_bin = convert_whole_to_bin(whole=ip_address)

    # 5) convert entire subnet into decimal
    subnet_single_dec = bin_to_dec(binary_single_word=subnet_single_bin)

    # 6) convert entire bcast into decimal
    bcast_single_dec = bin_to_dec(binary_single_word=bcast_single_bin)

    # 7) convert entire ip_address into decimal
    ip_address_single_dec = bin_to_dec(binary_single_word=ip_address_single_bin)

    # 8) determine if ip_address falls between subnet and bcast; returns True if yes, False if no
    lookup_result = subnet_lookup(ip_address=ip_address_single_dec, subnet=subnet_single_dec, bcast=bcast_single_dec)

    return lookup_result


# Testing:

subnet = "172.16.0.0"
netmask = "255.255.0.0"
ip_address = "172.16.255.255"

result = subnet_lookup(subnet=subnet, netmask=netmask, ip_address=ip_address)

print(result)
Peeved answered 13/6, 2022 at 1:20 Comment(0)
A
0

This function checks if IP Address falls in Private IP Subnet or Public Subnet Domain.

def is_private_ip(ip_address_as_str):
'''Takes String IP Address without Cider as input 
    Returns True if the IP Address falls in Private subnet
    Returns False if IP Address is public
'''
    class_a=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('10.0.0.0/8')
    class_b=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('172.16.0.0/12')
    class_c=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('192.168.0.0/16')
    class_local_loop=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('127.0.0.0/8')
    class_apipa=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('169.254.0.0/16')
    return class_a|class_b|class_c|class_local_loop|class_apipa
Aghast answered 4/6, 2023 at 12:31 Comment(0)
S
0

The other solution is to use IPy library

def is_ip_in_prefix(ip, *prefixes):
prefixes_ipset = IPy.IPSet([IPy.IP(item) for item in prefixes])
try:
    ip_ipset = IPy.IP(ip)
    return (
        ip_ipset in prefixes_ipset
        or
        # If the IP is IPv4/IPv6 and prefix is IPv6/IPv4 addresses, we need to convert it to a compatible
        # format.
        #   >>> IP('192.168.1.1').v46map()
        #   IP('::ffff:192.168.1.1')
        #   >>> IP('::ffff:192.168.1.1').v46map()
        #   IP('192.168.1.1')
        ip_ipset.v46map() in prefixes_ipset
    )
except ValueError:
    return False
Senhorita answered 1/10, 2023 at 16:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.