Como posso verificar se um ip está em uma rede em Python?


100

Dado um endereço IP (digamos 192.168.0.1), como faço para verificar se ele está em uma rede (digamos 192.168.0.0/24) no Python?

Existem ferramentas gerais em Python para manipulação de endereços IP? Coisas como pesquisas de host, endereço de IP para int, endereço de rede com máscara de rede para int e assim por diante? Esperançosamente na biblioteca Python padrão para 2.5.


Esta pergunta parece ser um canônico correto para respostas 2.x muito antigas, mas é obsoleto para 3.x Consulte Como organizar e atribuir canônicos para “Python / pandas comparar endereço IP / CIDR”?
SMCI

@smci Não vejo por quê; A resposta de phihag em stackoverflow.com/a/1004527/1709587 é uma resposta perfeitamente boa para Python 3 e está aqui desde 2014. Reverti sua edição que invalidou essa resposta.
Mark Amery de

@Staale - Você deve atualizar sua resposta aqui para uma que não tenha um bug crítico . As outras respostas usam bibliotecas integradas para realizar a mesma coisa em 1/10 da quantidade de código, sem quaisquer bugs.
Addison

Respostas:


30

Este artigo mostra que você pode fazê-lo com sockete structmódulos sem muito esforço extra. Eu adicionei um pouco ao artigo da seguinte maneira:

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)

Isso resulta em:

False
True

Se você quiser apenas uma única função que receba strings, ela ficaria assim:

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

8
Além disso, struct.unpack ('L', socket.inet_aton (ip)) [0] irá falhar em arquiteturas onde 'L' descompacta para algo diferente de 4 bytes, independentemente do endianness.
Rafał Dowgird

5
Continuando com o comentário de Rafal, para fazer isso funcionar em um interpretador Python de 64 bits, substitua a linha em questão por:return struct.unpack('<L',socket.inet_aton(ip))[0]
imbecil

11
Acho que sua solução tem um bug sério: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True(converti 'L' em '<L' em meu sistema operacional de 64 bits)
Taha Jahangir de

20
CUIDADO: Esta solução tem um bug sério:addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True
Taha Jahangir

6
Esta resposta tem um bug . Veja a resposta: stackoverflow.com/questions/819355/…
Debanshu Kundu

156

Eu gosto de usar o netaddr para isso:

from netaddr import CIDR, IP

if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
    print "Yay!"

Como arno_v apontou nos comentários, a nova versão do netaddr faz assim:

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

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

22
Ou na nova versão: from netaddr import IPNetwork, IPAddress IPAddress ("192.168.0.1") in IPNetwork ("192.168.0.0/24")
arno_v

140

Usando ipaddress ( no stdlib desde 3.3 , no 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

Se você quiser avaliar muitos endereços IP dessa maneira, provavelmente desejará calcular a máscara de rede antecipadamente, como

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

Então, para cada endereço, calcule a representação binária com um 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

Finalmente, você pode simplesmente verificar:

in_network = (a & mask) == netw

2
Cuidado, o python-ipaddr se comportou bem devagar para nós, então pode ser inadequado para alguns casos em que muitas comparações são necessárias com frequência. YMMV, então compare você mesmo.
drdaeman

Em algumas versões, pode ser necessário fornecer uma string Unicode em vez de um tipo str Python, como ipaddress.ip_address(u'192.168.0.1') in ipaddress.ip_network(u'192.168.0.0/24').
Moondoggy

1
Eu estava preocupado que esse método estivesse iterando na lista de endereços na rede, mas o módulo ipaddress substitui o __contains__método para fazê-lo de forma eficiente, comparando as representações inteiras da rede e dos endereços de broadcast, então fique tranquilo se essa foi a sua preocupação .
avatarofhope2 01 de


10

Este código está funcionando para mim no Linux x86. Eu realmente não pensei em problemas de endianess, mas eu o testei no módulo "ipaddr" usando mais de 200 mil endereços IP testados em 8 strings de rede diferentes, e os resultados de ipaddr são iguais a 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)

Exemplo:

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

Bom e rápido. Não há necessidade de uma biblioteca para algumas operações lógicas simples.
Chris Koston

7

Usando ipaddress Python3 :

import ipaddress

address = ipaddress.ip_address("192.168.0.1")
network = ipaddress.ip_network("192.168.0.0/16")

print(network.supernet_of(ipaddress.ip_network(f"{address}/{address.max_prefixlen}")))

Explicação

Você pode pensar em um endereço IP como uma rede com a maior máscara de rede possível ( /32para IPv4, /128para IPv6)

Verificar se 192.168.0.1está dentro 192.168.0.0/16é essencialmente o mesmo que verificar se 192.168.0.1/32é uma sub-rede de192.168.0.0/16


