Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Intermediate

Using Python to manipulate IP


addresses and perform CIDR
calculations
In this section, you will explore TCP/IP networking using Python scripts.
The Python ipaddress module
The ipaddress module simplifies working with IPv4 and IPv6 addresses in Python.
In this section, we will focus on IPv4 and will work primarily with the following
three class types:

IPv4Address: Represents a single IPv4 address


IPv4Network: Represents an IPv4 network
IPv4Interface: Represents an IPv4 interface

You can get more information about this module with the help command from the
Python interpreter:
is the class that represents and manipulates single IPv4 addresses:
IPv4Address
The class represents an IPv4 address or network. To create these objects in
Python, the module provides some basic factory functions:
import ipaddress
from ipaddress import IPv4Address, IPv4Network, IPv4Interface

After you create an IPv4/IPv6 object, you can get a lot information from the
class, for example, whether it is a multicast address or a private address, the
prefix length, and netmask.

In the following screenshot, we can see the methods that are used to check these
use cases:
From Python 3.3, the best way to check whether an IPv6 or IPv4 address is
correct is to use the Python standard library module, ipaddress.
Check out https://docs.python.org/3/library/ipaddress.html for the complete documentation.

If you're using Python 3.3 or later, you can use the ipaddress module to validate
the IP address:
>>> import ipaddress
>>> ipaddress.ip_address('127.0.0.1')
IPv4Address('127.0.0.1')
>>> ipaddress.ip_address('500.500.0.1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.7/ipaddress.py", line 54, in ip_address
address)
ValueError: '500.500.0.1' does not appear to be an IPv4 or IPv6 address

In this example, we use this method to validate both IPv4 and IPv6. You can find
the following code in the validate_ip_address.py file:
!/usr/bin/env python3

import ipaddress
import sys

try:
ip = ipaddress.ip_address(sys.argv[1])
print('%s is a correct IP%s address' % (ip, ip.version))
except ValueError:
print('address/netmask is invalid: %s' % sys.argv[1])
except:
print('Usage : %s ip' % sys.argv[0])

If you execute the previous script with an IP address as a parameter, it will


validate in both IPv4 and IPv6 versions:
$ python validate_ip_address.py 127.0.0.1
127.0.0.1 is a correct IP4 address
$ python validate_ip_address.py ::1
::1 is a correct IP6 address
Manipulating IP addresses
Often, you will need to manipulate IP addresses and perform some sort of
operations on them. Python 3 has a built-in ipaddress module to help you carry
out this task. It has convenient functions for defining the IP addresses and the IP
networks, and for finding lots of useful information. For example, if you would
like to know how many IP addresses exist in a given subnet, for instance,
10.0.1.0/255.255.255.0 or 10.0.1.0/24, you can find them with the help of the
following command.

This module will provide several classes and factory functions for working with
both IPv4 and IPv6 versions.
IP network objects
Let's import the ipaddress module and define a net4 network:
>>> import ipaddress
>>> net4 = ipaddress.ip_network('10.0.1.0/24')

Now, we can find some useful information, such as netmask and the
network/broadcast address, of net4:
>>> net4.netmask
IP4Address(255.255.255.0)

The netmask properties of net4 will be displayed as an IP4Address object. If you are
looking for its string representation, then you can call the str() method, as shown
here:
>>> str(net4.netmask)
'255.255.255.0'

Similarly, you can find the network and the broadcast addresses of net4 by using
the following code:
>>> str(net4.network_address)
10.0.1.0
>>> str(net4.broadcast_address)
10.0.1.255

We can get the number of addresses net4 can hold with the following command:
>>> net4.num_addresses
256

