簡體   English   中英

如何在 Python 中檢查 ip 是否在網絡中?

[英]How can I check if an ip is in a network in Python?

給定一個 IP 地址(比如 192.168.0.1),我如何在 Python 中檢查它是否在網絡中(比如 192.168.0.0/24)?

Python中有用於IP地址操作的通用工具嗎? 諸如主機查找,int 的 ip 地址,帶有網絡掩碼的網絡地址到 int 等的東西? 希望在 2.5 的標准 Python 庫中。

使用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 地址,您可能需要預先計算網絡掩碼,例如

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

我喜歡為此使用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!"

對於python3

import ipaddress
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/16')

輸出 :

False
True

這篇文章展示了你可以用socketstruct模塊做到這一點,而不需要太多額外的努力。 我在文章中添加了一點如下:

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

如果你只想要一個接受字符串的函數,它看起來像這樣:

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

使用 Python >= 3.7 ipaddress

import ipaddress

address = ipaddress.ip_address("192.168.0.1")
network = ipaddress.ip_network("192.168.0.0/16")

print(network.supernet_of(ipaddress.ip_network(f"{address}/{address.max_prefixlen}")))

解釋

您可以將IP 地址視為具有最大可能網絡掩碼的網絡( /32表示 IPv4, /128表示 IPv6)

檢查192.168.0.1是否在192.168.0.0/16與檢查192.168.0.0/16是否是192.168.0.1/32的子網本質上是一樣的

這段代碼在 Linux x86 上為我工作。 我並沒有真正考慮過字節序問題,但我已經使用超過 200K IP 地址針對 8 個不同的網絡字符串測試了“ipaddr”模塊,並且 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

我會盡可能推薦內置的ipaddress模塊。 雖然它只在 Python 3 中可用,但它非常易於使用,並且支持 IPv6。 為什么你還不使用 Python 3,對吧


接受的答案不起作用......這讓我很生氣。 掩碼是向后的,不適用於任何不是簡單的 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

這是一個函數,它返回一個帶點的二進制字符串來幫助可視化屏蔽..有點像ipcalc輸出。

    def bb(i):
     def s = '{:032b}'.format(i)
     def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]

例如:

蟒蛇的屏幕截圖

我不喜歡在不需要時使用模塊。 這項工作只需要簡單的數學運算,所以這是我完成這項工作的簡單函數:

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) 

就是這樣,這比上面包含模塊的解決方案要快得多。

我嘗試了 Dave Webb 的解決方案,但遇到了一些問題:

最根本的是 - 應該通過將 IP 地址與掩碼進行“與”來檢查匹配,然后檢查結果是否與網絡地址完全匹配。 不像之前那樣將 IP 地址與網絡地址進行與運算。

我還注意到,假設一致性將節省您的時間,而忽略 Endian 行為,只會適用於八位字節邊界(/24、/16)上的掩碼。 為了讓其他掩碼(/23、/21)正常工作,我在結構命令中添加了一個“大於”,並將用於創建二進制掩碼的代碼更改為以全“1”開頭並左移(32-mask )。

最后,我添加了一個簡單的檢查網絡地址是否對掩碼有效,如果不是就打印一個警告。

結果如下:

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

不在 2.5 的標准庫中,但 ipaddr 使這很容易。 我相信它是在 3.3 中,名稱為 ipaddress。

import ipaddr

a = ipaddr.IPAddress('192.168.0.1')
n = ipaddr.IPNetwork('192.168.0.0/24')

#This will return True
n.Contains(a)

依賴“struct”模塊可能會導致字節序和類型大小出現問題,而這並不是必需的。 socket.inet_aton() 也不是。 Python 非常適用於點分四組 IP 地址:

def ip_to_u32(ip):
  return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)

我需要針對一整套允許的源網絡對每個套接字 accept() 調用進行 IP 匹配,因此我將掩碼和網絡預先計算為整數:

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 是否在這些網絡之一中:

ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
  if ip & mask == net:
    # matched!
    break
else:
  raise BadClientIP(ipstr)

不需要模塊導入,並且代碼匹配速度非常快。

Marc 的代碼幾乎是正確的。 代碼的完整版本是 -

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。 我花了很多時間讓這段代碼工作,感謝 Marc 的正確答案。

