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

140

如何在Python中检查IP地址(比如192.168.0.1)是否属于某个网络(比如192.168.0.0/24)?

在Python中是否有一些常规工具用于IP地址的操作,例如主机查找、将IP地址转换为整数、将带有子网掩码的网络地址转换成整数等等?希望这些工具能够在Python 2.5标准库中找到。


这个问题似乎是一个不错的经典问题,适用于非常旧的2.x版本的答案,但对于3.x版本已经过时。请参见如何组织和分配“Python/pandas比较IP地址/CIDR”的经典问题? - smci
@smci,我不明白为什么;phihag在https://dev59.com/8HRA5IYBdhLWcg3wzhRY#1004527上的回答对于Python 3来说是一个完美的答案,并且自2014年以来一直存在。我已经撤销了您使该答案无效的编辑。 - Mark Amery
@Staale - 你应该更新你的答案,以一个没有重大漏洞的答案。其他答案使用内置库来完成相同的事情,只需要1/10的代码,而且没有任何漏洞。 - Addison
31个回答

1

我不知道标准库中是否有相关内容,但是PySubnetTree是一个Python库,可以进行子网匹配。


链接已更改。可以在GitHub找到PySubnetTree。 - Mayank Agarwal

1
关于以上所有内容,我认为socket.inet_aton()返回的是网络字节顺序的字节,因此正确的解包方式可能是:
struct.unpack('!L', ... )

0

有一个名为SubnetTree的API可用于Python,非常适合完成此任务。 这是一个简单的示例:

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

这是链接


0

如果您不想导入其他模块,可以选择:

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
为了避免内置或第三方模块随着时间的推移而改变其语法,我创建了自己的模块来实现这一点。我将其作为可导入的模块使用。希望这能帮助到某些人:

def subnet_lookup(subnet: str, netmask: str, ip_address: str):
    """
    :param subnet: subnet to test against (as string)
    :param netmask: mask of subnet
    :param ip_address: ip to test against subnet and mask

    :return True if a match; False if not a match

    Steps:

    1) convert entire subnet into one binary word
    2) convert entire mask into one binary word
    3) determine bcast from comparing subnet and mask
    4) convert entire ip_address into one binary word
    5) convert entire subnet into decimal
    6) convert entire bcast into decimal
    7) convert entire ip_address into decimal
    8) determine if ip_address falls between subnet and bcast using range(); returns True if yes, False if no
    """

    def convert_whole_to_bin(whole):
        ip_dec_list = whole.split(".")
        ip_bin_str = ""

        for ip in ip_dec_list:
            binary = dec_to_bin(int(ip))
            ip_bin_str += binary

        return ip_bin_str

    def dec_to_bin(decimal_octet: int):
        binary = bin(decimal_octet).replace("0b", "")

        return binary.rjust(8, '0')

    def split_binary_into_list(binary_octet: str):
        bin_list = []
        for s in binary_octet:
            bin_list.append(s)

        return bin_list

    def determine_bcast(subnet, netmask):
        subnet_split = split_binary_into_list(subnet)
        netmask_split = split_binary_into_list(netmask)
        bcast_list = []

        for subnet, mask in zip(subnet_split, netmask_split):
            if mask != '0':
                bcast_list.append(subnet)

            else:
                bcast_list.append('1')

        bcast_bin = "".join(bcast_list)

        return bcast_bin

    def bin_to_dec(binary_single_word: str):
        decimal = int(binary_single_word, 2)

        return decimal

    def subnet_lookup(ip_address, subnet, bcast):

        return ip_address in range(subnet, bcast + 1)

    # 1) convert entire subnet into one binary word
    subnet_single_bin = convert_whole_to_bin(whole=subnet)

    # 2) convert entire mask into one binary word
    mask_single_bin = convert_whole_to_bin(whole=netmask)

    # 3) determine bcast from comparing subnet and mask
    bcast_single_bin = determine_bcast(subnet=subnet_single_bin, netmask=mask_single_bin)

    # 4) convert entire ip_address into one binary word
    ip_address_single_bin = convert_whole_to_bin(whole=ip_address)

    # 5) convert entire subnet into decimal
    subnet_single_dec = bin_to_dec(binary_single_word=subnet_single_bin)

    # 6) convert entire bcast into decimal
    bcast_single_dec = bin_to_dec(binary_single_word=bcast_single_bin)

    # 7) convert entire ip_address into decimal
    ip_address_single_dec = bin_to_dec(binary_single_word=ip_address_single_bin)

    # 8) determine if ip_address falls between subnet and bcast; returns True if yes, False if no
    lookup_result = subnet_lookup(ip_address=ip_address_single_dec, subnet=subnet_single_dec, bcast=bcast_single_dec)

    return lookup_result


# Testing:

subnet = "172.16.0.0"
netmask = "255.255.0.0"
ip_address = "172.16.255.255"

result = subnet_lookup(subnet=subnet, netmask=netmask, ip_address=ip_address)

print(result)

0

我尝试了这些答案中的一个子集...但没有成功,最终我改编并修复了提出的代码,并编写了我的修复函数。

我测试过它,在小端架构上至少可以工作--例如x86--如果有人想在大端架构上尝试,请给我反馈。

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

希望有用,


0

从上述各种来源以及我自己的研究中,这就是我得出子网和地址计算的方法。这些要素足以解决问题和其他相关问题。

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

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

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

0
此函数用于检查IP地址是否属于私有IP子网或公共子网域。
def is_private_ip(ip_address_as_str):
'''Takes String IP Address without Cider as input 
    Returns True if the IP Address falls in Private subnet
    Returns False if IP Address is public
'''
    class_a=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('10.0.0.0/8')
    class_b=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('172.16.0.0/12')
    class_c=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('192.168.0.0/16')
    class_local_loop=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('127.0.0.0/8')
    class_apipa=ipaddress.ip_address(ip_address_as_str) in ipaddress.ip_network('169.254.0.0/16')
    return class_a|class_b|class_c|class_local_loop|class_apipa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接