So, if we subtract the network and the broadcast addresses, the total available IP
addresses will be 254. We can call the hosts() method on the net4 object. This will
produce a Python generator, which will supply all the hosts as IPv4Address objects:
>>> net4.hosts()
>>> <generator object _BaseNetwork.hosts at 0x02F25FC0>
>>> all_hosts = list(net4.hosts())
>>> len(all_hosts)
254
>>> print(all_hosts)
>>> [IPv4Address('10.0.1.1'), IPv4Address('10.0.1.2'), IPv4Address('10.0.1.3'), IPv4Address('10.0.1.4'
Subnetting in Python
Another use case is an IP subnetting application, which gives you the required IP
subnets based on required network size or amount of networks per location. We
can also find the subnet information from the IPv4Network objects, as follows:
>>> net4.subnets()
<generator object _BaseNetwork.subnets at 0x02F2C0C0>
>>> subnets = list( net4.subnets())
>>> subnets
[ IPv4Network('10.0.1.0/25'), IPv4Network('10.0.1.128/25') ]

The ipaddress module includes many functions to create subnets and supernets;
for example, we can use these methods to check whether a network overlaps
with another:
>>> ipnet = ipaddress.IPv4Network("10.2.0.0/16")
>>> list(ipnet.subnets())
[IPv4Network('10.2.0.0/17'), IPv4Network('10.2.128.0/17')]

The subnets(prefixlen_diff=1, new_prefix=None) method also has the capacity to


generate subnets with additional host bits or with a specific amount of network
bits. In the following example, we use the new_prefix argument in the subnets
method to define the number of network bits for the new network mask:
# new_prefix = number of network bits for the new mask
>>> list(ipnet.subnets(new_prefix=20))
[IPv4Network('10.2.0.0/20'), IPv4Network('10.2.16.0/20'), IPv4Network('10.2.32.0/20'), IPv4Network('1

Any IPv4Network object can tell, which is the opposite of the subnet by looking at its
parent supernet:
>>> net4.supernet()
IPv4Network('10.0.0.0/23')
Network interface objects
In the ipaddress module, we have a convenient class to represent an interface's IP
configuration in detail: IPv4Interface. It takes an arbitrary address and behaves
like a network address object:
>>> import ipaddress
>>> eth0 = ipaddress.IPv4Interface('192.168.0.1/24')
>>> eth0.ip
IPv4Address('192.168.0.1')
>>> eth0.with_prefixlen
'192.168.0.1/24'
>>> eth0.with_netmask
'192.168.0.1/255.255.255.0'
>>> eth0.network
IPv4Network('192.168.0.0/24')
>>> eth0.is_private
True
>>> eth0.is_reserved
False
>>> eth0.is_multicast
False
>>>

As you can see, a network interface, eth0, with the IPv4Address class, has been
defined. It has some interesting properties, such as IP and network address. In
the same way as the network objects, you can check whether the address is
private, reserved, or multicast.
IP address objects
In this example, the loopback interface is defined with the 127.0.0.1 IP address. As
you can see, the is_loopback property returns True:
>>> loopback = ipaddress.IPv4Interface('127.0.0.1')
>>> loopback.is_private
True
>>> loopback.is_reserved
False
>>> loopback.is_multicast
False
>>> loopback.is_loopback
True

The IP address classes have many more interesting properties. You can perform
some arithmetic and logical operations on those objects. For example, we can
check whether an IP address is part of a network.

In this example, we are checking whether an IP address is part of a specific


network. Here, a network called net has been defined by the network address,
which is 192.168.1.0/24, and the membership of eth0 and eth1 has been tested to see
if these IP addresses are part of the network:
>>> eth0 = ipaddress.IPv4Interface('192.168.1.1')
>>> eth1 = ipaddress.IPv4Interface('192.168.2.1')
>>> net = ipaddress.ip_network('192.168.1.0/24')
>>> eth0 in net
True
>>> eth1 in net
False
Planning IP addresses for your local
area network
If you are wondering how to pick up a suitable IP subnet, try the ipaddress
module. The following code snippet shows an example of how to choose a
specific subnet, based on the number of necessary host IP addresses for a small
private network.

Suppose you have a CIDR network address, such as 192.168.0.0/24, and you want
to generate a range of all the IP addresses that it represents (192.168.0.1 to
192.168.0.254). The ipaddress module can be easily used to perform such
calculations:
>>> import ipaddress
>>> net = ipaddress.ip_network('192.168.0.0/24')
>>> net
IPv4Network('192.168.0.0/24')
>>> for a in net:
... print(a)
...
192.168.0.1
192.168.0.2
192.168.0.3
...
192.168.0.254

In this example, we are using the ip_network method from the ipaddress module to
generate a range of all the IP addresses that represent the network.

You can find the following code in the net_ip_planner.py file:


!/usr/bin/env python3

import ipaddress as ip

CLASS_C_ADDR = '192.168.0.0'

mask = input("Enter the mask len (24-30): ")


mask = int(mask)
if mask not in range(23, 31):
raise Exception("Mask length must be between 24 and 30")

net_addr = CLASS_C_ADDR + '/' + str(mask)


print("Using network address:%s " %net_addr)

try:
network = ip.ip_network(net_addr)
except:
raise Exception("Failed to create network object")

print("This mask will give %s IP addresses" %(network.num_addresses))


print("The network configuration will be:")
print("\t network address: %s" %str(network.network_address))
print("\t netmask: %s" %str(network.netmask))
print("\t broadcast address: %s" %str(network.broadcast_address))
first_ip, last_ip = list(network.hosts())[0], list(network.hosts())[-1]
print("\t host IP addresses: from %s to %s" %(first_ip,last_ip))

The following is the execution of the previous script for some masks and
the C class IP address, 192.168.0.0:

Execution with mask 24:


Enter the mask len (24-30): 24
Using network address:192.168.0.0/24
This mask will give 256 IP addresses
The network configuration will be:
network address: 192.168.0.0
netmask: 255.255.255.0
broadcast address: 192.168.0.255
host IP addresses: from 192.168.0.1 to 192.168.0.254

Execution with mask 30:


Enter the mask len (24-30): 30
Using network address:192.168.0.0/30
This mask will give 4 IP addresses
The network configuration will be:
network address: 192.168.0.0
netmask: 255.255.255.252
broadcast address: 192.168.0.3
host IP addresses: from 192.168.0.1 to 192.168.0.2

You might also like