如何在Python中检查ip是否在网络中?

时间:2009-05-04 08:59:20

标签: python networking ip-address cidr

给定一个IP地址(比如说192.168.0.1),如何检查它是否在Python中的网络中(比如192.168.0.0/24)?

Python中是否有用于ip地址操作的通用工具?像主机查找,ip adddress到int,网络地址与netmask到int等等?希望在2.5的标准Python库中。

27 个答案:

答案 0 :(得分:139)

我喜欢使用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!"

答案 1 :(得分:109)

使用ipaddressin the stdlib since 3.3at PyPi for 2.6/2.7):

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

如果您想以这种方式评估 lot 的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

答案 2 :(得分:32)

This article显示您可以使用socketstruct模块执行此操作而无需太多额外工作。我在文章中加了一点如下:

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

答案 3 :(得分:9)

此代码适用于Linux x86。我没有真正考虑过endianess问题,但我已经针对“ipaddr”模块测试了它,使用针对8个不同网络字符串测试的超过200K IP地址,并且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

答案 4 :(得分:8)

对于python3

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

答案 5 :(得分:6)

我尝试过Dave Webb的解决方案,但遇到了一些问题:

最基本的 - 应该通过使用掩码对IP地址进行AND运算来检查匹配,然后检查结果与网络地址完全匹配。不要使用网络地址与IP地址进行AND运算。

我还注意到,只是忽略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

答案 6 :(得分:5)

接受的答案不起作用......这让我很生气。掩码是向后的,不适用于任何不是简单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输出。

    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

答案 7 :(得分:4)

Marc的代码几乎是正确的。代码的完整版本是 -

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。我有时间让这段代码工作,感谢Marc的正确答案。

答案 8 :(得分:4)

不在2.5的标准库中,但ipaddr使这很容易。我相信它名为ipaddress,名称为3.3。

import ipaddr

a = ipaddr.IPAddress('192.168.0.1')
n = ipaddr.IPNetwork('192.168.0.0/24')

#This will return True
n.Contains(a)

答案 9 :(得分:3)

依靠&#34; struct&#34;模块可能会导致字节序和类型大小的问题,并且不需要。也不是socket.inet_aton()。 Python使用点分四核IP地址非常有效:

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

我需要在每个套接字accept()调用上对一组允许的源网络进行IP匹配,所以我将掩码和网络预先计算为整数:

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

然后我可以快速查看给定IP是否属于其中一个网络:

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

不需要模块导入,匹配时代码非常

答案 10 :(得分:2)

#This works properly without the weird byte by byte handling
def addressInNetwork(ip,net):
    '''Is an address in a network'''
    # Convert addresses to host order, so shifts actually make sense
    ip = struct.unpack('>L',socket.inet_aton(ip))[0]
    netaddr,bits = net.split('/')
    netaddr = struct.unpack('>L',socket.inet_aton(netaddr))[0]
    # Must shift left an all ones value, /32 = zero shift, /0 = 32 shift left
    netmask = (0xffffffff << (32-int(bits))) & 0xffffffff
    # There's no need to mask the network address, as long as its a proper network address
    return (ip & netmask) == netaddr 

答案 11 :(得分:2)

选择的答案有一个错误。

以下是正确的代码:

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

注意:ipaddr & netmask == netaddr & netmask而不是ipaddr & netmask == netmask

我还将((2L<<int(bits)-1) - 1)替换为((1L << int(bits)) - 1),因为后者似乎更容易理解。

答案 12 :(得分:2)

这是我为最长前缀匹配编写的一个类:

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

这是一个测试程序:

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

答案 13 :(得分:2)

来自netaddr的

导入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')]

以下是此方法的用法:

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

基本上,你提供一个ip地址作为第一个参数,一个cidrs列表作为第二个参数。返回命中列表。