... não sei por que essa resposta não está no topo (ainda).
Filippo Vitale

6

Tentei a solução de Dave Webb, mas encontrei alguns problemas:

Mais fundamentalmente - uma correspondência deve ser verificada colocando o endereço IP em AND com a máscara e, em seguida, verificando se o resultado corresponde ao endereço de rede exatamente. Não fazer AND do endereço IP com o endereço de rede como foi feito.

Eu também percebi que simplesmente ignorar o comportamento de Endian, assumindo que a consistência o salvará, funcionará apenas para máscaras nos limites do octeto (/ 24, / 16). Para que outras máscaras (/ 23, / 21) funcionem corretamente, adicionei um "maior que" aos comandos de estrutura e alterei o código para a criação da máscara binária para começar com "1" e deslocar para a esquerda em (32-máscara )

Por fim, adicionei uma verificação simples de que o endereço de rede é válido para a máscara e apenas imprimo um aviso se não for.

Aqui está o 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

Isso parece funcionar de forma confiável em 64 bits ('L' falha porque o valor é 32 bits) e retorna os valores em uma ordem razoável (ipaddr será 0xC0A80001 para 192.168.0.1). Ele também lida com "192.168.0.1/24" como uma máscara de rede para "192.168.0.1" (não é padrão, mas possível e facilmente corrigível)
IBBoard

Funciona perfeitamente no Python 2.4
xlash

6

Não sou fã de usar módulos quando eles não são necessários. Este trabalho requer apenas matemática simples, então aqui está minha função simples para fazer o trabalho:

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

Então, para usá-lo:

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

É isso, é muito mais rápido do que as soluções acima com os módulos incluídos.


{TypeError}'map' object is not subscriptable. Você precisa de um o = list(o)aftero = map(int, ip.split('.'))
gies0r

5

Não na biblioteca padrão para 2.5, mas ipaddr torna isso muito fácil. Acredito que esteja no 3.3 com o nome 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)

Este é de longe o meu favorito de todas as inúmeras opções aqui (na época do comentário, 2017). Obrigado!
rsaw

5

A resposta aceita não funciona ... o que está me deixando com raiva. A máscara está invertida e não funciona com nenhum bit que não seja um bloco de 8 bits simples (por exemplo, / 24). Adaptei a resposta e funciona bem.

    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

aqui está uma função que retorna uma string binária pontilhada para ajudar a visualizar o mascaramento .. tipo de ipcalcsaída semelhante .

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

por exemplo:

captura de tela de python


4

O código de Marc está quase correto. Uma versão completa do código é -

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, das mesmas fontes acima ...

Uma observação muito importante é que o primeiro código tem uma pequena falha - O endereço IP 255.255.255.255 também aparece como um IP válido para qualquer sub-rede. Tive muito tempo para fazer esse código funcionar e agradeço ao Marc pela resposta correta.


Experimentado e testado. De todos os exemplos de socket / struct nesta página, este é o único correto
Zabuzzman

4

Depender do módulo "struct" pode causar problemas com endian-ness e tamanhos de tipo, e simplesmente não é necessário. Nem socket.inet_aton (). Python funciona muito bem com endereços IP quádruplos com pontos:

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

Preciso fazer a correspondência de IP em cada chamada accept () de soquete, em relação a todo um conjunto de redes de origem permitidas, então eu pré-calculo máscaras e redes, como números inteiros:

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

Então posso ver rapidamente se um determinado IP está dentro de uma dessas redes:

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

Nenhuma importação de módulo necessária e o código é muito rápido na correspondência.


A que se refere esse cached_masks ??
ajin

2

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

Aqui está o uso para 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.

Basicamente, você fornece um endereço IP como o primeiro argumento e uma lista de cidrs como o segundo argumento. Uma lista de resultados é retornada.


2
#Isso funciona corretamente sem o estranho tratamento de byte por byte
def addressInNetwork (ip, net):
    '' 'É um endereço em uma rede' ''
    # Converter endereços em ordem de host, para que as mudanças realmente façam sentido
    ip = struct.unpack ('> L', socket.inet_aton (ip)) [0]
    netaddr, bits = net.split ('/')
    netaddr = struct.unpack ('> L', socket.inet_aton (netaddr)) [0]
    # Deve deslocar para a esquerda um valor de todos, / 32 = deslocamento zero, / 0 = deslocamento 32 para a esquerda
    máscara de rede = (0xffffffff << (32-int (bits))) & 0xffffffff
    # Não há necessidade de mascarar o endereço de rede, desde que seja um endereço de rede adequado
    return (ip & netmask) == netaddr 

O código não funcionou corretamente no sistema operacional de 64 bits devido a netmaskvalores incorretos . Tomei a liberdade de consertar isso.
drdaeman

2

solução anterior tem um bug em ip & net == net. A pesquisa de ip correta é ip & netmask = net

