Étant donné une adresse IP (par exemple 192.168.0.1), comment puis-je vérifier si elle est dans 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.
Cet article montre que vous pouvez le faire avec socket
et struct
modules sans 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)
Cela génère:
False
True
Si vous voulez juste une seule fonction qui prend des chaînes, cela 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
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 ipaddress ( dans la bibliothèque stdlib depuis 3.3 , sous 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 voulez évaluer un lot d'adresses IP de cette façon, vous voudrez 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, calcule la représentation binaire avec l’un des suivants:
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 sur Linux x86. Je n'ai pas vraiment réfléchi aux problèmes de finalité, mais je l'ai testé avec le module "ipaddr" en utilisant plus de 200K adresses IP testées avec 8 chaînes de réseau différentes, et les résultats de ipaddr sont identiques à 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
Je ne suis pas fan de l'utilisation de modules quand ils ne sont pas nécessaires. Ce travail nécessite seulement des calculs simples, voici donc ma fonction simple pour faire le travail:
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
Puis 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)
Ça y est, c'est beaucoup plus rapide que les solutions ci-dessus avec les modules inclus.
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 ET entre l'adresse IP et le masque, puis en vérifiant que le résultat correspond exactement à l'adresse réseau. Pas ANDing l'adresse IP avec l'adresse réseau comme cela a été fait.
J'ai aussi remarqué que le fait d'ignorer le comportement Endian en supposant que la cohérence vous évitera ne fonctionnera que pour les masques situés sur les limites des octets (/ 24,/16). Pour que d’autres masques (/ 23,/21) fonctionnent correctement, j’ai ajouté un "supérieur à" aux commandes de struct, puis modifié le code permettant de créer le masque binaire de manière à ce que tous les "1" soient décalés à gauche (32-mask). ).
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
La réponse acceptée ne fonctionne pas ... ce qui me met en colère. Le masque est à l'envers et ne fonctionne avec aucun bit qui ne soit 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 la sortie ipcalc
.
def bb(i):
def s = '{:032b}'.format(i)
def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]
par exemple:
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 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.
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)
S'appuyer sur le module "struct" peut poser des problèmes d'endurance 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 en tant qu'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.
La réponse choisie a un bug.
Voici le code correct:
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)
, car 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
# Ceci fonctionne correctement sans la gestion bizarre octet par octet Def addressInNetwork (ip, net): '' 'Est-ce une adresse dans un réseau' ''. # Convertir les adresses en ordre d'hôte, les changements ont donc du sens ip = struct.unpack ('> L', socket.inet_aton (ip)) [0] netaddr, bits = net.split ('/') netaddr = struct.unpack ('> L', socket.inet_aton (netaddr)) [0] # Doit décaler à gauche toutes les valeurs,/32 = décalage à zéro,/0 = 32 décalage à gauche masque de réseau = (0xffffffff & lt & lt (32-int (bits))) & 0xffffffff # Il n’est pas nécessaire de masquer l’adresse réseau tant que cette adresse est correcte return (ip & netmask) == netaddr
>>> 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.
Fondamentalement, vous fournissez une adresse IP comme premier argument et une liste de cidrs comme second argument. Une liste des hits sont retournés.
Merci pour votre script!
J'ai beaucoup travaillé dessus pour que tout fonctionne ... Alors je le partage ici
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.0
J'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'
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!
Donc, ma nouvelle fonction addressInNetwork ressemble à ceci:
#!/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 juste !!
#!/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!
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.
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)
la solution précédente a un bug dans ip & net == net. La recherche ip correcte est ip & netmask = 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");
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;
struct.unpack('!L', ... )
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')
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 les architectures Endian peu développées - par exemple, x86 - si quelqu'un aime essayer une architecture big endian, donnez-moi s'il vous plaît des informations.
IP2Int
code provient de this post , l’autre méthode est un correctif de travail complet (pour mes cas de test) des propositions précédentes de 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,
De différentes sources ci-dessus et de mes propres recherches, voici comment j'ai obtenu le calcul de sous-réseau et d'adresse. 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)
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]