PythonでIPがネットワークにあるかどうかを確認するにはどうすればよいですか?
-
03-07-2019 - |
質問
IPアドレス(たとえば192.168.0.1)を指定した場合、Pythonでネットワーク(たとえば192.168.0.0/24)にあるかどうかを確認するにはどうすればよいですか?
PythonでIPアドレスを操作するための一般的なツールはありますか?ホストルックアップ、IPアドレスをintに、ネットワークアドレスをintに、などのホストルックアップのようなものですか?できれば2.5の標準Pythonライブラリで。
解決
この記事は、 socket
および struct
モジュールはあまり手間がかかりません。次のように記事に少し追加しました。
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
他のヒント
そのために 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!"
ipaddress の使用( 3.3以降のstdlib内、 2.6 / 2.7のPyPiで):
>>> 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
このコードはLinux x86上で動作します。エンディアンネスの問題については何も考えていませんが、<!> quot; ipaddr <!> quot;に対してテストしました。 8つの異なるネットワーク文字列に対してテストされた20万を超えるIPアドレスを使用するモジュール。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
python3の場合
In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
Out[64]: False
Dave Webbのソリューションを試しましたが、いくつかの問題が発生しました:
最も基本的には、IPアドレスとマスクをANDして、ネットワークアドレスと正確に一致する結果を確認することにより、一致を確認する必要があります。行われたように、IPアドレスとネットワークアドレスのAND演算を行いません。
また、一貫性があなたを救うと仮定してエンディアンの振る舞いを無視することは、オクテット境界(/ 24、/ 16)のマスクに対してのみ働くことに気づきました。他のマスク(/ 23、/ 21)が正常に機能するように、<!> quot; greater than <!> quot;を追加しました。すべての<!> quot; 1 <!> quotで始まるように、structコマンドにバイナリマスクを作成するためのコードを変更しました。左に(32マスク)シフトします。
最後に、ネットワークアドレスがマスクに対して有効であるという簡単なチェックを追加し、有効でない場合は警告を出力します。
結果は次のとおりです。
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
必要のないモジュールを使用するのは好きではありません。このジョブは単純な数学のみを必要とするため、ジョブを実行するための単純な関数を次に示します。
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)
それだけです。これは、上記のモジュールを含むソリューションよりもはるかに高速です。
受け入れられた答えは機能しません...これは私を怒らせています。マスクは逆向きであり、単純な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]
eg:
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)
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としても表示されます。私はこのコードを機能させるのにかなりの時間を費やし、正しい答えをしてくれたマークに感謝します。
<!> quot; struct <!> quot;に依存モジュールは、エンディアンネスと型のサイズに問題を引き起こす可能性があり、単に必要ではありません。 socket.inet_aton()もありません。 Pythonは、ドットで区切られた4つの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)
モジュールのインポートは不要で、コードは照合時に非常に高速です。
netaddr import 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アドレスを、2番目の引数としてcidrのリストを提供します。ヒットのリストが返されます。
#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
選択した回答にはバグがあります。
次は正しいコードです:
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)
に置き換えました。後者の方がわかりやすいようです。
これは、最長のプレフィックス一致用に作成したクラスです。
#!/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
スクリプトをありがとう!
私はすべてを機能させるためにかなり長い間取り組んでいます...だから私はここで共有しています
- netaddrクラスを使用すると、バイナリ変換を使用するよりも10倍遅くなるため、IPの大きなリストで使用する場合は、netaddrクラスを使用しないことを検討してください
-
makeMask関数が機能していません! / 8、/ 16、/ 24のみで機能します
例:bits = <!> quot; 21 <!> quot; ; socket.inet_ntoa(struct.pack( '= L'、(2L <!> lt; <!> lt; 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/
例:
#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
- 別の問題は、IPがネットワークに属している場合の照合プロセスです!基本的な操作は、(ipaddr <!> amp; netmask)と(network <!> amp; 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
他の人の助けになり、時間を節約できることを願っています!
上記のすべてに関連して、socket.inet_aton()はネットワーク順にバイトを返すと思うので、それらを解凍する正しい方法はおそらく
struct.unpack('!L', ... )
以前のソリューションにはip <!> amp;にバグがあります。 net == net。正しいIPルックアップはip <!> ampです。 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");
この仕事を非常にうまく行うPythonで利用可能なSubnetTreeと呼ばれるAPIがあります。 これは簡単な例です:
import SubnetTree
t = SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")
print("10.0.1.3" in t)
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')
$ python check-subnet.py
False
True
False
標準ライブラリには何もありませんが、 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)
ここに私のコードがあります
# -*- 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]
これらの回答で提案されたソリューションのサブセットを試してみました。成功せず、最終的に提案されたコードを適応および修正し、固定機能を作成しました。
私はそれをテストし、少なくともリトルエンディアンアーキテクチャ(例:x86)で動作します。誰かがビッグエンディアンアーキテクチャを試してみたい場合は、フィードバックをお願いします。
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
希望、