código corrigido para bugs:

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

2

A resposta escolhida tem um bug.

A seguir está o código correto:

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

Nota: em ipaddr & netmask == netaddr & netmaskvez de ipaddr & netmask == netmask.

Eu também substituo ((2L<<int(bits)-1) - 1)por ((1L << int(bits)) - 1), pois este último parece mais compreensível.


Acho que a conversão da máscara ((2L<<int(bits)-1) - 1)está correta. por exemplo, se a máscara for 16, deve ser "255.255.0.0" ou 65535L, mas ((1L << int(bits)) - 1)obterá 32767L, o que não está certo.
Chris.Q

@ Chris.Q, ((1L << int(bits)) - 1)dá 65535L no meu sistema, com bitsdefinido para 16 !!
Debanshu Kundu

Além disso, para bitsdefinido como 0, ((2L<<int(bits)-1) - 1)está gerando erro.
Debanshu Kundu

Sim, na verdade nenhum valor diferente de / 0, / 8, / 16, / 32 está funcionando corretamente.
Debanshu Kundu

2

Aqui está uma classe que escrevi para correspondência de prefixo mais longa:

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

E aqui está um programa de teste:

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

1

Obrigado pelo seu roteiro!
Tenho trabalhado bastante nisso para fazer tudo funcionar ... Então, estou compartilhando isso aqui

  • Usar a classe netaddr é 10 vezes mais lento do que usar a conversão binária, então se você quiser usá-la em uma grande lista de IP, você deve considerar não usar a classe netaddr
  • A função makeMask não está funcionando! Funcionando apenas para / 8, / 16, / 24
    Ex:

    bits = "21"; socket.inet_ntoa (struct.pack ('= L', (2L << int (bits) -1) - 1))
    '255.255.31.0' enquanto deveria ser 255.255.248.0

    Então, usei outra função 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'
  • Outro problema é o processo de correspondência se um IP pertence a uma rede! A operação básica deve ser comparar (ipaddr e máscara de rede) e (rede e máscara de rede).
    Ex: por enquanto, a função está errada

#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!

Portanto, minha nova função addressInNetwork se parece com:


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

E agora, a resposta é certa !!


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

Espero que ajude outras pessoas, economizando tempo para elas!


1
a versão atual do código acima fornece um rastreamento na última linha, que você pode "| =" um int e uma tupla.
Sean Reifschneider

1

Com relação a todos os itens acima, acho que socket.inet_aton () retorna bytes na ordem da rede, então a maneira correta de descompactá-los é provavelmente

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

1
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


Você pode explicar o que está adicionando às respostas já fornecidas?
David Guyon

As respostas fornecidas têm algum problema que não conseguiu lidar com o CIDR. Acabei de alterar a ordem dos bytes do endereço IP. Assim:>>> struct.unpack('!L',socket.inet_aton('10.10.10.110'))[0] 168430190 >>> socket.inet_ntoa(struct.pack('!L', 168430190)) '10.10.10.110'
Johnson

1
Obrigado pela resposta. Acho que é algo que você deve acrescentar à sua resposta para esclarecer. Está no espírito do StackOverflow explicar "o quê", "por que" e, finalmente, "como". Sua resposta contém apenas o "como" :(. Eu deixo você concluir sua resposta editando-a;).
David Guyon de


0

De várias fontes acima e de minha própria pesquisa, foi assim que fiz o cálculo de sub-rede e endereço funcionar. Essas peças são suficientes para resolver a questão e outras questões 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)

0

Existe uma API chamada SubnetTree disponível em python que faz esse trabalho muito bem. Este é um exemplo simples:

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

Este é o link


0

Aqui está meu código

# -*- 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')

0

Se você não quiser importar outros módulos, você pode ir com:

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]

0

Eu tentei um subconjunto de soluções propostas nessas respostas ... sem sucesso, finalmente adaptei e fixei o código proposto e escrevi minha função fixa.

Eu testei e funciona pelo menos em arquiteturas little endian - egx86 - se alguém gosta de experimentar uma arquitetura big endian, por favor me dê um feedback.

IP2Intcódigo vem desta postagem , o outro método é totalmente (para meus casos de teste) uma correção de trabalho de propostas anteriores nesta questão.

O 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

Espero que seja útil,


0

Aqui está a solução usando o pacote netaddr

from netaddr import IPNetwork, IPAddress


def network_has_ip(network, ip):

    if not isinstance(network, IPNetwork):
        raise Exception("network parameter must be {0} instance".format(IPNetwork.__name__))

    if not isinstance(ip, IPAddress):
        raise Exception("ip parameter must be {0} instance".format(IPAddress.__name__))

    return (network.cidr.ip.value & network.netmask.value) == (ip.value & network.netmask.value)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.