Search code examples
pythonip-address

Search IP /24 network in dict of ISPs


I have an IP Address which I would like to replace after the 3rd dot. e.g. IP

156.13.216.251

Output:

156.13.216.0

With this output I would like like to be able to search a dictionary to match the result:

IPList =

[
'156.13.216.251'
'156.13.216.250'
]

Dict:

 {
            "techCountry": "ISP",
            "techCity": "saint-petersburg",
            "techName": "Cahit Eyigunlu",
            "adminState": "pr.",
            "registrantPostalCode": "e16 1ah",
            "telephone": "+443333031000",
            "domain": "156.13.216.0"
}

As the output matches I would like to then append this to a new dict with the matched result.

I have attempted to split it on the first three with the following but it appears to split it all:

for i in IPList:
    i.split(".",3)[:3]

Solution

  • Python has a library named ipaddress.

    You may easily use it like so:

    import ipaddress
    
    ip_list = [
    '156.13.216.251',
    '156.13.216.250'
    ]
    
    isps = [
         {
            "techCountry": "ISP",
            "techCity": "saint-petersburg",
            "techName": "Cahit Eyigunlu",
            "adminState": "pr.",
            "registrantPostalCode": "e16 1ah",
            "telephone": "+443333031000",
            "domain": "156.13.216.0"
        }
    ]
    
    # First, I suggest to map the networks to ISPs, otherwise it'll be an O(n) operation
    # for every IP.
    # We add the /24 to signal a network.
    network_to_isp = {isp["domain"] + "/24": isp for isp in isps}
    
    
    ips_not_found = []
    ips_to_isps = {}
    
    for ip in ip_list:
        network = ipaddress.IPv4Network((ip, 24), strict=False)
        isp = network_to_isp.get(str(network))
        if isp is None:
            ips_not_found.append(ip)
        else:
            ips_to_isps[ip] = isp
    

    Assuming we don't know the network and wish to search, the following code will work.

    Keep in mind it might make up to 32 searches per IPv4. Since we don't know the network that the IP exists in. We have no way to circumvent it.

    import ipaddress
    
    ip_list = [
    '156.13.216.251',
    '156.13.216.250'
    ]
    
    isps = [
         {
            "techCountry": "ISP",
            "techCity": "saint-petersburg",
            "techName": "Cahit Eyigunlu",
            "adminState": "pr.",
            "registrantPostalCode": "e16 1ah",
            "telephone": "+443333031000",
            "domain": "156.13.216.0"
        }
    ]
    
    # First, I suggest to map the networks to ISPs, otherwise it'll be an O(n) operation
    # for every IP.
    # We assume the network is unknown.
    network_to_isp = {ipaddress.ip_address(isp["domain"]): isp for isp in isps}
    
    def iter_supernets(address, *, max_mask=None, min_mask=0):
        network = ipaddress.ip_network(address)
        return (network.supernet(new_prefix=mask)
                for mask in range(max_mask or network.prefixlen, min_mask, -1))
    
    ips_to_isps = {}
    
    for ip in ip_list:
        for network in iter_supernets(ip):
            isp = network_to_isp.get(network.network_address)
            if isp:
                ips_to_isps[ip] = isp
                break
        else:
            raise RuntimeError("No ISP found for {}".format(ip))