簡體   English   中英

如何有效地檢查給定的IP地址是否屬於Python中的IP子網?

[英]How to efficiently check if a given IP Address belong to an IP subnetwork in Python?

我有一組大約200,000個IP地址和10,000個子網(1.1.1.1/24)。 對於每個IP地址,我需要檢查它是否屬於這些子網之一,但由於它是一個如此龐大的數據集並且我的計算能力較低,我希望能夠有效地實現這一點。

在搜索時,我找到的一種方法就是這個( https://stackoverflow.com/a/820124/7995937 ):

from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
     print "Yay!"

但是由於我必須循環這超過200,000個IP地址,並且每個地址循環超過10,000個子網,我不確定這是否有效。 我的第一個疑問是,檢查“IPNetwork()中的IPAddress()”只是線性掃描還是以某種方式優化?

我想出的另一個解決方案是列出包含在IP子網中的所有IP的列表(大約有13,000,000個IP,沒有重復),然后對其進行排序。 如果我這樣做,那么在我的200,000個IP地址的循環中,我只需要通過一組更大的IP地址對每個IP進行二進制搜索。

for ipMasked in ipsubnets:  # Here ipsubnets is the list of all subnets
        setUnmaskedIPs = [str(ip) for ip in IPNetwork(ipMasked)]
        ip_list = ip_list + setUnmaskedIPs
ip_list = list(set(ip_list))  # To eliminate duplicates
ip_list.sort()

然后,我可以按以下方式執行二進制搜索:

for ip in myIPList:  # myIPList is the list of 200,000 IPs
    if bin_search(ip,ip_list):
        print('The ip is present')

這種方法比另一種更有效嗎? 或者還有其他更有效的方法來執行此任務嗎?

這可能不是最好的解決方案,但我建議使用集合而不是列表。 優化集以檢查集合中是否存在任何給定值,因此您將使用單個操作替換二進制搜索。 代替:

ip_list = list(set(ip_list))

做就是了:

ip_set = set(ip_list)

然后你的代碼的另一部分變成:

for ip in myIPList:  # myIPList is the list of 200,000 IPs
    if ip in ip_set:
        print('The ip is present')

編輯:為了使內存更有效,您可以跳過創建中間列表:

ip_set = set()
for ipMasked in ipsubnets: 
    ip_set.update([str(ip) for ip in IPNetwork(ipMasked)])

好的,所以排序需要O(nlogn),如果是13,000,000你最終做O(13000000log(13000000))。 然后你正在迭代超過200000個IP並在13000000上的排序列表上進行二進制搜索O(logn)。我真誠地懷疑這是最好的解決方案。 我建議你使用地圖

from netaddr import IPNetwork, IPAddress
l_ip_address = map(IPAddress, list_of_ip_address)
l_ip_subnet = map(IPNetwork, list_of_subnets)

if any(x in y for x in l_ip_address for y in l_ip_subnet):
    print "FOUND"

如果該地址的N個前導位與其中一個N位子網的N個前導位匹配,則在子網中輸入您的IP地址。 所以,首先列出空集。 將每個子網編碼為32位整數,並將尾隨位屏蔽掉。 例如,1.2.3.4/23等於(0x01020304和0xfffffe00)等於0x01020200。 將此數字添加到列表中的第23個集合,即subnets[23] 繼續所有子網。

要查看您的子網中是否有IP地址,請以與32位數字ipaddr相同的方式對IP地址進行編碼,然后(類似於未經測試的代碼)

for N in range( 32, 0, -1)
    mask = ( 0xffffffff >> (32-N) ) << (32-N)
    if (ipaddr & mask) in subnets[N] :
        # have found ipaddr in one of our subnets
        break # or do whatever...
else
    # have not found  ipaddr

在最壞的O(log N)中查找集合中的數字,其中N在集合中的元素數量中。 對於不在子網集中的IP地址的最壞情況,此代碼最多執行32次。 如果預計大多數地址都存在,那么首先要測試具有最多元素的集合進行優化。 那可能是

for N in ( 24, 16, 8, 29, 23, 28, 27, 26, 25, 22, 15, 21 ... )

或者您可以在運行時計算最佳序列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM