337 lines
11 KiB
ReStructuredText
337 lines
11 KiB
ReStructuredText
.. _ipaddress-howto:
|
|
|
|
***************************************
|
|
An Introduction to the ipaddress module
|
|
***************************************
|
|
|
|
:author: Peter Moody
|
|
:author: Nick Coghlan
|
|
|
|
.. topic:: Overview
|
|
|
|
This document aims to provide a gentle introduction to the
|
|
:mod:`ipaddress` module. It is aimed primarily at users that aren't
|
|
already familiar with IP networking terminology, but may also be useful
|
|
to network engineers wanting an overview of how :mod:`ipaddress`
|
|
represents IP network addressing concepts.
|
|
|
|
|
|
Creating Address/Network/Interface objects
|
|
==========================================
|
|
|
|
Since :mod:`ipaddress` is a module for inspecting and manipulating IP addresses,
|
|
the first thing you'll want to do is create some objects. You can use
|
|
:mod:`ipaddress` to create objects from strings and integers.
|
|
|
|
|
|
A Note on IP Versions
|
|
---------------------
|
|
|
|
For readers that aren't particularly familiar with IP addressing, it's
|
|
important to know that the Internet Protocol is currently in the process
|
|
of moving from version 4 of the protocol to version 6. This transition is
|
|
occurring largely because version 4 of the protocol doesn't provide enough
|
|
addresses to handle the needs of the whole world, especially given the
|
|
increasing number of devices with direct connections to the internet.
|
|
|
|
Explaining the details of the differences between the two versions of the
|
|
protocol is beyond the scope of this introduction, but readers need to at
|
|
least be aware that these two versions exist, and it will sometimes be
|
|
necessary to force the use of one version or the other.
|
|
|
|
|
|
IP Host Addresses
|
|
-----------------
|
|
|
|
Addresses, often referred to as "host addresses" are the most basic unit
|
|
when working with IP addressing. The simplest way to create addresses is
|
|
to use the :func:`ipaddress.ip_address` factory function, which automatically
|
|
determines whether to create an IPv4 or IPv6 address based on the passed in
|
|
value::
|
|
|
|
>>> ipaddress.ip_address('192.0.2.1')
|
|
IPv4Address('192.0.2.1')
|
|
>>> ipaddress.ip_address('2001:DB8::1')
|
|
IPv6Address('2001:db8::1')
|
|
|
|
Addresses can also be created directly from integers. Values that will
|
|
fit within 32 bits are assumed to be IPv4 addresses::
|
|
|
|
>>> ipaddress.ip_address(3221225985)
|
|
IPv4Address('192.0.2.1')
|
|
>>> ipaddress.ip_address(42540766411282592856903984951653826561)
|
|
IPv6Address('2001:db8::1')
|
|
|
|
To force the use of IPv4 or IPv6 addresses, the relevant classes can be
|
|
invoked directly. This is particularly useful to force creation of IPv6
|
|
addresses for small integers::
|
|
|
|
>>> ipaddress.ip_address(1)
|
|
IPv4Address('0.0.0.1')
|
|
>>> ipaddress.IPv4Address(1)
|
|
IPv4Address('0.0.0.1')
|
|
>>> ipaddress.IPv6Address(1)
|
|
IPv6Address('::1')
|
|
|
|
|
|
Defining Networks
|
|
-----------------
|
|
|
|
Host addresses are usually grouped together into IP networks, so
|
|
:mod:`ipaddress` provides a way to create, inspect and manipulate network
|
|
definitions. IP network objects are constructed from strings that define the
|
|
range of host addresses that are part of that network. The simplest form
|
|
for that information is a "network address/network prefix" pair, where the
|
|
prefix defines the number of leading bits that are compared to determine
|
|
whether or not an address is part of the network and the network address
|
|
defines the expected value of those bits.
|
|
|
|
As for addresses, a factory function is provided that determines the correct
|
|
IP version automatically::
|
|
|
|
>>> ipaddress.ip_network('192.0.2.0/24')
|
|
IPv4Network('192.0.2.0/24')
|
|
>>> ipaddress.ip_network('2001:db8::0/96')
|
|
IPv6Network('2001:db8::/96')
|
|
|
|
Network objects cannot have any host bits set. The practical effect of this
|
|
is that ``192.0.2.1/24`` does not describe a network. Such definitions are
|
|
referred to as interface objects since the ip-on-a-network notation is
|
|
commonly used to describe network interfaces of a computer on a given network
|
|
and are described further in the next section.
|
|
|
|
By default, attempting to create a network object with host bits set will
|
|
result in :exc:`ValueError` being raised. To request that the
|
|
additional bits instead be coerced to zero, the flag ``strict=False`` can
|
|
be passed to the constructor::
|
|
|
|
>>> ipaddress.ip_network('192.0.2.1/24')
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: 192.0.2.1/24 has host bits set
|
|
>>> ipaddress.ip_network('192.0.2.1/24', strict=False)
|
|
IPv4Network('192.0.2.0/24')
|
|
|
|
While the string form offers significantly more flexibility, networks can
|
|
also be defined with integers, just like host addresses. In this case, the
|
|
network is considered to contain only the single address identified by the
|
|
integer, so the network prefix includes the entire network address::
|
|
|
|
>>> ipaddress.ip_network(3221225984)
|
|
IPv4Network('192.0.2.0/32')
|
|
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
|
|
IPv6Network('2001:db8::/128')
|
|
|
|
As with addresses, creation of a particular kind of network can be forced
|
|
by calling the class constructor directly instead of using the factory
|
|
function.
|
|
|
|
|
|
Host Interfaces
|
|
---------------
|
|
|
|
As mentioned just above, if you need to describe an address on a particular
|
|
network, neither the address nor the network classes are sufficient.
|
|
Notation like ``192.0.2.1/24`` is commonly used by network engineers and the
|
|
people who write tools for firewalls and routers as shorthand for "the host
|
|
``192.0.2.1`` on the network ``192.0.2.0/24``", Accordingly, :mod:`ipaddress`
|
|
provides a set of hybrid classes that associate an address with a particular
|
|
network. The interface for creation is identical to that for defining network
|
|
objects, except that the address portion isn't constrained to being a network
|
|
address.
|
|
|
|
>>> ipaddress.ip_interface('192.0.2.1/24')
|
|
IPv4Interface('192.0.2.1/24')
|
|
>>> ipaddress.ip_network('2001:db8::1/96')
|
|
IPv6Interface('2001:db8::1/96')
|
|
|
|
Integer inputs are accepted (as with networks), and use of a particular IP
|
|
version can be forced by calling the relevant constructor directly.
|
|
|
|
|
|
Inspecting Address/Network/Interface Objects
|
|
============================================
|
|
|
|
You've gone to the trouble of creating an IPv(4|6)(Address|Network|Interface)
|
|
object, so you probably want to get information about it. :mod:`ipaddress`
|
|
tries to make doing this easy and intuitive.
|
|
|
|
Extracting the IP version::
|
|
|
|
>>> addr4 = ipaddress.ip_address('192.0.2.1')
|
|
>>> addr6 = ipaddress.ip_address('2001:db8::1')
|
|
>>> addr6.version
|
|
6
|
|
>>> addr4.version
|
|
4
|
|
|
|
Obtaining the network from an interface::
|
|
|
|
>>> host4 = ipaddress.ip_interface('192.0.2.1/24')
|
|
>>> host4.network
|
|
IPv4Network('192.0.2.0/24')
|
|
>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
|
|
>>> host6.network
|
|
IPv6Network('2001:db8::/96')
|
|
|
|
Finding out how many individual addresses are in a network::
|
|
|
|
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
|
|
>>> net4.numhosts
|
|
256
|
|
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
|
|
>>> net6.numhosts
|
|
4294967296
|
|
|
|
Iterating through the "usable" addresses on a network::
|
|
|
|
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
|
|
>>> for x in net4.hosts():
|
|
print(x)
|
|
192.0.2.1
|
|
192.0.2.2
|
|
192.0.2.3
|
|
192.0.2.4
|
|
<snip>
|
|
192.0.2.252
|
|
192.0.2.253
|
|
192.0.2.254
|
|
|
|
|
|
Obtaining the netmask (i.e. set bits corresponding to the network prefix) or
|
|
the hostmask (any bits that are not part of the netmask):
|
|
|
|
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
|
|
>>> net4.netmask
|
|
IPv4Address('255.255.255.0')
|
|
>>> net4.hostmask
|
|
IPv4Address('0.0.0.255')
|
|
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
|
|
>>> net6.netmask
|
|
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
|
|
>>> net6.hostmask
|
|
IPv6Address('::ffff:ffff')
|
|
|
|
|
|
Exploding or compressing the address::
|
|
|
|
>>> addr6.exploded
|
|
'2001:0db8:0000:0000:0000:0000:0000:0000'
|
|
>>> addr6.compressed
|
|
'2001:db8::'
|
|
>>> net6.exploded
|
|
'2001:0db8:0000:0000:0000:0000:0000:0000/96'
|
|
>>> net6.compressed
|
|
'2001:db8::/96'
|
|
|
|
While IPv4 doesn't support explosion or compression, the associated objects
|
|
still provide the relevant properties so that version neutral code can
|
|
easily ensure the most concise or most verbose form is used for IPv6
|
|
addresses while still correctly handling IPv4 addresses.
|
|
|
|
|
|
Networks as lists of Addresses
|
|
==============================
|
|
|
|
It's sometimes useful to treat networks as lists. This means it is possible
|
|
to index them like this::
|
|
|
|
>>> net4[1]
|
|
IPv4Address('192.0.2.1')
|
|
>>> net4[-1]
|
|
IPv4Address('192.0.2.255')
|
|
>>> net6[1]
|
|
IPv6Address('2001::1')
|
|
>>> net6[-1]
|
|
IPv6Address('2001::ffff:ffff')
|
|
|
|
|
|
It also means that network objects lend themselves to using the list
|
|
membership test syntax like this::
|
|
|
|
if address in network:
|
|
# do something
|
|
|
|
Containment testing is done efficiently based on the network prefix::
|
|
|
|
>>> addr4 = ipaddress.ip_address('192.0.2.1')
|
|
>>> addr4 in ipaddress.ip_network('192.0.2.0/24')
|
|
True
|
|
>>> addr4 in ipaddress.ip_network('192.0.3.0/24')
|
|
False
|
|
|
|
|
|
Comparisons
|
|
===========
|
|
|
|
:mod:`ipaddress` provides some simple, hopefully intuitive ways to compare
|
|
objects, where it makes sense::
|
|
|
|
>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
|
|
True
|
|
|
|
A :exc:`TypeError` exception is raised if you try to compare objects of
|
|
different versions or different types.
|
|
|
|
|
|
Using IP Addresses with other modules
|
|
=====================================
|
|
|
|
Other modules that use IP addresses (such as :mod:`socket`) usually won't
|
|
accept objects from this module directly. Instead, they must be coerced to
|
|
an integer or string that the other module will accept::
|
|
|
|
>>> addr4 = ipaddress.ip_address('192.0.2.1')
|
|
>>> str(addr4)
|
|
'192.0.2.1'
|
|
>>> int(addr4)
|
|
3221225985
|
|
|
|
|
|
Getting more detail when instance creation fails
|
|
================================================
|
|
|
|
When creating address/network/interface objects using the version-agnostic
|
|
factory functions, any errors will be reported as :exc:`ValueError` with
|
|
a generic error message that simply says the passed in value was not
|
|
recognized as an object of that type. The lack of a specific error is
|
|
because it's necessary to know whether the value is *supposed* to be IPv4
|
|
or IPv6 in order to provide more detail on why it has been rejected.
|
|
|
|
To support use cases where it is useful to have access to this additional
|
|
detail, the individual class constructors actually raise the
|
|
:exc:`ValueError` subclasses :exc:`ipaddress.AddressValueError` and
|
|
:exc:`ipaddress.NetmaskValueError` to indicate exactly which part of
|
|
the definition failed to parse correctly.
|
|
|
|
The error messages are significantly more detailed when using the
|
|
class constructors directly. For example::
|
|
|
|
>>> ipaddress.ip_address("192.168.0.256")
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address
|
|
>>> ipaddress.IPv4Address("192.168.0.256")
|
|
Traceback (most recent call last):
|
|
...
|
|
ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256'
|
|
|
|
>>> ipaddress.ip_network("192.168.0.1/64")
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network
|
|
>>> ipaddress.IPv4Network("192.168.0.1/64")
|
|
Traceback (most recent call last):
|
|
...
|
|
ipaddress.NetmaskValueError: '64' is not a valid netmask
|
|
|
|
However, both of the module specific exceptions have :exc:`ValueError` as their
|
|
parent class, so if you're not concerned with the particular type of error,
|
|
you can still write code like the following::
|
|
|
|
try:
|
|
network = ipaddress.IPv4Network(address)
|
|
except ValueError:
|
|
print('address/netmask is invalid for IPv4:', address)
|
|
|