131 votes

Comment puis-je vérifier si une ip est dans un réseau en Python ?

Étant donné une adresse IP (disons 192.168.0.1), comment puis-je vérifier si elle est dans un réseau (disons 192.168.0.0/24) en Python ?

Existe-t-il des outils généraux en Python pour la manipulation des adresses IP ? Des choses comme la recherche d'hôtes, l'adresse IP en int, l'adresse réseau avec le masque de réseau en int, etc ? Avec un peu de chance, dans la bibliothèque Python standard de la version 2.5.

0 votes

Cette question semble être un canonical correct pour les très anciennes réponses 2.x, mais elle est obsolète pour 3.x See Comment organiser et attribuer des canoniques pour "Python/pandas compare adresse IP/CIDR" ?

0 votes

@smci Je ne vois pas pourquoi ; la réponse de phihag à stackoverflow.com/a/1004527/1709587 est une réponse parfaitement bonne pour Python 3 et est là depuis 2014. J'ai annulé votre modification qui invalidait cette réponse.

0 votes

@Staale - Vous devriez mettre à jour votre réponse ici en une qui n'a pas de bogue critique . Les autres réponses utilisent des bibliothèques intégrées pour accomplir la même chose en 1/10e de la quantité de code, sans aucun bogue.

189voto

phihag Points 89765

Utilisation de adresse IP ( dans la stdlib depuis 3.3 , à 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 manière, vous voudrez probablement calculer le masque de réseau à l'avance, comme suit

n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)

Ensuite, pour chaque adresse, calculez la représentation binaire avec l'une des méthodes suivantes

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

3 votes

Attention, python-ipaddr s'est comporté assez lentement pour nous, donc il mai être inadapté à certains cas où de nombreuses comparaisons sont nécessaires fréquemment. C'est à chacun de voir, alors faites vos propres comparaisons.

0 votes

Dans certaines versions, vous devrez peut-être fournir une chaîne unicode au lieu d'un type Python str, comme par exemple ipaddress.ip_address(u'192.168.0.1') in ipaddress.ip_network(u'192.168.0.0/24') .

3 votes

J'étais préoccupé par le fait que cette méthode itère sur la liste des adresses du réseau, mais le module ipaddress supplante __contains__ pour le faire de manière efficace en comparant les représentations entières des adresses de réseau et de diffusion, donc rassurez-vous si c'était votre souci.

172voto

nosklo Points 75862

J'aime utiliser netaddr pour ça :

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 de cette façon :

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

0 votes

>>> netaddr.all_matching_cidrs("192.168.0.1", ["192.168.0.0/24", "212.11.64.0/19"] ) [IPNetwork('192.168.0.0/24')]

27 votes

Ou dans la nouvelle version : from netaddr import IPNetwork, IPAddress IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24")

1 votes

Notez que cette approche, bien que l'API soit agréable, nécessite l'installation d'un module tiers. Alors que l'autre réponse, qui traite du module ipaddress intégré, ne le fait pas.

27voto

Dave Webb Points 90034

Cet article montre que vous pouvez le faire avec socket y struct sans trop d'efforts supplémentaires. 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)

Ces sorties :

False
True

Si vous voulez juste une fonction unique qui prend des chaînes de caractères, 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

3 votes

Je suppose qu'il peut y avoir des problèmes de grand et petit endian ici, mais puisque la résolution du réseau et de l'adresse IP se fait sur le même ordinateur, les deux nombres devraient avoir la même erreur et au moins être dans le même ordre endian.

0 votes

Bon point, bien que je ne pense pas qu'il y ait de "problèmes" en tant que tels. La chose essentielle est que stuct.unpack et socket.inet_aton utilisent par défaut l'endianness du système, donc le long que vous obtenez sera toujours OK et correspondra aux masques créés par la fonction makeMask. Une fois que vous avez des longs, l'ordre dans lequel les octets sont vérifiés lorsque vous utilisez les opérateurs bit à bit n'a plus d'importance.

2 votes

AFAIK inet_aton utilise par défaut l'ordre des octets du réseau, qui est big endian. En fait, votre code dépend du fait qu'inet_aton ait un ordre endianné. en face de à celui du système, de sorte que les premiers octets de l'adresse IP (par exemple 10 dans "10.0.0.0") s'alignent sur les octets les moins significatifs du masque (par exemple 0xff dans 0x000000ff).

15voto

Ce code fonctionne pour moi sur Linux x86. Je n'ai pas vraiment réfléchi aux problèmes d'endianess, mais je l'ai testé par rapport au module "ipaddr" en utilisant plus de 200K adresses IP testées par rapport à 8 chaînes réseau différentes, et les résultats d'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

2 votes

Agréable et rapide. Pas besoin d'une bibliothèque pour quelques opérations logiques simples.

6voto

Paul McDonnell Points 41

J'ai essayé la solution de Dave Webb mais j'ai rencontré quelques problèmes :

Plus fondamentalement, une correspondance doit être vérifiée en faisant un ET entre l'adresse IP et le masque, puis en vérifiant que le résultat correspond exactement à l'adresse réseau. Et non pas en faisant un ET entre l'adresse IP et l'adresse réseau comme cela a été fait.

J'ai également remarqué que le fait d'ignorer le comportement de l'Endian en supposant que la cohérence vous sauvera ne fonctionne que pour les masques sur les limites d'octets (/24, /16). Pour que les autres masques (/23, /21) fonctionnent correctement, j'ai ajouté un "plus grand que" aux commandes struct et j'ai modifié le code de création du masque binaire pour commencer par tous les "1" et décaler à gauche par (32-mask).

Enfin, j'ai ajouté une simple vérification que l'adresse réseau est valide pour le masque et j'ai simplement imprimé 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

0 votes

Cette méthode semble fonctionner de manière fiable sur 64 bits ('L' échoue car la valeur est 32 bits) et renvoie les valeurs dans un ordre raisonnable (ipaddr sera 0xC0A80001 pour 192.168.0.1). Il s'adapte également à "192.168.0.1/24" comme masque de réseau pour "192.168.0.1" (pas standard, mais possible et facilement corrigeable).

0 votes

Fonctionne parfaitement sur Python 2.4

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X