代码之家  ›  专栏  ›  技术社区  ›  Staale

如何在python中检查IP是否在网络中?

  •  81
  • Staale  · 技术社区  · 16 年前

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

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

    25 回复  |  直到 6 年前
        1
  •  31
  •   David Webb    16 年前

    This article 显示你可以用 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
    
        2
  •  133
  •   nosklo    12 年前

    我喜欢使用 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!"
    
        3
  •  101
  •   phihag    10 年前

    使用 ipaddress ( in the stdlib since 3.3 , at 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
    

    如果你想评估 许多 通过这种方式,您可能需要预先计算网络掩码,比如

    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
    
        4
  •  9
  •   Sean Reifschneider    14 年前

    这段代码在Linux x86上对我有效。我并没有真正考虑到endianes的问题,但是我已经用超过200K的IP地址对“ipaddr”模块进行了测试,对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
    
        5
  •  6
  •   Paul McDonnell    13 年前

    我尝试了戴夫·韦伯的解决方案,但遇到了一些问题:

    最基本的-匹配应该通过将IP地址与掩码进行anding来检查,然后检查结果是否与网络地址完全匹配。没有像以前那样用网络地址来填充IP地址。

    我还注意到,如果忽略了endian行为,假设一致性可以保存,那么只对八位字节边界上的掩码有效(/24,/16)。为了使其他掩码(/23,/21)正常工作,我在结构命令中添加了一个“greater than”,并将用于创建二进制掩码的代码更改为从所有“1”开始并向左移动(32个掩码)。

    最后,我添加了一个简单的检查,确认网络地址对掩码有效,如果不有效,只打印一个警告。

    结果如下:

    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
  •  6
  •   Landon    10 年前

    我不喜欢在不需要模块的时候使用它们。这项工作只需要简单的数学,所以我的简单功能是:

    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) 
    

    就是这样,这比上面包含模块的解决方案快得多。

        7
  •  6
  •   petertc    7 年前

    对于Python 3

    In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
    Out[64]: False
    
        8
  •  5
  •   jorb    10 年前

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

        9
  •  4
  •   James Robinson    11 年前

    在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)
    
        10
  •  4
  •   Nitin Khanna    10 年前

    马克的密码几乎是正确的。代码的完整版本是-

    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。我花了不少时间让这段代码正常工作,感谢马克给出了正确的答案。

        11
  •  3
  •   gstein    7 年前

    依赖于“struct”模块可能会导致endian ness和类型大小出现问题,而不需要这样做。socket.inet也不是。python非常适合点分四路IP地址:

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

    我需要在每个socket 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)
    

    不需要导入模块,代码为 非常 匹配速度快。

        12
  •  2
  •   user221014    13 年前

    从netaddr导入所有匹配的目录

    >>> 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地址作为第一个参数,并提供一个CIDR列表作为第二个参数。返回点击列表。

        13
  •  2
  •   drdaeman    13 年前
    #这在没有奇怪的逐字节处理的情况下工作正常
    def addressinnetwork(IP,网络):
    ''是网络中的地址'''
    #将地址转换为主机顺序,这样移位实际上是有意义的。
    ip=struct.unpack('>l',socket.inet_aton(ip))[0]
    netaddr,bits=net.split('/')
    netaddr=struct.unpack('>l',socket.inet_aton(netaddr))[0]
    #必须左移所有一个值,/32=零移位,/0=32左移
    网络掩码=(0xffffffff&lt&lt(32 int(bits)))amp;0xffffffff
    #只要网络地址正确,就不需要屏蔽网络地址。
    返回(IP&netmask)==netaddr
    
        14
  •  2
  •   Debanshu Kundu    11 年前

    选择的答案有一个错误。

    以下是正确的代码:

    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) 因为后者似乎更容易理解。

        15
  •  2
  •   Amir    8 年前

    以下是我为最长前缀匹配编写的类:

    #!/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
    
        16
  •  1
  •   Marc Leurent    15 年前

    谢谢你的剧本!
    我做了很长时间的工作,使一切都能工作…所以我在这里分享

    
    #!/usr/bin/python
    >>> calcDottedNetmask(21)
    >>> '255.255.248.0'
    
    • 另一个问题是如果一个IP属于一个网络,那么匹配的过程就是这样的!基本操作应该是比较(ipaddr&netmask)和(network&netmask)。
      例:目前功能有问题
    
    #!/usr/bin/python
    >>> addressInNetwork('188.104.8.64','172.16.0.0/12')
    >>>True which is completely WRONG!!
    

    所以我的新地址网络函数如下:

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

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

        17
  •  1
  •   Quentin Stafford-Fraser    12 年前

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

    struct.unpack('!L', ... )
    
        18
  •  1
  •   user3265849    11 年前

    以前的解决方案在ip&net==net中有一个错误。正确的IP查找是ip&netmask=net

    固定码:

    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");
    
        19
  •  1
  •   salaheddine    9 年前

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

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

    This is the link

        20
  •  1
  •   Ajean Vishal Agrawal    9 年前
    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版


        21
  •  1
  •   RichieHindle    7 年前

    我对标准库一无所知,但是 PySubnetTree 是将进行子网匹配的python库。

        22
  •  0
  •   Nathan Garabedian    10 年前

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

    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)
    
        23
  •  0
  •   ruohanc    8 年前

    这是我的密码

    # -*- 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')
    
        24
  •  0
  •   Benny93    7 年前

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

    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]
    
        25
  •  0
  •   Fabiano Tarlao    7 年前

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

    我测试过它,并且至少在小endian架构上工作——例如x86——如果有人喜欢尝试大endian架构,请给我反馈。

    IP2Int 代码来自 this post 另一种方法是完全(对于我的测试用例)修复这个问题中以前的建议。

    代码:

    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
    

    希望有用,

    推荐文章