選擇的答案有一個錯誤。

以下是正確的代碼:

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)替換為((1L << int(bits)) - 1) ,因為后者似乎更容易理解。

從 Python 3.7開始,您可以使用subnet_ofsupernet_of輔助方法,它們是標准庫的一部分:

要僅針對單個 IP 進行測試,您可以使用子網掩碼/32表示“僅此 IP 地址”作為子網,或者您可以將 IP 地址傳遞給IPv4NeworkIPv6Nework構造函數,它們將為您返回子網值.

所以對於你的例子:

from ipaddress import IPv4Network, IPv4Address

# Store IP Address as variable
>>> myip = IPv4Address('192.168.0.1')
>>> myip
IPv4Address('192.168.0.1')

# This treats the IP as a subnet
>>> myip_subnet = IPv4Network(myip)
>>> myip_subnet
IPv4Network('192.168.0.1/32')

# The other subnet to test membership against
>>> other_subnet = IPv4Network('192.168.0.0/24')
>>> other_subnet
IPv4Network('192.168.0.0/24')

# Now we can test
>>> myip_subnet.subnet_of(other_subnet)
True

Python中有用於IP地址操作的通用工具嗎? 諸如主機查找,int 的 ip 地址,帶有網絡掩碼的網絡地址到 int 等的東西? 希望在 2.5 的標准 Python 庫中。

在 Python 3 中,有一個ipaddress模塊,它具有用於 IPv4 和 IPv6 操作的工具。 您可以通過強制轉換將它們轉換為 int,即int(IPv4Address('192.168.0.1')) 主機等的ipaddress模塊中還有許多其他有用的功能。

#This works properly without the weird byte by byte handling
def addressInNetwork(ip,net):
    '''Is an address in a network'''
    # Convert addresses to host order, so shifts actually make sense
    ip = struct.unpack('>L',socket.inet_aton(ip))[0]
    netaddr,bits = net.split('/')
    netaddr = struct.unpack('>L',socket.inet_aton(netaddr))[0]
    # Must shift left an all ones value, /32 = zero shift, /0 = 32 shift left
    netmask = (0xffffffff << (32-int(bits))) & 0xffffffff
    # There's no need to mask the network address, as long as its a proper network address
    return (ip & netmask) == 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

這是一個測試程序:

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

以前的解決方案在 ip & net == net 中有一個錯誤。 正確的 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");

從 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 地址作為第一個參數,並提供一個 cidr 列表作為第二個參數。 返回命中列表。

關於上述所有內容,我認為 socket.inet_aton() 按網絡順序返回字節,因此解壓它們的正確方法可能是

struct.unpack('!L', ... )

謝謝你的劇本!
我已經做了很長時間的工作以使一切正常......所以我在這里分享它

  • 使用 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

    所以我使用了來自http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/的另一個函數 calcDottedNetmask(mask)
    前任:


#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
  • 另一個問題是IP是否屬於網絡的匹配過程! 基本操作應該是比較 (ipaddr & netmask) 和 (network & netmask)。
    例如:暫時,功能錯誤

#!/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

我希望它會幫助其他人,為他們節省時間!

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')

$蟒蛇檢查子網.py
錯誤的
真的
錯誤的

我不知道標准庫中的任何內容,但PySubnetTree是一個 Python 庫,可以進行子網匹配。

從上面的各種來源以及我自己的研究來看,這就是我如何讓子網和地址計算工作的方式。 這些作品足以解決問題和其他相關問題。

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)

在 python 中有一個稱為 SubnetTree 的 API 可以很好地完成這項工作。 這是一個簡單的例子:

import SubnetTree
t = SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")
print("10.0.1.3" in t)

這是鏈接

這是我的代碼

# -*- 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]

我在這些答案中嘗試了提議的解決方案的一個子集。沒有成功,我最終調整並修復了提議的代碼並編寫了我的固定函數。

我對其進行了測試,並且至少可以在小端架構上工作——egx86——如果有人喜歡嘗試大端架構,請給我反饋。

IP2Int代碼來自這篇文章,另一種方法是完全(對於我的測試用例)對這個問題中先前提案的工作修復。

編碼:

def IP2Int(ip):
    o = map(int, ip.split('.'))
    res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
    return res


