pythonroutesipsubnet

If I have a starting and an ending IP, how can I find the subnet?


I need to block some IPs from some countries but I'm only given the starting IP and the ending IP like this:

11.22.33.44 - 11.22.35.200

I'd like to calculate the subnet to have it like this:

(not accurate)

11.22.33.44/14

How can I determine the subnet given an IP address range in Python?


Solution

  • I haven't spent much time networking recently, but here's how I think you can do this most efficiently.

    First of all, it's important to recognize that not all IP ranges can be represented as a single subnet. Let's take a look at a common subnet like

    192.168.0.0/24
    

    The /24 indicates that the first 24 bits of 192.168.0.0 give the network prefix, in this case 192.168.0 and the remaining bits are used for host addressing. You get a range from 192.168.0.0 to 192.168.0.255.

    ipcalc can help us out here. Let's see if it agrees:

    $ ipcalc 192.168.0.0 - 192.168.0.255
    deaggregate 192.168.0.0 - 192.168.0.255
    192.168.0.0/24
    

    So far so good. But what if you want to exclude the .0 or the .255, which is used for broadcast? There isn't a single subnet that represents that range:

    $ ipcalc 192.168.0.0 - 192.168.0.254
    deaggregate 192.168.0.0 - 192.168.0.254
    192.168.0.0/25
    192.168.0.128/26
    192.168.0.192/27
    192.168.0.224/28
    192.168.0.240/29
    192.168.0.248/30
    192.168.0.252/31
    192.168.0.254/32
    

    Subnets grow by powers of two and when our range doesn't slot into that framework cleanly we end up with things like this.

    The example you gave, incidentally, doesn't give a single subnet:

    $ ipcalc 11.22.33.44 - 11.22.35.200
    deaggregate 11.22.33.44 - 11.22.35.200
    11.22.33.44/30
    11.22.33.48/28
    11.22.33.64/26
    11.22.33.128/25
    11.22.34.0/24
    11.22.35.0/25
    11.22.35.128/26
    11.22.35.192/29
    11.22.35.200/32
    

    Python has an ipaddress module. Let's see if its summarize_address_range() function agrees with ipcalc (formatted for readability):

    >>> import ipaddress
    
    >>> list(ipaddress.summarize_address_range(
            ipaddress.IPv4Address('192.168.0.0'),
            ipaddress.IPv4Address('192.168.0.255')))
    
    [IPv4Network('192.168.0.0/24')]
    

    So far so good. How about the second example?

    >>> list(ipaddress.summarize_address_range(
            ipaddress.IPv4Address('11.22.33.44'),
            ipaddress.IPv4Address('11.22.35.200')))
    
    [IPv4Network('11.22.33.44/30'),
     IPv4Network('11.22.33.48/28'),
     IPv4Network('11.22.33.64/26'),
     IPv4Network('11.22.33.128/25'),
     IPv4Network('11.22.34.0/24'),
     IPv4Network('11.22.35.0/25'),
     IPv4Network('11.22.35.128/26'),
     IPv4Network('11.22.35.192/29'),
     IPv4Network('11.22.35.200/32')]
    

    Looks like a match to me.