答案 14 :(得分: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


答案 15 :(得分:1)

之前的解决方案在ip&amp; net == net。正确的ip查找是ip&amp; netmask = net

bugfixed code:

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

答案 16 :(得分:1)

在python中有一个名为SubnetTree的API可以很好地完成这项工作。 这是一个简单的例子:

bool opt_constructed = false;
int opt_i; // not constructed

new int (&opt_i)(12); opt_constructed = true; // in-place constructed

int*p = &opt_i;

opt_i = 24;

assert(p == &opt_i);

// destuctor
if (opt_constructed) {
  // call opt_i's destructor if it has one
}

This is the link

答案 17 :(得分:1)

我不知道标准库中的任何内容,但PySubnetTree是一个将进行子网匹配的Python库。

答案 18 :(得分:1)

关于上述所有内容,我认为socket.inet_aton()以网络顺序返回字节,因此解压缩它们的正确方法可能是

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

答案 19 :(得分:1)

感谢您的脚本!
我已经做了很长时间才能使一切工作......所以我在这里分享它

  • 使用netaddr Class比使用二进制转换慢10倍,所以如果你想在一个大的IP列表中使用它,你应该考虑不使用netaddr class
  • makeMask功能不起作用!仅适用于/ 8,/ 16,/ 24
    年前:

      

    bits =“21”; socket.inet_ntoa(struct.pack('= L',(2L&lt;&lt; int(bits)-1) - 1))

      '255.255.31.0',而应该是255.255.248.0

    所以我使用了来自http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/的另一个函数calcDottedNetmask(mask) 例如:


#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
  • 另一个问题是如果IP属于网络则匹配的过程!基本操作应该是比较(ipaddr和网络掩码)和(网络和网络掩码)。
    例如:暂时,功能错误

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

所以我的新addressInNetwork函数看起来像:


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


现在,回答是对的!!


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

我希望它能帮助别人,为他们节省时间!

答案 20 :(得分:0)

从上面的各种来源,以及我自己的研究,这是我如何使子网和地址计算工作。这些部分足以解决问题和其他相关问题。

metastore_dir = collections.defaultdict(list)
with open(local_registry_file_path + data_ext_dt + "_metastore_metadata.csv",'rb') as metastore_metadata:
    for line in metastore_metadata: 
        key = line[2]
        key = key.lower().strip()
        if (key in metastore_dir):
            metastore_dir[key].append(line[0])
        else:
            metastore_dir[key] = line[0]

答案 21 :(得分:0)

这是我的代码

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

答案 22 :(得分:0)

如果您不想导入其他模块,可以使用:

Bool

答案 23 :(得分:0)

我在这些答案中尝试了一个提议的解决方案的子集..没有成功,我终于修改并修复了提议的代码并编写了我的固定函数。

我已经对其进行了测试,并且至少可以在小型字节序体系结构(例如x86)上运行,如果有人愿意尝试使用大型字节序体系结构,请给我反馈。

http://demo.com/media代码来自this post,另一种方法是此问题中以前的建议的完全(对于我的测试用例而言)工作解决方案。

代码:

IP2Int

希望有用

答案 24 :(得分:0)

使用Python3 ipaddress

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

说明

您可以将 IP地址视为具有最大可能的网络掩码(IPv4为/32,IPv6为/128)的网络 / p>

检查 192.168.0.1 是否在 192.168.0.0/16 中与检查 192.168.0.1/32 是否为 192.168.0.0/16

的子网

答案 25 :(得分:0)

这是使用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)

答案 26 :(得分:0)

As of Python 3.7,您可以使用 subnet_ofsupernet_of 辅助方法,它们是标准库的一部分:

要仅针对单个 IP 进行测试,您可以仅使用子网掩码 /32,这意味着“仅此 IP 地址”作为子网,或者您可以将 IP 地址传递给 IPv4Nework 或 { {1}} 构造函数,它们将为您返回一个子网值。

例如:

IPv6Nework
<块引用>

Python 中是否有用于 ip 地址操作的通用工具?东西 像主机查找,IP 地址到 int,网络地址和网络掩码到 int 等等?希望在 2.5 的标准 Python 库中。

在 Python 3 中,有 ipaddress 模块,其中包含用于 IPv4 和 IPv6 操作的工具。您可以通过强制转换将它们转换为 int,即 from ipaddress import IPv4Network, IPv4Address # Store IP Address as variable >>> myip = IPv4Address('192.168.0.1') >>> myip IPv4Address('192.168.0.1') # This treats the IP as a subnet >>> myip_subnet = IPv4Network(myip) >>> myip_subnet IPv4Network('192.168.0.1/32') # The other subnet to test membership against >>> other_subnet = IPv4Network('192.168.0.0/24') >>> other_subnet IPv4Network('192.168.0.0/24') # Now we can test >>> myip_subnet.subnet_of(other_subnet) True int(IPv4Address('192.168.0.1')) 模块中有许多其他有用的函数,用于主机等。