I’ve begun my introduction to the world of Python coding a couple of weeks ago. This post is about an IPv6 address shortener and expander code that I’ve jumped into to learn the basic knobs in Python.
An IPv6 address is 128 bit addressing scheme written in the following form:
There are 8 groups of X delimited by colon (:) Let’s just call each group of 4 Xs quartets.
Each X above represents a hexadecimal number with valid values between 0 and F.
First start by explicitly stating the rules to shorten and/or expand IPv6. There are two major rules to shorten an IPv6 address.
- All leading 0s in a quartet can be dropped.
For example, 0001 becomes 1, 00ac becomes ac, 0de4 becomes de4. FF02:0000:0000:0000:0000:0000:0000:0002 can be shortened to FF02:0:0:0:0:0:0:2
If the zeros are not leading, they can’t be dropped.
For example, 1000 can’t be shortened any further. Same for a200 and so on.
2. After leading 0s have been dropped, if there are two or more consecutive quartet place holders with value 0, they can be replaced once by a double colon (::) But this double colon replacement can be made only once.
FF02:0000:0000:0000:0000:0000:0000:0002 can be shortened first as FF02:0:0:0:0:0:0:2. Since we have 6 successive quartet place holders with value 0, we can replace all of them with a double colon as FF02::2. This is the shortest form of the given IPv6 address.
If the initial IPv6 address is FF02:0000:0000:0002:0000:0000:0000:0003, leading 0s can be dropped to shorten it to FF02:0:0:2:0:0:0:3. In this case we have two choices of consecutive 0s that can be replaced with a double colon. We can choose only one. Therefore, either FF02::2:0:0:0:3 or FF02:0:0:2::3 will be valid shortest forms for the IPv6 address initially provided. Doing something like FF02::2::3 is criminal 🙂
With these rules in mind, now we can briefly list out some requirements for the code we are about to write. If you like, you can write these requirements in the phrasing specified in RFC 2119 (https://www.ietf.org/rfc/rfc2119.txt)
Brief list of basic requirements
- Code must prompt user to choose from option of shortening or expanding an IPv6 address.
- Code must accept IPv6 input from user.
- If user enters an invalid IPv6 address, code must prompt user to re-enter a valid IPv6 address.
- Validity of each value in the IPv6 address must be tested. Valid hex values are from 0 to F. Anything outside of hex values from 0 to F must trigger error and prompt the user to re-enter a valid IPv6 address.
- Code must detect colon (:) positions in the IPv6 address entered by user. If user enters colon at an invalid position, code must detect and prompt user re-enter a valid IPv6 address.
- If user enters double colons (::) twice, code must return an error message and prompt user to re-enter a valid IPv6 address.
I’ll define three functions besides a main() function:
- ipv6_validator: This function will check validity of IPv6 address entered by user
- ipv6_shortener: This function will shorten a valid IPv6 address
- ipv6_expander: This function will expand a valid IPv6 address
The IPv6 validator functions checks against the basic requirements listed above to ensure the IPv6 address entered by the user is valid. As you can see in the bottom part of this post in the main() function, before any shortening or expansion operation is performed, this validity check is invoked through the ipv6_validator() function.
def ipv6_validator(ipv6_entered): if ipv6_entered.count('::') > 1: print '\nYou entered an invalid IPv6 address. Please try again.\n' b = ipv6_entered.split(':') if len(b) > 8: print '\nYou entered an invalid IPv6 address. Please try again.\n' for i in range(len(b)): c = list(b[i]) #There most likely is a much better way than to list out the valid values d = ['0','1','2','3','4','5','6','7','8','9','a','A','b','B','c','C','d','D','e','E','f','F'] for j in range(len(c)): if c[j] not in d: print '\nYou entered an invalid IPv6 address. Please try again.\n'
A very interesting part of the ipv6_shortener() function that I ran in to is it’s demand for dynamic variable value in a loop because elements of the list may be deleted at each iteration. That affects the length of the remaining list variable; which in turn affects the amount of iterations needed as the loop comes back to execute next. For this purpose, I’ve used a while loop with a dynamic num_of_loops variable.
def ipv6_shortener(ipv6_entered): b = ipv6_entered.split(':') for i in range(len(b)): c = list(b[i]) num_of_loops = len(c)-1 j = 0 while j < num_of_loops: if c[j]=='0': del c[j] num_of_loops = len(c) - 1 else: break b[i]=''.join(c) print '\nShort form of your IPv6 address is =', ':'.join(b),'\n'
The other function is ipv6_expander() In this function, I’ve used padding of 0 values to replace a double colon (::) from a shorter form of an IPv6 address. Since the number of times a double colon (::) can show up in a valid IPv6 address is only once, the number of padding 0s can be thought of as a function of the number of colons(:)
If there are 2 colons in the IPv6 address, padding 6 0s is necessary. If 3, padding 5 0s and so on. I’ve to say there is one unhandled exception I’ve left in this stanza of code. i.e. If the number of elements in a quartet is exactly 4, using a string padding approach doesn’t cut it.
def ipv6_expander(ipv6_entered): if '::' in ipv6_entered: num_of_0_padding = 8-ipv6_entered.count(':') new_ipv6 = ipv6_entered.replace(('::'),':0'*num_of_0_padding) print '\nExpanded form of your IPv6 address is =',new_ipv6,'\n' else: b = ipv6_entered.split(':') for i in range(len(b)): c = list(b[i]) for j in range(4-len(c)): c.insert(j,'0') b[i]=''.join(c) print '\nExpanded form of your IPv6 address is =', ':'.join(b),'\n'
The main() function is where I’ve prompted the user to interact with the code. Respective function calls are made to the appropriate function depending on the user’s choice of operation.
def main(): while True: print "\nWhat operation would you like to perform?\n\t1. Shorten an IPv6 address\n\t2. Expand an IPv6 address\n\t3. Exit" try: choice = int(raw_input('Select an option: ')) except ValueError: print "\nYour choice of operation is incorrect. Please select 1, 2 or 3." continue if choice==1: ipv6_entered = raw_input('\nPlease enter an IPv6 address: ') ipv6_validator(ipv6_entered) ipv6_shortener(ipv6_entered) elif choice==2: ipv6_entered = raw_input('\nPlease enter an IPv6 address: ') ipv6_validator(ipv6_entered) ipv6_expander(ipv6_entered) elif choice==3: exit() else: print "\nPLEASE ENTER A VALID CHOICE TO TRY AGAIN.\n" if __name__ == '__main__': main()
While I’m certain plenty of improvements can be made to this code and there are a million other ways to do the same thing, I’m unlikely to dwell on it for now. I’ve grabbed the fundamental lessons I was aiming to get out of the process of writing it; such as:
- the immutability of strings
- working with list variable types in Python
- when to prefer while loops instead of for loops
- how Python treats local and global variables
- how to handle different exceptions to adapt messaging to variable user inputs and so on.
This is my first relatively long Python code. The process has made me aware of the influence from my distant past (i.e. good ol’ college days) of dabbling with C++. Hopefully, as I proceed, I’ll learn more Pythonic ways of writing to achieve the same end.
Going forward, I’ll lean towards coding aimed at manipulating router and switch outputs and practical scripts that could help automate grunt work day-to-day type tasks. Let me know if you have similar ideas if you’re working on any.