我如何检查一个ip是否在pythonnetworking中
给定一个IP地址(比如192.168.0.1),我该如何检查它是否在Python中的networking(比如192.168.0.0/24)?
在Python中是否有通用工具来处理IP地址? 东西像主机查找,IP地址添加到int,networking掩码到int等networking地址? 希望在2.5的标准Python库中。
这篇文章展示了你可以使用socket
和struct
模块,而不需要额外的工作。 我给文章加了一点如下:
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)
这输出:
False True
如果你只想要一个带string的函数,它看起来像这样:
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
我喜欢使用netaddr :
from netaddr import CIDR, IP if IP("192.168.0.1") in CIDR("192.168.0.0/24"): print "Yay!"
正如arno_v在评论中指出的,netaddr的新版本是这样的:
from netaddr import IPNetwork, IPAddress if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"): print "Yay!"
使用ipaddress ( 在3.3版本的stdlib中 , 在PyPi 2.6 / 2.7版本中 ):
>>> import ipaddress >>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24') True
如果你想以这种方式评估很多 IP地址,你可能需要预先计算networking掩码
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
最后,你可以简单地检查:
in_network = (a & mask) == netw
这个代码在Linux x86上工作。 我没有真正考虑过永久性问题,但是我已经使用超过200K的IP地址对8个不同的networkingstring进行了testing,并针对“ipaddr”模块进行了testing,并且ipaddr的结果与此代码相同。
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)
例:
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16') True >>> print addressInNetwork('10.9.8.7', '10.9.1.0/24') False
我试过Dave Webb的解决scheme,但遇到了一些问题:
最根本的是 – 匹配应该通过将IP地址与掩码进行AND操作来检查,然后精确地检查与networking地址匹配的结果。 不要像所做的那样将IP地址与networking地址进行和运算。
我还注意到,只是忽略Endian行为,假设一致性将会节省您,只会在八位字节边界(/ 24,/ 16)上使用掩码。 为了让其他掩码(/ 23,/ 21)正常工作,我在struct命令中添加了一个“大于”字符,并修改了用于创build二进制掩码的代码,以全部“1”开始并向左移动(32掩码)。
最后,我添加了一个简单的检查,即networking地址对于掩码是有效的,如果不是,则只是打印一个警告。
结果如下:
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
我不喜欢在不需要的时候使用模块。 这项工作只需要简单的math,所以这是我做这个工作的简单function:
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
然后使用它:
>>> 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)
就是这样,这比上面的解决scheme要快得多。
被接受的答案是行不通的,这让我很生气。 掩码是反向的,并不适用于任何不是简单的8位块(例如/ 24)的位。 我调整了答案,它很好地工作。
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
这里是一个函数,返回一个虚线的二进制string,以帮助可视化屏蔽..有点像ipcalc
输出。
def bb(i): def s = '{:032b}'.format(i) def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]
例如:
马克的代码几乎是正确的。 代码的完整版本是 –
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))
显然,从上面的相同来源…
非常重要的一点是,第一个代码有一个小故障 – IP地址255.255.255.255也显示为任何子网的有效IP。 我有一段时间让这个代码工作,并感谢马克正确的答案。
不在2.5的标准库,但ipaddr使这非常容易。 我相信这是在名为ipaddress的3.3下。
import ipaddr a = ipaddr.IPAddress('192.168.0.1') n = ipaddr.IPNetwork('192.168.0.0/24') #This will return True n.Contains(a)
从netaddr导入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')]
以下是这种方法的用法:
>>> 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.
基本上你提供了一个ip地址作为第一个参数和一个cidrs列表作为第二个参数。 返回匹配列表。
#这个工作正常,没有奇怪的字节处理 def addressInNetwork(ip,net): '''是networking中的地址''' #将地址转换为主机顺序,所以转换实际上是有意义的 ip = struct.unpack('> L',socket.inet_aton(ip))[0] netaddr,bits = net.split('/') netaddr = struct.unpack('> L',socket.inet_aton(netaddr))[0] #必须左移全1值,/ 32 =零点移位,/ 0 = 32左移 netmask =(0xffffffff <<(32-int(bits)))&0xffffffff #只要有一个合适的networking地址,就不需要屏蔽networking地址 返回(IP和networking掩码)== netaddr
这是我写的最长的前缀匹配的类:
#!/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
这里是一个testing程序:
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
我不知道标准库中的任何东西,但是PySubnetTree是一个可以进行子网匹配的Python库。
谢谢你的脚本!
我有相当长的一段时间,使一切工作…所以我在这里分享
- 使用netaddr类比使用二进制转换慢10倍,所以如果你想在一个大的IP列表上使用它,你应该考虑不使用netaddr类
-
makeMask函数不工作! 只为/ 8,/ 16,/ 24工作
例如:位=“21”; socket.inet_ntoa(struct.pack('= L',(2L << int(bits)-1) – 1))
'255.255.31.0',而应该是255.255.248.0所以我用另一个函数calcDottedNetmask(掩码)从http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
例如:
#!/usr/bin/python >>> calcDottedNetmask(21) >>> '255.255.248.0'
- 另一个问题是如果一个IP属于一个networking,匹配的过程! 基本操作应该是比较(ipaddr和networking掩码)和(networking和networking掩码)。
例如:目前这个function是错误的
#!/usr/bin/python >>> addressInNetwork('188.104.8.64','172.16.0.0/12') >>>True which is completely WRONG!!
所以我的新的addressInNetwork函数看起来像:
#!/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))
而现在,答案是正确的!
#!/usr/bin/python >>> addressInNetwork('188.104.8.64','172.16.0.0/12') False
我希望这会帮助别人,为他们节省时间!
关于上述所有,我认为socket.inet_aton()以networking顺序返回字节,所以解压缩它们的正确方法可能
struct.unpack('!L', ... )
以前的解决scheme有一个在IP&净==网错误。 正确的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) - 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");
select的答案有一个错误。
以下是正确的代码:
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
注意: ipaddr & netmask == netaddr & netmask
而不是ipaddr & netmask == netmask
。
我也用((2L<<int(bits)-1) - 1)
replace((2L<<int(bits)-1) - 1)
((1L << int(bits)) - 1)
,因为后者似乎更容易理解。
在Python中有一个名为SubnetTree的API可以很好地完成这项工作。 这是一个简单的例子:
import SubnetTree t = SubnetTree.SubnetTree() t.insert("10.0.1.3/32") print("10.0.1.3" in t)
这是链接
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
假
真正
假
从上面的各种来源,以及从我自己的研究,这是我如何得到子网和地址计算工作。 这些作品足以解决问题和其他相关问题。
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)
这是我的代码
# -*- 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')
如果你不想导入你可以使用的其他模块:
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]
依赖于“struct”模块可能会导致endian – ness和字体大小的问题,只是不需要。 socket.inet_aton()也不是。 Python可以很好地处理虚线四个IP地址:
def ip_to_u32(ip): return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)
我需要在每个套接字的accept()调用上进行IP匹配,对一整套允许的源networking,所以我预先计算了掩码和networking的整数:
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
那么我可以很快看到一个给定的IP是否在这些networking之一内:
ip = ip_to_u32(ipstr) for mask, net in cached_masks: if ip & mask == net: # matched! break else: raise BadClientIP(ipstr)
不需要模块input,而且代码在匹配上非常快。