给定两个文件(IP和子网信息),创建将每个IP与子网相关联的文件

时间:2017-02-04 00:11:54

标签: python csv ip-address subnet netmask

我已经用适当的方式解决这个问题几天了,我正在寻求一些帮助。

我有两个文件,需要创建一个显示关系的第三个文件。

  1. IP地址文件 - ip.csv
  2. 子网文件 - subnet.csv
  3. 我需要指定每个IP所在的子网,并创建第三个文件

    ip.csv文件将包含大约150万个IP,而subnet.csv文件将包含大约140,000个子网。

    ip.csv文件示例:

    IP,Type
    10.78.175.167,IPv4
    10.20.3.56,IPv4
    

    subnet.csv文件示例:

    Subnet,Netmask
    10.176.122.136/30,255.255.255.252
    10.20.3.0/24,255.255.254.0
    

    我需要创建的文件格式:

    Subnet,IP
    10.20.3.0/24,10.20.3.56
    

    我试图利用这些页面中的内容:

    这是我尝试过的代码。它适用于小型设备,但我在使用完整的文件集运行时遇到问题。

    #!/usr/local/bin/python2.7
    import csv
    import ipaddress
    import iptools
    import re
    import SubnetTree
    import sys
    from socket import inet_aton
    
    testdir = '/home/test/testdir/'
    iprelfile = testdir + 'relationship.csv'
    testipsub = testdir + 'subnet.csv'
    testipaddr = testdir + 'ip.csv'
    
    o1 = open (iprelfile, "a")
    
    # Subnet file
    IPR = set()
    o1.write('Subnet,IP\n')
    with open(testipsub, 'rb') as master:
        reader = csv.reader(master)
        for row in reader:
            if 'Subnet' not in row[0]:
                # Convert string to unicode to be parsed with ipaddress module
                b = unicode(row[1])
                # Using ipaddress module to create list containing every IP in subnet
                n2 = ipaddress.ip_network(b)
                b1 = (list(n2.hosts()))
                # IP address file
                with open(testipaddr, 'rb') as ipaddy:
                    readera = csv.reader(ipaddy)
                    for rowa in readera:
                        if 'IP' not in rowa[0]:
                            bb = rowa[0]
                            for ij in b1:
                                # Convert to string for comparison
                                f = str(ij)
                                # If the IP address is in subnet range
                                if f == bb:
                                    IPR.update([row[0] + ',' + bb + '\n'])
    
    
    for ip in IPR:
        o1.write(ip + '\n')
    
    # Closing the file
    o1.close()
    

1 个答案:

答案 0 :(得分:0)

您可以将所有子网读入内存并按网络地址对其进行排序。这将允许您使用bisect进行二进制搜索,以便找到每个IP的子网。这仅适用于子网彼此不重叠的情况,如果他们这样做,则可能需要使用segment tree

import bisect
import csv
import ipaddress

def sanitize(ip):
    parts = ip.split('/', 1)
    parts[0] = '.'.join(str(int(x)) for x in parts[0].split('.'))

    return '/'.join(parts)

with open('subnet.csv') as subnet_f:
    reader = csv.reader(subnet_f)
    next(reader)    # Skip column names

    # Create list of subnets sorted by network address and
    # list of network addresses in the same order
    subnets = sorted((ipaddress.IPv4Network(sanitize(row[0])) for row in reader),
                     key=lambda x: x.network_address)
    network_addrs = [subnet.network_address for subnet in subnets]

with open('ip.csv') as ip_f, open('output.csv', 'w', newline='') as out_f:
    reader = csv.reader(ip_f)
    next(reader)

    writer = csv.writer(out_f)
    writer.writerow(['Subnet', 'IP'])

    for row in reader:
        ip = ipaddress.IPv4Address(sanitize(row[0]))
        index = bisect.bisect(network_addrs, ip) - 1

        if index < 0 or subnets[index].broadcast_address < ip:
            continue    # IP not in range of any networks
        writer.writerow([subnets[index], ip])

输出:

Subnet,IP
10.20.3.0/24,10.20.3.56

以上具有 O(n log m)的时间复杂度,其中n是IP的数量和m个网络。请注意,它只能在Python 3中运行,因为Python {2.7}中不包含ipaddress。如果您需要使用Python 2.7,则可以使用backports

更新高效解决方案的第一个目标是找到一种以有效方式处理每个IP的方法。循环遍历所有子网非常昂贵,所以它不会做。在每个子网中创建第一个IP的排序列表要好得多。对于给定的数据,它看起来像这样:

[IPv4Address('10.20.3.0'), IPv4Address('10.176.122.136')]

这将允许我们执行二进制搜索,以便找到等于或低于单个IP的IP地址索引。例如,当我们搜索IP 10.20.3.56时,我们使用bisect.bisect为我们提供大于IP的第一个索引并将其递减1:

>>> l = [IPv4Address('10.20.3.0'), IPv4Address('10.176.122.136')]
>>> index = bisect.bisect(l, IPv4Address('10.20.3.56'))
>>> index
1
>>> l[index - 1]
IPv4Address('10.20.3.0')

由于我们已将网络存储到另一个列表,该列表的顺序相同,因此我们可以使用索引来检索给定的子网。一旦我们拥有子网,我们仍然需要检查单个IP是否等于或低于子网内的最后一个IP。如果单个IP在子网内,则将行写入结果,如果不移动到下一个IP。