In this step-by-step tutorial, you’ll learn how to inspect and manipulate IP addresses as Python objects with Python’s ipaddress module, improving your understanding of IP address mechanics and patterns used by the module.

Python’s ipaddress module is an underappreciated gem from the Python standard library. You don’t have to be a full-blown network engineer to have been exposed to IP addresses in the wild. IP addresses and networks are ubiquitous in software development and infrastructure. They underpin how computers, well, address each other.

Learning through doing is an effective way to master IP addresses. The ipaddress module allows you to do just that by viewing and manipulating IP addresses as Python objects. In this tutorial, you’ll get a better grasp of IP addresses by using some of the features of Python’s ipaddress module.

In this tutorial, you’ll learn:

  • How IP addresses work, both in theory and in Python code
  • How IP networks represent groups of IP addresses and how you can inspect relationships between the two
  • How Python’s **ipaddress** module cleverly uses a classic design pattern to allow you to do more with less

To follow along, you just need Python 3.3 or higher since ipaddress was added to the Python standard library in that version. The examples in this tutorial were generated using Python 3.8.

IP Addresses in Theory and Practice

If you remember only one concept about IP addresses, then remember this: an IP address is an integer. This piece of information will help you better understand both how IP addresses function and how they can be represented as Python objects.

Before you jump into any Python code, it can be helpful to see this concept fleshed out mathematically. If you’re here just for some examples of how to use the ipaddress module, then you can skip down to the next section, on using the module itself.

Mechanics of IP Addresses

You saw above that an IP address boils down to an integer. A fuller definition is that an IPv4 address is a 32-bit integer used to represent a host on a network. The term host is sometimes used synonymously with an address.

It follows that there are 232 possible IPv4 addresses, from 0 to 4,294,967,295 (where the upper bound is 232 - 1). But this is a tutorial for human beings, not robots. No one wants to ping the IP address 0xdc0e0925.

The more common way to express an IPv4 address is using quad-dotted notation, which consists of four dot-separated decimal integers:

220.14.9.37

It’s not immediately obvious what underlying integer the address 220.14.9.37 represents, though. Formulaically, you can break the IP address 220.14.9.37 into its four octet components:

>>> (
...     220 * (256 ** 3) +
...      14 * (256 ** 2) +
...       9 * (256 ** 1) +
...      37 * (256 ** 0)
... )
3691907365

As shown above, the address 220.14.9.37 represents the integer 3,691,907,365. Each octet is a byte, or a number from 0 to 255. Given this, you can infer that the maximum IPv4 address is 255.255.255.255 (or FF.FF.FF.FF in hex notation), while the minimum is 0.0.0.0.

Next, you’ll see how Python’s ipaddress module does this calculation for you, allowing you to work with the human-readable form and let the address arithmetic occur out of sight.

The Python ipaddress Module

To follow along, you can fetch your computer’s external IP address to work with at the command line:

$ curl -sS ifconfig.me/ip
220.14.9.37

This requests your IP address from the site ifconfig.me, which can be used to show an array of details about your connection and network.

Note: In the interest of technical correctness, this is quite possibly not your computer’s very own public IP address. If your connection sits behind a NATed router, then it’s better thought of as an “agent” IP through which you reach the Internet.

Now open up a Python REPL. You can use the IPv4Address class to build a Python object that encapsulates an address:

>>> from ipaddress import IPv4Address

>>> addr = IPv4Address("220.14.9.37")
>>> addr
IPv4Address('220.14.9.37')

Passing a str such as "220.14.9.37" to the IPv4Address constructor is the most common approach. However, the class can also accept other types:

>>> IPv4Address(3691907365)  # From an int
IPv4Address('220.14.9.37')

>>> IPv4Address(b"\xdc\x0e\t%")  # From bytes (packed form)
IPv4Address('220.14.9.37')

While constructing from a human-readable str is probably the more common way, you might see bytes input if you’re working with something like TCP packet data.

The conversions above are possible in the other direction as well:

>>> int(addr)
3691907365
>>> addr.packed
b'\xdc\x0e\t%'

In addition to allowing round-trip input and output to different Python types, instances of IPv4Address are also hashable. This means you can use them as keys in a mapping data type such as a dictionary:

>>> hash(IPv4Address("220.14.9.37"))
4035855712965130587

>>> num_connections = {
...     IPv4Address("220.14.9.37"): 2,
...     IPv4Address("100.201.0.4"): 16,
...     IPv4Address("8.240.12.2"): 4,
... }

On top of that, IPv4Address also implements methods that allow for comparisons using the underlying integer:

>>> IPv4Address("220.14.9.37") > IPv4Address("8.240.12.2")
True

>>> addrs = (
...     IPv4Address("220.14.9.37"),
...     IPv4Address("8.240.12.2"),
...     IPv4Address("100.201.0.4"),
... )
>>> for a in sorted(addrs):
...     print(a)
...
8.240.12.2
100.201.0.4
220.14.9.37

You can use any of the standard comparison operators to compare the integer values of address objects.

Note: This tutorial focuses on Internet Protocol version 4 (IPv4) addresses. There are also IPv6 addresses, which are 128-bit rather than 32-bit and are expressed in a headier form such as 2001:0:3238:dfe1:63::fefb. Since the arithmetic of addresses is largely the same, this tutorial cuts one variable out of the equation and focuses on IPv4 addresses.

The ipaddress module features a more flexible factory function, ip_address(), which accepts an argument that represents either an IPv4 or an IPv6 address and does its best to return either an IPv4Address or an IPv6Address instance, respectively.

In this tutorial, you’ll cut to the chase and build address objects with IPv4Address directly.

As you’ve seen above, the constructor itself for IPv4Address is short and sweet. It’s when you start lumping addresses into groups, or networks, that things become more interesting.

#python #developer

Learn IP Address Concepts with Python's ipaddress Module
3.30 GEEK