我知道这里有一些类似的问题,但是他们大多想要找到范围本身(它使用一些库,比如stackoverflow说的是我的问题的一个例子)并且是另一种语言。
我有办法将子网转换为子网中ip的范围的开头和结尾(好吧,措辞不好,它就像1.1.1.1/16 -> (1.1.0.0 , 1.1.255.255)
)
我现在想检查1.1.2.2
是否在此子网内。我可以简单地进行>
和<
进行比较吗?
ip_range = ('1.1.0.0', '1.1.255.255')
if '1.1.2.2' >= ip_range[0] and '1.1.2.2' <= ip_range[1]:
return True
当我测试它时,它可以工作,但我不知道它是否总能用于任何ipv4 ip。我假设我只是比较ASCII顺序,所以这应该始终有效,但有没有例外?
答案 0 :(得分:6)
在Python 3.3及更高版本中,您应该使用ipaddress
模块。
from ipaddress import ip_network, ip_address
net = ip_network("1.1.0.0/16")
print(ip_address("1.1.2.2") in net) # True
答案 1 :(得分:4)
您的代码会比较字符串,而不是数字。我建议改用元组:
>>> ip_range = [(1,1,0,0), (1,1,255,255)]
>>> testip = (1,1,2,2)
>>> testip > ip_range[0] and testip < ip_range[1]
True
>>> testip = (1,3,1,1)
>>> testip > ip_range[0] and testip < ip_range[1]
False
答案 2 :(得分:4)
你不能真正对点分隔的数字列表进行字符串比较,因为你的测试只会因输入1.1.99.99
而失败,因为'9'
只是大于'2'
>>> '1.1.99.99' < '1.1.255.255'
False
因此,您可以通过理解表达式将输入转换为整数元组
def convert_ipv4(ip):
return tuple(int(n) for n in ip.split('.'))
请注意缺少类型检查,但如果您的输入是正确的IP地址,那就没问题了。由于你有一个2元组的IP地址,你可以创建一个以开始和结束为参数的函数,通过参数列表传递该元组,并只用一个语句返回(因为Python允许比较链接)。也许就像:
def check_ipv4_in(addr, start, end):
return convert_ipv4(start) < convert_ipv4(addr) < convert_ipv4(end)
测试一下。
>>> ip_range = ('1.1.0.0', '1.1.255.255')
>>> check_ipv4_in('1.1.99.99', *ip_range)
True
使用此方法,您可以懒洋洋地将其扩展为IPv6,但是需要转换为十六进制(而不是整数)。
答案 3 :(得分:1)
这通常不起作用,因为字符串比较处于整理顺序,而不是四个字段的数值。例如,'1.1.2.2'&gt; '1.1.128.1' - 第5个角色的关键位置,'1'对'2'。
如果您想比较字段,请尝试分成列表:
ip_vals = [int(x) for x in ip_range.split('.')]
ip_vals现在是一个值列表;您可以比较列表并获得我认为您想要的结果。
答案 4 :(得分:0)
有一个ipaddress
模块,它提供了人们所需要的所有功能。以下内容并非基于此-它只是显示了另一种方法。
积木
def ipv4_mask(cidr):
mask = 2**32 - 2**(32 - int(cidr))
return (mask >> sh & 0xff for sh in (24, 16, 8, 0))
def ipv6_mask(cidr):
mask = 2**128 - 2**(128 - int(cidr))
return (mask >> sh & 0xff for sh in range(120, -1, -8))
def ipv4_bytes(ip):
return (int(b) for b in ip.split('.'))
def ipv6_bytes(ip):
words = ip.split(':')
filled = False
for word in words:
if word:
yield int(word[:-2] or '0', 16)
yield int(word[-2:], 16)
elif filled:
yield 0
yield 0
else:
filled = True
for _ in range(9 - len(words)):
yield 0
yield 0
除IPv6字节功能外,所有基本功能都非常简单。与简单的IPv4格式相比,IPv6地址的不同格式需要更多的逻辑来进行解析。例如,环回可以表示为::1
。也可以用相邻的冒号来表示0的范围,例如:aaaa::1111
代表aaaa:0:0:0:0:0:0:1111
。
会员资格检查
要确定IP是否在IP和CIDR网络掩码位说明符定义的IP范围内,如果按预期方式应用网络掩码(作为掩码),则无需计算起始地址和结束地址。以下两个功能是如何确定IPv4地址是否为CIDR标记网络IP成员的示例。另一个显示IPv6测试,以确定一个子网是否在另一个子网中。
使用以上内容作为构建块,我们可以为ipv4或ipv6构造自定义功能。
def ipv4_cidr_member_of(ip1, ip2):
ip2, m = ip2.split('/')
return not any((a ^ b) & m
for a, b, m in
zip(ipv4_bytes(ip1),
ipv4_bytes(ip2),
ipv4_mask(m)))
def ipv6_cidr_subnet_of(ip1, ip2):
ip1, m1 = ip1.split('/')
ip2, m2 = ip2.split('/')
return int(m1) >= int(m2) and \
not any((a ^ b) & m
for a, b, m in
zip(ipv6_bytes(ip1),
ipv6_bytes(ip2),
ipv6_mask(m2)))
>>> ipv6_cidr_subnet_of('aaaa:bbbb:cccc:dddd:1100::/72',
... 'aaaa:bbbb:cccc:dddd::/64')
True
>>> ipv4_cidr_member_of('11.22.33.44', '11.22.33.0/24')
True
>>>
使用这种方法,比较通常包括对两个IP字节进行XOR运算,然后与网络掩码进行AND运算。只需将以“ ipv4_
”开头的功能更改为“ ipv6_
”即可,将IPv4算法转换为IPv6,反之亦然。使用构建块,IPv4或IPv6的算法在此级别是相同的。
使用构建块,可以创建自定义功能,例如确定两个CIDR标记的IP地址是否都在同一网络上,或者一个与另一个在同一网络内,这类似于{{ 1}}具有逻辑功能。
范围
请记住,如果您将掩码视为真实掩码,则无需计算子网范围即可确定成员资格。如果出于任何原因想要该范围,可以使用IP和网络掩码以与上述其他示例类似的方式获得它。
...subnet_of()
效率
该功能似乎比使用>>> def ipv4_cidr_range_bytes(ip):
... ip, m = ip.split('/')
... ip = list(ipv4_bytes(ip))
... m = list(ipv4_mask(m))
... start = [ b & m for b, m in zip(ip, m)]
... end = [(b | ~m) & 0xff for b, m in zip(ip, m)]
... return start, end
...
>>> ipv4_cidr_range_bytes('11.22.34.0/23')
([11, 22, 34, 0], [11, 22, 35, 255])
>>>
>>> # For IPv6, the above function could have been converted to look
>>> # just like it, but let's mix it up for fun with a single pass
>>> # over the data with zip(), then group into bytes objects with map()
>>>
>>> def ipv6_cidr_range_bytes(ip):
... ip, m = ip.split('/')
... s, e = map(lambda *x: bytes(x),
... *((b & m, (b | ~m) & 0xff)
... for b, m in zip(ipv6_bytes(ip),
... ipv6_mask(m))))
... return s, e
...
>>> ipv6_cidr_range_bytes('aaaa:bbbb:cccc:dddd:1100::/72')
(b'\xaa\xaa\xbb\xbb\xcc\xcc\xdd\xdd\x11\x00\x00\x00\x00\x00\x00\x00',
b'\xaa\xaa\xbb\xbb\xcc\xcc\xdd\xdd\x11\xff\xff\xff\xff\xff\xff\xff')
对象和方法要快一些:
ipaddress
缓存
如果在应用程序中重复相同的比较-经常重复相同的IP,则>>> # Using the ipaddress module:
>>> timeit.timeit("a = ip_network('192.168.1.0/24'); "
"b = ip_network('192.168.1.128/30'); "
... "b.subnet_of(a)", globals=globals(), number=10**4)
0.2772132240352221
>>>
>>> # Using this code:
>>> timeit.timeit("ipv4_cidr_subnet_of('192.168.1.128/30', '192.168.1.0/24')",
... globals=globals(), number=10**4)
0.07261682399985148
>>>
可用于修饰功能并可能获得更高的效率:
functools.lru_cache
这会缓存参数并返回值,因此当在from functools import lru_cache
@lru_cache
def ipv6_cidr_member_of(ip1, ip2):
ip1 = ipv6_bytes(ip1)
ip2, m = ip2.split('/')
ip2 = ipv6_bytes(ip2)
m = ipv6_mask(m)
return not any((a ^ b) & m for a, b, m in zip(ip1, ip2, m))
中再次检查相同的ip1
的成员身份时,缓存会快速返回上一次计算的值,并且函数主体无需重做操作。
ip2
此测试仅显示5个周期。高速缓存命中与未命中的比率越高,效率的收益就越高。
答案 5 :(得分:0)
对于python 2 & 3
,请使用:
from ipaddress import ip_network, ip_address
def in_cidr(ip, cidr):
return ip_address(ip) in ip_network(cidr)
对于pyhton 2.7
,请使用:
pip install ipaddress