Как я могу проверить, есть ли ip в сети на Python? - PullRequest
85 голосов
/ 04 мая 2009

Учитывая IP-адрес (скажем, 192.168.0.1), как мне проверить, находится ли он в сети (скажем, 192.168.0.0/24) в Python?

Существуют ли в Python общие инструменты для манипулирования IP-адресами? Такие вещи, как поиск хоста, IP-адрес для INT, сетевой адрес с маской сети для INT и так далее? Надеюсь, в стандартной библиотеке Python для 2.5.

Ответы [ 25 ]

140 голосов
/ 04 мая 2009

Мне нравится использовать netaddr для этого:

from netaddr import CIDR, IP

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

Как указал arno_v в комментариях, новая версия netaddr делает это так:

from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
    print "Yay!"
109 голосов
/ 17 июня 2009

Использование ipaddress ( в stdlib начиная с 3.3 , в PyPi для 2.6 / 2.7 ):

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

Если вы хотите таким образом оценить лот IP-адресов, вы, вероятно, захотите рассчитать предварительную маску сети, как

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

Затем для каждого адреса вычислите двоичное представление с одним из

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

Наконец, вы можете просто проверить:

in_network = (a & mask) == netw
32 голосов
/ 04 мая 2009

В этой статье показано, что вы можете сделать это с модулями socket и struct без особых усилий. Я добавил в статью немного следующее:

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)

Это выводит:

False
True

Если вы просто хотите одну функцию, которая принимает строки, она будет выглядеть так:

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
9 голосов
/ 16 декабря 2010

Этот код работает для меня на Linux x86. На самом деле я не задумывался над проблемами с бесконечностью, но я протестировал его на модуле «ipaddr», используя более 200K IP-адресов, протестированных на 8 различных сетевых строках, и результаты ipaddr такие же, как у этого кода.

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)

Пример:

>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False
8 голосов
/ 23 июля 2018

Для python3

In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
Out[64]: False
6 голосов
/ 29 апреля 2015

Я не фанат использования модулей, когда они не нужны. Эта работа требует только простой математики, поэтому вот моя простая функция:

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

Затем использовать его:

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

Вот и все, это намного быстрее, чем решения выше с включенными модулями.

6 голосов
/ 10 мая 2012

Я попробовал решение Дейва Уэбба, но столкнулся с некоторыми проблемами:

Самое главное - соответствие должно быть проверено путем ИД IP-адреса с маской, а затем проверки результата, точно совпадающего с Сетевым адресом. НЕ ИЛИ IP-адрес с Сетевым адресом, как было сделано.

Я также заметил, что просто игнорируя поведение Endian, предполагая, что согласованность спасет вас, вы будете работать только для масок на границах октетов (/ 24, / 16). Для того, чтобы другие маски (/ 23, / 21) работали правильно, я добавил «больше чем» в команды struct и изменил код для создания двоичной маски, чтобы он начинался со всех «1» и сдвигался влево на (32-mask ).

Наконец, я добавил простую проверку, что сетевой адрес действителен для маски, и просто напечатал предупреждение, если это не так.

Вот результат:

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
5 голосов
/ 06 июня 2015

Принятый ответ не работает ... что меня злит. Маска работает в обратном направлении и не работает с битами, которые не являются простым 8-битным блоком (например, / 24). Я адаптировал ответ, и он прекрасно работает.

    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

здесь есть функция, которая возвращает пунктирную двоичную строку, чтобы помочь визуализировать маскирование .. вроде как ipcalc output.

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

например:

screen shot of python

4 голосов
/ 29 марта 2011

Код Марка почти правильный. Полная версия кода -

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

Очевидно, из тех же источников, что и выше ...

Очень важное замечание: у первого кода есть небольшая ошибка - IP-адрес 255.255.255.255 также отображается как действительный IP-адрес для любой подсети. У меня было много времени, чтобы заставить этот код работать, и спасибо Марку за правильный ответ.

4 голосов
/ 26 сентября 2013

Нет в стандартной библиотеке для 2.5, но ipaddr делает это очень просто. Я считаю, что это в 3.3 под названием 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)
...