根据Wikipedia,以下是所有保留的IPv4地址:
RESERVED_IPV4 = [
'0.0.0.0/8',
'10.0.0.0/8',
'100.64.0.0/10',
'127.0.0.0/8',
'169.254.0.0/16',
'172.16.0.0/12',
'192.0.0.0/24',
'192.0.2.0/24',
'192.88.99.0/24',
'192.168.0.0/16',
'198.18.0.0/15',
'198.51.100.0/24',
'203.0.113.0/24',
'224.0.0.0/4',
'233.252.0.0/24',
'240.0.0.0/4',
'255.255.255.255/32'
]
我想在不使用
ipaddress
的情况下检查是否保留了任何给定的IP地址,这很简单,我只需要检查它是否属于任何地址范围。
我已经以编程方式将保留网络转换为开始、结束
int
s 的 IP 对:
RESERVED_IPV4 = [
(0, 16777215),
(167772160, 184549375),
(1681915904, 1686110207),
(2130706432, 2147483647),
(2851995648, 2852061183),
(2886729728, 2887778303),
(3221225472, 3221225727),
(3221225984, 3221226239),
(3227017984, 3227018239),
(3232235520, 3232301055),
(3323068416, 3323199487),
(3325256704, 3325256959),
(3405803776, 3405804031),
(3758096384, 4294967295)
]
一个天真的解决方案是检查数字是否落入任何范围,如下所示:
RESERVED_IPV4_RANGES = [range(*e) for e in RESERVED_IPV4]
def is_reserved_range(ip):
return any(ip in e for e in RESERVED_IPV4_RANGES)
但这是低效的。由于这些是 IP 地址,同一范围内的所有 IP 地址共享相同的二进制前缀:
In [252]: [(f'{a:032b}', f'{b:032b}') for a, b in RESERVED_IPV4]
Out[252]:
[('00000000000000000000000000000000', '00000000111111111111111111111111'),
('00001010000000000000000000000000', '00001010111111111111111111111111'),
('01100100010000000000000000000000', '01100100011111111111111111111111'),
('01111111000000000000000000000000', '01111111111111111111111111111111'),
('10101001111111100000000000000000', '10101001111111101111111111111111'),
('10101100000100000000000000000000', '10101100000111111111111111111111'),
('11000000000000000000000000000000', '11000000000000000000000011111111'),
('11000000000000000000001000000000', '11000000000000000000001011111111'),
('11000000010110000110001100000000', '11000000010110000110001111111111'),
('11000000101010000000000000000000', '11000000101010001111111111111111'),
('11000110000100100000000000000000', '11000110000100111111111111111111'),
('11000110001100110110010000000000', '11000110001100110110010011111111'),
('11001011000000000111000100000000', '11001011000000000111000111111111'),
('11100000000000000000000000000000', '11111111111111111111111111111111')]
所以更聪明的方法是检查 IP 地址的二进制表示是否以任何前缀开头:
def longest_common_prefix(a, b):
short = min(len(a), len(b))
for i in range(short):
if a[:i] != b[:i]:
break
return a[:i-1] if i else ''
RESERVED_IPV4_PREFIXES = tuple(longest_common_prefix(f'{a:032b}', f'{b:032b}') for a, b in RESERVED_IPV4)
def is_reserved_prefix(ip):
return f'{ip:032b}'.startswith(RESERVED_IPV4_PREFIXES)
前缀是:
In [253]: RESERVED_IPV4_PREFIXES
Out[253]:
('00000000',
'00001010',
'0110010001',
'01111111',
'1010100111111110',
'101011000001',
'110000000000000000000000',
'110000000000000000000010',
'110000000101100001100011',
'1100000010101000',
'110001100001001',
'110001100011001101100100',
'110010110000000001110001',
'111')
我已经确认第二种方法比第一种更有效:
In [254]: import random
In [255]: n = random.randrange(2**32)
In [256]: n
Out[256]: 634831452
In [257]: is_reserved_prefix(n)
Out[257]: False
In [258]: is_reserved_range(n)
Out[258]: False
In [259]: %timeit is_reserved_range(n)
1.92 µs ± 56 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
In [260]: %timeit is_reserved_prefix(n)
734 ns ± 6.59 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
但是它能变得更有效率吗?喜欢直接对
int
s 使用按位运算吗?我知道如何使用按位运算与 IP 地址相互转换,但我不知道在这种情况下如何使用它们。
我已经确认直接比较
int
s也比智能方法慢:
In [261]: %timeit any(a <= n <= b for a, b in RESERVED_IPV4)
1.71 µs ± 6.31 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)