Comment puis-je vérifier si une adresse IP est dans un réseau en Python?
-
03-07-2019 - |
Question
Étant donné une adresse IP (par exemple 192.168.0.1), comment puis-je vérifier si elle se trouve sur un réseau (par exemple 192.168.0.0/24) en Python?
Existe-t-il des outils généraux en Python pour la manipulation des adresses IP? Des trucs comme des recherches d’hôte, une adresse IP à int, une adresse réseau avec un masque de réseau à int, etc.? Espérons que dans la bibliothèque standard Python pour 2.5.
La solution
Cet article montre que vous pouvez le faire avec socket
et struct
modules sans trop d'effort supplémentaire. J'ai ajouté un peu à l'article comme suit:
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)
Cette sortie:
False
True
Si vous voulez juste une fonction qui prend des chaînes, elle ressemblerait à ceci:
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
Autres conseils
J'aime utiliser netaddr pour cela:
from netaddr import CIDR, IP
if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
print "Yay!"
Comme arno_v l'a souligné dans les commentaires, la nouvelle version de netaddr le fait comme ceci:
from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
print "Yay!"
Utilisation de adresse IP ( dans la bibliothèque stdlib depuis la version 3.3 , sur PyPi pour 2.6 / 2.7 ):
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24')
True
Si vous souhaitez évaluer un lot d'adresses IP de cette manière, vous souhaiterez probablement calculer le masque de réseau à l'avance, comme
.n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)
Puis, pour chaque adresse, calculez la représentation binaire avec l’un des
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
Enfin, vous pouvez simplement vérifier:
in_network = (a & mask) == netw
Ce code fonctionne pour moi sous Linux x86. Je n'ai pas vraiment réfléchi aux problèmes d'endianess, mais je l'ai testé contre & "Ipaddr &"; module utilisant plus de 200K adresses IP testées sur 8 chaînes de réseau différentes, et les résultats de ipaddr sont les mêmes que ce code.
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)
Exemple:
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False
Pour python3
In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
Out[64]: False
J'ai essayé la solution de Dave Webb mais j'ai rencontré quelques problèmes:
Fondamentalement, une correspondance doit être vérifiée en effectuant une opération AND avec l'adresse du masque avec le masque, puis en vérifiant que le résultat correspond exactement à l'adresse du réseau. Ne pas utiliser l'adresse IP avec l'adresse réseau comme cela a été fait.
J'ai aussi remarqué qu'ignorer le comportement Endian en supposant que la cohérence vous évitera de ne travailler que pour les masques sur les limites d'octets (/ 24, / 16). Pour que d'autres masques (/ 23, / 21) fonctionnent correctement, j'ai ajouté un & Quotient supérieur à & Quot; aux commandes de struct et changé le code pour la création du masque binaire pour commencer par tous les " 1 " et décaler à gauche (masque 32).
Enfin, j’ai ajouté une simple vérification de la validité de l’adresse réseau pour le masque et simplement imprimer un avertissement si ce n’est pas le cas.
Voici le résultat:
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
Je ne suis pas fan de l'utilisation de modules lorsqu'ils ne sont pas nécessaires. Ce travail ne nécessite que des calculs simples. Voici donc ma fonction simple:
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
Ensuite, pour l'utiliser:
>>> 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)
C'est ça, c'est beaucoup plus rapide que les solutions ci-dessus avec les modules inclus.
La réponse acceptée ne fonctionne pas ... ce qui me met en colère. Le masque est à l'envers et ne fonctionne pas avec des bits qui ne sont pas un simple bloc de 8 bits (par exemple / 24). J'ai adapté la réponse et cela fonctionne bien.
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
voici une fonction qui retourne une chaîne binaire en pointillés pour aider à visualiser le masquage .. un peu comme ipcalc
en sortie.
def bb(i):
def s = '{:032b}'.format(i)
def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]
par exemple:
Pas dans la bibliothèque Standard pour 2.5, mais ipaddr rend cela très facile. Je crois que c'est en 3.3 sous le nom 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)
Le code de Marc est presque correct. Une version complète du code est -
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))
Évidemment, à partir des mêmes sources que ci-dessus ...
Une remarque très importante est que le premier code a un petit problème - L'adresse IP 255.255.255.255 apparaît également comme une adresse IP valide pour tout sous-réseau. J'ai passé énormément de temps à faire fonctionner ce code et merci à Marc pour la bonne réponse.
S'appuyant sur le " struct " module peut causer des problèmes d’endian-ness et de tailles de caractères, ce qui n’est pas nécessaire. Socket.inet_aton () non plus. Python fonctionne très bien avec les adresses IP en pointillés:
def ip_to_u32(ip):
return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)
Je dois effectuer une correspondance IP sur chaque appel accept () du socket, par rapport à tout un ensemble de réseaux sources autorisés. Par conséquent, je pré-calcule les masques et les réseaux, sous forme d'entiers:
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
Ensuite, je peux rapidement voir si une adresse IP donnée se trouve dans l'un de ces réseaux:
ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
if ip & mask == net:
# matched!
break
else:
raise BadClientIP(ipstr)
Aucune importation de module requise et le code est très rapide à la correspondance.
de netaddr importez 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')]
Voici l'utilisation de cette méthode:
>>> 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.
En gros, vous fournissez une adresse IP comme premier argument et une liste de cidrs comme deuxième argument. Une liste de résultats est renvoyée.
#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
La réponse choisie a un bug.
Le code correct est le suivant:
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
Remarque: ipaddr & netmask == netaddr & netmask
au lieu de ipaddr & netmask == netmask
.
Je remplace également ((2L<<int(bits)-1) - 1)
par ((1L << int(bits)) - 1)
, ce dernier semble plus compréhensible.
Voici une classe que j'ai écrite pour la correspondance du préfixe le plus long:
#!/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
Et voici un programme de test:
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
Merci pour votre script!
Je travaille assez longtemps pour que tout fonctionne correctement ... Donc, je le partage ici
- L'utilisation de netaddr Class est 10 fois plus lente que celle de la conversion binaire. Si vous souhaitez l'utiliser sur une grande liste d'adresses IP, vous devriez envisager de ne pas utiliser la classe netaddr
-
La fonction makeMask ne fonctionne pas! Ne travaille que pour / 8, / 16, / 24
Ex:bits = " 21 " ; socket.inet_ntoa (struct.pack ('= L', (2L < < int (bits) -1) - 1))
'255.255.31.0' alors qu'il devrait être 255.255.248.0J'ai donc utilisé une autre fonction calcDottedNetmask (masque) de http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
Ex:
#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
- Un autre problème est le processus de mise en correspondance si une adresse IP appartient à un réseau! Les opérations de base doivent consister à comparer (ipaddr & Et masque de réseau) et (réseau & De masque; masque de réseau).
Ex: pour l’instant, la fonction est incorrecte
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!
Ainsi, ma nouvelle fonction addressInNetwork ressemble à:
#!/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))
Et maintenant, la réponse est bonne !!
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
False
J'espère que cela aidera d'autres personnes en leur faisant gagner du temps!
En ce qui concerne tout ce qui précède, je pense que socket.inet_aton () renvoie les octets dans l’ordre du réseau. Le moyen correct de les décompresser est probablement
struct.unpack('!L', ... )
La solution précédente a un bogue dans ip & amp; net == net. La recherche ip correcte est ip & Amp; masque de réseau = net
code corrigé:
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");
Il existe une API appelée SubnetTree disponible en python qui fait très bien ce travail. Voici un exemple simple:
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
Faux
Vrai
Faux
Je ne connais rien dans la bibliothèque standard, mais PySubnetTree est une bibliothèque Python qui fera la correspondance de sous-réseau.
De différentes sources ci-dessus et de mes propres recherches, c’est ainsi que le calcul des sous-réseaux et des adresses a fonctionné. Ces pièces sont suffisantes pour résoudre la question et d’autres questions connexes.
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)
Voici mon code
# -*- 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')
Si vous ne souhaitez pas importer d'autres modules, vous pouvez utiliser:
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]
J'ai essayé un sous-ensemble de solutions proposées dans ces réponses .. sans succès, j'ai finalement adapté et corrigé le code proposé et écrit ma fonction fixe.
Je l'ai testé et fonctionne au moins sur des architectures little endian - par exemple, x86 - si quelqu'un aime essayer une architecture big endian, donnez-moi s'il vous plaît vos commentaires.
IP2Int
le code provient de cette publication , l'autre méthode est un correctif de travail complet (pour mes cas de test). des propositions précédentes dans cette question.
Le code:
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
J'espère utile,