Ahmad
Ahmad

Reputation: 1845

How ipaddress module in Python handles IPs

I am asked to do an App to gives the total possible hosts for a subnet, Sadly I cannot use ipaddress module provided from Python so I need to write my own methods to check and validate entered data
My Code is like this:

def IP_Validation(chk_IP_vaild):
    if chk_IP_vaild.count(".") == 3 and all(isIP(i) for i in chk_IP_vaild.split(".")):
        return True
    return False
        
def isIP(IP_String):
    try:
        return str(int(IP_String)) == IP_String and 0 <= int(IP_String) <= 255
    except:
        return False

def Calc_Networks(IP_One , IP_Two):
    if IP_Validation(IP_One) and IP_Validation(IP_Two):
        print (IP_One,IP_Two)
    else:
        print ("There is an error")


Calc_Networks("172.11.254.1", "172.1.254.")
Calc_Networks("172.11.254.1", "172.1.254.0")

Output

There is an error
172.11.254.1 172.1.254.0

but this only checks if entered IPs are IPv4, so I was googling and checking solutions over the Internet and I came across this:

import ipaddress
def calc_inclusive_subnet(ip1, ip2): #accepts 2 IP strings
    #make IP Address objects
    ip1_obj=ipaddress.IPv4Address(ip1)
    ip2_obj=ipaddress.IPv4Address(ip2)

    if ip1_obj<=ip2_obj:
        min_ip=ip1_obj
        max_ip=ip2_obj
    else:
        min_ip=ip2_obj
        max_ip=ip1_obj   
    distance = int(max_ip)-int(min_ip) #**
    return distance

print (calc_inclusive_subnet("192.168.1.0", "192.168.2.255"))

Output

511

What I noticed that If I did a subtraction to two IPs declared with ipaddress module it does it right with no problem so I went to the Docs in order to understand how this module handles IPs so I can use a similar method


Doing the subtraction using code 1 it gives the following error:

def Calc_Networks(IP_One , IP_Two):
    if IP_Validation(IP_One) and IP_Validation(IP_Two):
        dis = int(IP_Two) - int(IP_One)
        return dis

The Output is:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-154-9e53188ce6f1> in <module>
     16 
     17 
---> 18 print(Calc_Networks("172.11.254.1", "172.1.254.1"))
     19 Calc_Networks("172.11.254.1", "172.1.254.")

<ipython-input-154-9e53188ce6f1> in Calc_Networks(IP_One, IP_Two)
     12 def Calc_Networks(IP_One , IP_Two):
     13     if IP_Validation(IP_One) and IP_Validation(IP_Two):
---> 14         dis = int(IP_Two) - int(IP_One)
     15         return dis
     16 

ValueError: invalid literal for int() with base 10: '172.1.254.1'

also these posts 1 & 2 could not help me!

Thanks in advance

Upvotes: 1

Views: 95

Answers (1)

xavc
xavc

Reputation: 1295

The reason that your Calc_Networks code didn't work but the calc_inclusive_subnet did is that in calc_inclusive_subnet an ipaddress.IPv4Address was passed to int, which has an __int__ method, and gives you what you need. Your code, however, simply passes the string representation to int, which tries to parse the IP address as a normal int, which "172.11.254.1" isn't.

If you don't want to use ipaddress you can still implement a function to parse the IP address into a single integer:

def parse_ip_to_int(ip_address):
    octets = ip_address.split('.')
    return int.from_bytes(map(int, octets), 'big')

The above function splits ip_address into a list of strings, each string corresponding to one octet of the address (so for "172.11.254.1", ["172", "11", "254", "1"]). We then use map to convert those strings into ints. Finally we use the useful int.from_bytes, which allows us to convert that iterable of octets into one big integer. This could alternatively by achieved with a loop and bit shifting left by 8. The second argument of int.from_bytes, 'big', indicates that the most significant octet is the first one, which is how IPv4 addresses are encoded.

From here we can now correctly implement your Calc_Networks function. I have simplified it by using abs to ensure the distance we obtain is positive, rather than finding the minimum and maximum like in the original calc_inclusive_subnet.

def Calc_Networks(IP_One, IP_Two):
    if IP_Validation(IP_One) and IP_Validation(IP_Two):
        return abs(parse_ip_to_int(IP_One) - parse_ip_to_int(IP_Two))

Similar to your initial implementation, if the validation fails, then None is returned. A final note, although what I've presented does produce the same results as the calc_inclusive_subnet you've shown, all that function did was count the number of addresses from a starting address up to, but not including, an end address. Your desired behaviour may be different.

Upvotes: 1

Related Questions