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.
import socket,struct
def makeMask(n):"return a mask of n bits as a long integer"return(2L<<n-1)-1def 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)
输出:
FalseTrue
如果您只想要一个采用字符串的函数,它将看起来像这样:
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
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
n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)
然后,对于每个地址,使用以下任一方法计算二进制表示形式
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
>>> 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
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')
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)
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 matchesif netmask == netmask &(4294967295<<(32-int(bits))):# Validate network address is valid for maskreturn ipaddr_masked == netmask
else:print"***WARNING*** Network",netaddr,"not valid with mask /"+bits
return ipaddr_masked == netmask
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
回答 7
我不喜欢在不需要模块时使用它们。这项工作仅需要简单的数学运算,因此这是我执行该工作的简单函数:
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 offfor i in range(maskLengthFromRight):if i < len(binString):
chopAmount += int(binString[len(binString)-1-i])*2**i
minVal = ipNetworkInt-chopAmount
maxVal = minVal+2**maskLengthFromRight -1return minVal <= ipInt and ipInt <= maxVal
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
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.
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:
回答 10
马克的代码几乎是正确的。该代码的完整版本是-
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 =0for 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))
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.
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:
>>> 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 IPAddressand/orIPNetwork objects from the provided
sequence, an empty list if there was no match.
>>> 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.
#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
回答 14
先前的解决方案在ip&net == net中存在错误。正确的ip查找是ip&netmask = net
错误修正的代码:
import socket
import struct
def makeMask(n):"return a mask of n bits as a long integer"return(2L<<n-1)-1def 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");
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");
#!/usr/bin/python>>> addressInNetwork('188.104.8.64','172.16.0.0/12')>>>True which is completely WRONG!!
所以我的新addressInNetwork函数看起来像:
#!/usr/bin/pythonimport 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 =0for i in xrange(32-int(mask),32):
bits |=(1>24,(bits &0xff0000)>>16,(bits &0xff00)>>8,(bits &0xff))
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
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))
class iptools:@staticmethoddef dottedQuadToNum(ip):"convert decimal dotted quad string to long integer"return struct.unpack('>L', socket.inet_aton(ip))[0]@staticmethoddef numToDottedQuad(n):"convert long int to dotted quad string"return socket.inet_ntoa(struct.pack('>L', n))@staticmethoddef makeNetmask(mask):
bits =0for i in xrange(32-int(mask),32):
bits |=(1<< i)return bits
@staticmethoddef 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)
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)
回答 22
在python中有一个称为SubnetTree的API可以很好地完成这项工作。这是一个简单的例子:
importSubnetTree
t =SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")print("10.0.1.3"in t)
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 23return ip_bits[:net_mask]== net_ip_bits[:net_mask]
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]
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.
from netaddr importIPNetwork,IPAddressdef network_has_ip(network, ip):ifnot isinstance(network,IPNetwork):raiseException("network parameter must be {0} instance".format(IPNetwork.__name__))ifnot isinstance(ip,IPAddress):raiseException("ip parameter must be {0} instance".format(IPAddress.__name__))return(network.cidr.ip.value & network.netmask.value)==(ip.value & network.netmask.value)
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)