¿Cómo puedo verificar si una IP está en una red en Python?

Dada una dirección IP (digamos 192.168.0.1), ¿cómo verifico si está en una red (digamos 192.168.0.0/24) en Python?

¿Existen herramientas generales en Python para la manipulación de direcciones IP? ¿Cosas como las búsquedas de host, dirección IP a int, dirección de red con máscara de red a int y así sucesivamente? Esperemos que en la biblioteca estándar de Python para 2.5.

Este artículo muestra que puede hacerlo con módulos socket y struct sin demasiado esfuerzo adicional. Agregué un poco al artículo de la siguiente manera:

 import socket,struct def makeMask(n): "return a mask of n bits as a long integer" return (2L< 

Esto produce:

 False True 

Si solo quieres una única función que tenga cadenas, se vería así:

 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< 

Me gusta usar netaddr para eso:

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

Como arno_v señaló en los comentarios, la nueva versión de netaddr lo hace así:

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

Usando ipaddress ( en el stdlib desde 3.3 , en PyPi para 2.6 / 2.7 ):

 >>> import ipaddress >>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24') True 

Si desea evaluar muchas direcciones IP de esta manera, probablemente querrá calcular la máscara de red por adelantado, como

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

Luego, para cada dirección, calcule la representación binaria con uno de

 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 

Por último, puede simplemente comprobar:

 in_network = (a & mask) == netw 

Este código me funciona en Linux x86. Realmente no he pensado en los problemas de endianess, pero lo he probado contra el módulo “ipaddr” usando más de 200K de direcciones IP probadas contra 8 cadenas de red diferentes, y los resultados de ipaddr son los mismos que este código.

 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) 

Ejemplo:

 >>> print addressInNetwork('10.9.8.7', '10.9.1.0/16') True >>> print addressInNetwork('10.9.8.7', '10.9.1.0/24') False 

Para python3

 In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24') Out[64]: False 

Probé la solución de Dave Webb pero encontré algunos problemas:

Fundamentalmente, la coincidencia se debe verificar mediante la AND con la dirección IP con la máscara, y luego se verifica que el resultado coincida exactamente con la dirección de la red. No ANDANDO la dirección IP con la dirección de Red como se hizo.

También me di cuenta de que ignorar el comportamiento de Endian suponiendo que la consistencia te ahorrará solo funcionará para máscaras en los límites de octetos (/ 24, / 16). Para hacer que otras máscaras (/ 23, / 21) funcionen correctamente, agregué un “mayor que” a los comandos de estructura y cambié el código para crear la máscara binaria para comenzar con todos los “1” y moverme a la izquierda con (32-máscara ).

Finalmente, agregué una simple comprobación de que la dirección de red es válida para la máscara y solo imprima una advertencia si no lo es.

Aquí está el resultado:

 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 

No soy un fanático de usar módulos cuando no son necesarios. Este trabajo solo requiere matemáticas simples, así que aquí está mi función simple para hacer el trabajo:

 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 

Entonces para usarlo:

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

Eso es todo, esto es mucho más rápido que las soluciones anteriores con los módulos incluidos.

La respuesta aceptada no funciona … lo que me está enojando. La máscara está hacia atrás y no funciona con ningún bit que no sea un simple bloque de 8 bits (por ejemplo, / 24). Adapte la respuesta, y funciona muy 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 

Aquí hay una función que devuelve una cadena binaria de puntos para ayudar a visualizar el enmascaramiento … algo así como la salida de ipcalc .

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

p.ej:

captura de pantalla de python

No en la biblioteca estándar para 2.5, pero ipaddr lo hace muy fácil. Creo que está en 3.3 bajo el nombre de 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) 

El código de Marc es casi correcto. Una versión completa del código es –

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

Obviamente de las mismas fonts que arriba …

Una nota muy importante es que el primer código tiene una pequeña falla: la dirección IP 255.255.255.255 también se muestra como una IP válida para cualquier subred. Tuve un montón de tiempo para hacer funcionar este código y gracias a Marc por la respuesta correcta.

Confiar en el módulo “estructura” puede causar problemas con la endiancia y el tamaño de los tipos, y simplemente no es necesario. Tampoco es socket.inet_aton (). Python funciona muy bien con direcciones IP de cuatro puntos:

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

Necesito hacer una comparación de IP en cada llamada de socket accept () contra un conjunto completo de redes de origen permitidas, por lo que calculo las máscaras y las redes como enteros:

 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 

Entonces puedo ver rápidamente si una IP determinada está dentro de una de esas redes:

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

