In this post I will introduce you to the Python standard library module ipaddress. This module is especially important for network engineers to become familiar with as we work with IP addresses on a daily basis.

The Python standard library

The Python standard library is a set of modules that come with a standard Python installation, the modules are expected to always be available and no extra library installation steps are necessary. The benefit of this is portability. If you only use standard library modules you can assume your Python script to work without any library dependency problems on your target system.

Example of modules and libraries that come with the Python standard library that you might find useful are collections, getpass, json, urllib and ipaddress.

Imports

Instead of loading all methods and libraries available when you run your Python program you use the import statement to only load what you need. This is to reduce overhead. You can also do module specific imports using the from statement.

>>> from keyword import kwlist

Here we did an import of the kwlist object from the keyword module. The kwlist variable contains a list of keywords that are reserved by Python which you can’t use as identifiers.

>>> kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 
 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 
 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda',
 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with',
 'yield'
]
>>> len(kwlist)
35

The ipaddress module

Now that we have covered some ground, let’s dive into the ipaddress module by first importing it.

>>> import ipaddress

To read the documentation use the help() function.

>>> help(ipaddress)
Help on module ipaddress:
NAME
    ipaddress - A fast, lightweight IPv4/IPv6 manipulation library in Python.
<output omitted>

Okay so the module supports both IPv4 and IPv6 addresses, that’s great! The easiest way to get started is to create an IP address object from a string using ipaddress.ip_address(). The ip_address class will automatically determine the IP version upon creation of the object. You can also use integers as input if you find any use-case for that.

>>> loopback1 = ipaddress.ip_address("127.0.0.1")
>>> loopback2 = ipaddress.ip_address("::1")
>>> loopback1
IPv4Address('127.0.0.1')
>>> loopback2
IPv6Address('::1')
>>> loopback1.is_loopback
True
>>> loopback2.is_loopback
True

As you can see the class created an IPv4Address object based on the input to loopback1 and an IPv6Address based on the input to loopback2. We can also see that the class was able to identify these addresses as loopback upon creation.

Notice that the IPv6 address is written in an abbreviated form, we can also use the exploded and compressed attributes to expand/compress an IPv6 addresses. I must say that I think “exploded” is a bad name for the attribute, maybe it makes it easier to remember.

>>> loopback2.exploded
'0000:0000:0000:0000:0000:0000:0000:0001'

Working with networks

Let’s create an IPv4 network based on a string with slash notation. We’ll use the ip_network class this time to accomplish this.

>>> p2p_network = ipaddress.ip_network("10.177.1.0/30")
>>> p2p_network
IPv4Network('192.168.1.0/30')

We can also easily test if this is an RFC 1918 address.

>>> if p2p_network.is_private:
...     print("We need to NAT")
... else:
...     print("The address is public")
...
We need to NAT

Which host addresses are available for assignment? Remeber the first and the last address is reserved and cannot be assigned to a host. Here we create a list of addresses which can be assigned hosts.

>>> free_to_use = []
>>> for host_address in p2p_network.hosts():
...     free_to_use.append(host_address)
...
>>> free_to_use
[IPv4Address('10.177.1.1'), IPv4Address('10.177.1.2')]

To find the broadcast address of a network we can look into the broadcast_address attribute.

>>> network = ipaddress.ip_network("192.168.1.0/27")
>>> network.broadcast_address
IPv4Address('192.168.1.31')

Cisco uses wildcard masks for ACLs. How do we convert a set of networks to use the wildcard syntax? It’s actually pretty simple.


>>> acl_list = [("permit", "10.0.0.0/8"),
...             ("permit", "172.16.0.0/12"), 
...             ("permit", "192.168.0.0/16"),
...             ("deny", "0.0.0.0/0")]
>>> for row in acl_list:
...     net = ipaddress.ip_network(row[1])
...     net, wildcard = net.with_hostmask.split("/")
...     print(f"access-list 1 {row[0]} {net} {wildcard}")
...
access-list 1 permit 10.0.0.0 0.255.255.255
access-list 1 permit 172.16.0.0 0.15.255.255
access-list 1 permit 192.168.0.0 0.0.255.255
access-list 1 deny 0.0.0.0 255.255.255.255

Here we begin by creating an ACL list containing tuples where the first item in each tuple is a permit or a deny string and the second item is the network. The with_hostmask attribute of the IPv4Network object is what we use to solve the problem of wildcards. The attribute returns a string formatted like this ‘10.0.0.0/0.255.255.255’. That’s why I use the str.split() methods to separate the net and the wildcard mask into two variables.

Working with host addresses

In order to work with host addresses in CIDR notation you need to use the ip_interface class. Without a mask specified it defaults to a /32 mask with IPv4 and a /128 mask with IPv6.

>>> ipaddress.ip_interface("192.168.1.1")
IPv4Interface('192.168.1.1/32')
>>> ipaddress.ip_interface("192.168.1.10/24")
IPv4Interface('192.168.1.10/24')
>>> ifconfig = ipaddress.ip_interface("192.168.10.10/24")

You can also use the addition and subtraction operators on these object types.

>>> ifconfig + 2
IPv4Interface('192.168.10.12/32')
>>> ifconfig - 11
IPv4Interface('192.168.9.255/32')

Integration with other modules

Most classes and functions that requires an IP address as an argument expects a string. So if you created an IP address using the ipaddress module you should generally convert it to a string before using it in other functions and classes.

>>> net
IPv4Network('10.0.0.0/8')
>>> str(net)
'10.0.0.0/8'

Summary

I think the ipaddress module is important for network engineers to understand and have some experience with. If you want to automate things like ACL creation, routing, firewall rules etc. it will be a very useful tool for you to have in your toolbox.

The ipaddress module