def addressInNetwork(ip, net_n_bits):
    ipaddr = IP2Int(ip)
    net, bits = net_n_bits.split('/')
    netaddr = IP2Int(net)
    bits_num = int(bits)
    netmask = ((1L << bits_num) - 1) << (32 - bits_num)
    return ipaddr & netmask == netaddr & netmask

希望有用,

這是使用 netaddr 包的解決方案

from netaddr import IPNetwork, IPAddress


def network_has_ip(network, ip):

    if not isinstance(network, IPNetwork):
        raise Exception("network parameter must be {0} instance".format(IPNetwork.__name__))

    if not isinstance(ip, IPAddress):
        raise Exception("ip parameter must be {0} instance".format(IPAddress.__name__))

    return (network.cidr.ip.value & network.netmask.value) == (ip.value & network.netmask.value)

為了避免內置或第三方模塊隨着時間的推移改變它們的語法,我創建了我自己的來做這件事。 我將其用作可導入模塊。 我希望這可以幫助別人:


def subnet_lookup(subnet: str, netmask: str, ip_address: str):
    """
    :param subnet: subnet to test against (as string)
    :param netmask: mask of subnet
    :param ip_address: ip to test against subnet and mask

    :return True if a match; False if not a match

    Steps:

    1) convert entire subnet into one binary word
    2) convert entire mask into one binary word
    3) determine bcast from comparing subnet and mask
    4) convert entire ip_address into one binary word
    5) convert entire subnet into decimal
    6) convert entire bcast into decimal
    7) convert entire ip_address into decimal
    8) determine if ip_address falls between subnet and bcast using range(); returns True if yes, False if no
    """

    def convert_whole_to_bin(whole):
        ip_dec_list = whole.split(".")
        ip_bin_str = ""

        for ip in ip_dec_list:
            binary = dec_to_bin(int(ip))
            ip_bin_str += binary

        return ip_bin_str

    def dec_to_bin(decimal_octet: int):
        binary = bin(decimal_octet).replace("0b", "")

        return binary.rjust(8, '0')

    def split_binary_into_list(binary_octet: str):
        bin_list = []
        for s in binary_octet:
            bin_list.append(s)

        return bin_list

    def determine_bcast(subnet, netmask):
        subnet_split = split_binary_into_list(subnet)
        netmask_split = split_binary_into_list(netmask)
        bcast_list = []

        for subnet, mask in zip(subnet_split, netmask_split):
            if mask != '0':
                bcast_list.append(subnet)

            else:
                bcast_list.append('1')

        bcast_bin = "".join(bcast_list)

        return bcast_bin

    def bin_to_dec(binary_single_word: str):
        decimal = int(binary_single_word, 2)

        return decimal

    def subnet_lookup(ip_address, subnet, bcast):

        return ip_address in range(subnet, bcast + 1)

    # 1) convert entire subnet into one binary word
    subnet_single_bin = convert_whole_to_bin(whole=subnet)

    # 2) convert entire mask into one binary word
    mask_single_bin = convert_whole_to_bin(whole=netmask)

    # 3) determine bcast from comparing subnet and mask
    bcast_single_bin = determine_bcast(subnet=subnet_single_bin, netmask=mask_single_bin)

    # 4) convert entire ip_address into one binary word
    ip_address_single_bin = convert_whole_to_bin(whole=ip_address)

    # 5) convert entire subnet into decimal
    subnet_single_dec = bin_to_dec(binary_single_word=subnet_single_bin)

    # 6) convert entire bcast into decimal
    bcast_single_dec = bin_to_dec(binary_single_word=bcast_single_bin)

    # 7) convert entire ip_address into decimal
    ip_address_single_dec = bin_to_dec(binary_single_word=ip_address_single_bin)

    # 8) determine if ip_address falls between subnet and bcast; returns True if yes, False if no
    lookup_result = subnet_lookup(ip_address=ip_address_single_dec, subnet=subnet_single_dec, bcast=bcast_single_dec)

    return lookup_result


# Testing:

subnet = "172.16.0.0"
netmask = "255.255.0.0"
ip_address = "172.16.255.255"

result = subnet_lookup(subnet=subnet, netmask=netmask, ip_address=ip_address)

print(result)

暫無
暫無

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

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