No se necesitan importaciones de módulos, y el código es muy rápido en la comparación.

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

Aquí está el uso de este método:

 >>> 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. 

Básicamente, proporciona una dirección IP como primer argumento y una lista de cidrs como segundo argumento. Se devuelve una lista de hits.

 # Esto funciona correctamente sin el extraño byte por byte de manejo
 def addressInNetwork (ip, net):
     '' 'Es una dirección en una red' ''
     # Convertir las direcciones al orden del host, por lo que los turnos realmente tienen sentido
     ip = struct.unpack ('> L', socket.inet_aton (ip)) [0]
     netaddr, bits = net.split ('/')
     netaddr = struct.unpack ('> L', socket.inet_aton (netaddr)) [0]
     # Debe desplazarse a la izquierda un valor de todos, / 32 = cambio a cero, / 0 = cambio a la izquierda
     máscara de red = (0xffffffff << (32-int (bits))) y 0xffffffff
     # No es necesario enmascarar la dirección de red, siempre que sea una dirección de red adecuada
     return (ip & netmask) == netaddr 

La respuesta elegida tiene un error.

El siguiente es el código correcto:

 def addressInNetwork(ip, net_n_bits): ipaddr = struct.unpack(' 

Nota: ipaddr & netmask == netaddr & netmask lugar de ipaddr & netmask == netmask .

También sustituyo ((2L< por ((1L << int(bits)) - 1) , ya que este último parece más comprensible.

Aquí hay una clase que escribí para la coincidencia de prefijo más larga:

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

Y aquí hay un progtwig de prueba:

 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 

¡Gracias por tu guión!
Tengo mucho trabajo para hacer que todo funcione … Así que lo comparto aquí.

  • El uso de la clase netaddr es 10 veces más lento que el uso de la conversión binaria, por lo que si desea usarlo en una gran lista de IP, debe considerar no usar la clase netaddr
  • ¡La función makeMask no está funcionando! Solo trabajando para / 8, / 16, / 24
    Ex:

    bits = “21”; socket.inet_ntoa (struct.pack (‘= L’, (2L << int (bits) -1) - 1))
    ‘255.255.31.0’ mientras que debería ser 255.255.248.0

    Así que he usado otra función calcDottedNetmask (máscara) de http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
    Ex:

 #!/usr/bin/python >>> calcDottedNetmask(21) >>> '255.255.248.0' 
  • Otro problema es el proceso de hacer coincidir si una IP pertenece a una red. La operación básica debe ser comparar (ipaddr y netmask) y (red y netmask).
    Ej: por el momento, la función es incorrecta.
 #!/usr/bin/python >>> addressInNetwork('188.104.8.64','172.16.0.0/12') >>>True which is completely WRONG!! 

Así que mi nueva función addressInNetwork se parece a:

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

Y ahora, la respuesta es correcta !!

 #!/usr/bin/python >>> addressInNetwork('188.104.8.64','172.16.0.0/12') False 

Espero que ayude a otras personas, ¡ahorrando tiempo para ellos!

En relación con todo lo anterior, creo que socket.inet_aton () devuelve bytes en orden de red, por lo que la manera correcta de descomprimirlos es probablemente

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

La solución anterior tiene un error en ip & net == net. La búsqueda ip correcta es ip & netmask = net

Código de corrección de errores:

 import socket import struct def makeMask(n): "return a mask of n bits as a long integer" return (2L< 

Existe una API llamada SubnetTree disponible en Python que hace muy bien este trabajo. Este es un ejemplo simple :

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

Este es el enlace

 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
Falso
Cierto
Falso

No conozco nada en la biblioteca estándar, pero PySubnetTree es una biblioteca de Python que hará una coincidencia de subred.

De varias fonts mencionadas anteriormente, y de mi propia investigación, así es como funcionó el cálculo de direcciones y subredes. Estas piezas son suficientes para resolver la pregunta y otras preguntas relacionadas.

 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) 

Aqui esta mi codigo

 # -*- 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 no desea importar otros módulos, puede ir con:

 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] 

Probé un subconjunto de soluciones propuestas en estas respuestas … sin éxito, finalmente adapté y arreglé el código propuesto y escribí mi función fija.

Lo probé y funciona al menos en architectures little endian, por ejemplo, 86, si a alguien le gusta probar una architecture big endian, por favor, dame un comentario.

IP2Int código IP2Int proviene de esta publicación , el otro método es una solución que funciona completamente (para mi caso de prueba) de propuestas anteriores en esta pregunta.

El código:

 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 

Esperanza útil,