如何在不使用 Python 内置的情况下检查 IP 地址是否被保留?

问题描述 投票:0回答:0

根据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)
python python-3.x ip-address
© www.soinside.com 2019 - 2024. All rights reserved.