Python Get Subnet Info from an IP Address within the Subnet
When working with IP addresses programmatically it will at times be required to get details of the subnet such as the subnet address, broadcast, or usable addresses. While Python has an integrated IP address library for interacting with IP addresses there is not an integrated way of getting the subnet from just an IP address. In this post I will show you how to do that with a simple loop.
Required Information
In order to calculate the subnet for a given IP address you will need the following information up front.
- IP address
- CIDR notation of the subnet you wish to calculate for
Finding the Subnet
Since the IP address provided could be the subnet address, an IP address within the subnet or the broadcast address we can just check if the IPv4Subnet object accepts the address and CIDR subnet, if not, decrement the address and try again. If we do this over and over until we dont throw an error, we have our subnet.
Below is a function which does exactly this.
def find_subnet(address, cidr):
address_object = IPv4Address(address)
while True:
try:
return IPv4Network(f'{address_object}/{cidr}')
except ValueError:
address_object = address_object - 1
Stepping through the code, we accept the address and CIDR as strings.
We then create an IPv4Address object based on the address provided so we can easily perform subtractions to get the next address.
We then drop into a loop which will try and instantiate an IPv4Network object with the address and CIDR. The key piece here is knowing the IPv4Network object will throw a value error if the address provided is not a subnet address based on the CIDR provided. So, we simply have to just guess a bunch of times until the network accepts a provided value.
If the provided value throws the ValueError exception, we subtract one from the provided address and try again.
As I stated earlier we do this over and over until the IPv4Network object does not throw an error which will be our IPv4Network object for the network that our provided IP address belongs to.
So now if I make a small script to accept user input, run the function to calculate our subnet, and display the subnet information we can display the subnet information.
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('ip_address', type=str, help='IP address you wish to find the subnet for')
parser.add_argument('subnet_mask', type=str, help='CIDR notation of subnet mask the IP address is on')
args = parser.parse_args()
subnet = find_subnet(args.ip_address, args.subnet_mask)
print(f'Supplied Address: {args.ip_address}')
print(f'Subnet Address: {subnet.network_address}')
print(f'Usable IPs: {subnet.network_address+1} - {subnet.broadcast_address-1}')
print(f'Broadcast Address: {subnet.broadcast_address}')
And running this script we can see its output
$ python find_subnet.py 10.12.22.22 20
Supplied Address: 10.12.22.22
Subnet Address: 10.12.16.0
Usable IPs: 10.12.16.1 - 10.12.31.254
Broadcast Address: 10.12.31.255
$
We can see providing an address of 10.12.22.22 with a CIDR notation of 20 (255.255.240.0) that address belongs to subnet 10.12.16.0/20
Completed Script
If you would like to copy the full script and play with it yourself, I have included it below.
from ipaddress import IPv4Address, IPv4Network
def find_subnet(address, cidr):
"""
Keep subtracting the IP address until we reach the closest network address given the supplied CIDR
and return the IPv4Network object.
:param address: IP address string
:param cidr: CIDR
:return: IPv4Network of the address provided
:rtype: IPv4Network
"""
address_object = IPv4Address(address)
while True:
try:
return IPv4Network(f'{address_object}/{cidr}')
except ValueError:
address_object = address_object - 1
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('ip_address', type=str, help='IP address you wish to find the subnet for')
parser.add_argument('subnet_mask', type=str, help='CIDR notation of subnet mask the IP address is on')
args = parser.parse_args()
subnet = find_subnet(args.ip_address, args.subnet_mask)
print(f'Supplied Address: {args.ip_address}')
print(f'Subnet Address: {subnet.network_address}')
print(f'Usable IPs: {subnet.network_address+1} - {subnet.broadcast_address-1}')
print(f'Broadcast Address: {subnet.broadcast_address}')