Введение в модуль ipaddress

Автор:Peter Moody
Автор:Nick Coghlan

Обзор

Этот документ предназначен для того, чтобы обеспечить мягкое введение в модуль ipaddress. Она ориентирована в первую очередь на пользователей, которые еще не знакомы с терминологией IP сетей, но также может быть полезна инженерам сети, желающим получить обзор того, как ipaddress представляет концепции IP-сетевых адресаций.

Создание объектов Адрес/Сеть/Интерфейс

Поскольку ipaddress является модулем для проверки и манипулирования IP- адресами, первое, что вы хотите сделать, это создать некоторые объекты. Можно использовать ipaddress для создания объектов из строки и целых чисел.

Примечание о версиях IP

Для читателей, не особенно знакомых с IP-адресацией, важно знать, что интернет- протокол в настоящее время находится в процессе перехода от версии 4 протокола к версии 6. Этот переход происходит в основном потому, что версия 4 протокола не предоставляет достаточного количества адресов для удовлетворения потребностей всего мира, особенно учитывая растущее количество устройств с прямым подключением к интернету.

Объяснение деталей различий между двумя версиями протокола выходит за рамки область видимости этого введения, но читателям необходимо, по крайней мере, знать, что эти две версии существуют, и иногда будет необходимо принудительно использовать одну или другую версию.

IP адреса хоста

Адреса, часто называемые «адресами хоста», являются наиболее базовой единицей при работе с IP-адресацией. Самым простым способом создания адресов является использование функции ipaddress.ip_address() фабрики, которая автоматически определяет, следует ли создавать IPv4 или IPv6 адрес на основе переданного значения:

>>> ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:db8::1')

Адреса также могут быть созданы непосредственно из целых чисел. Предполагается, что значения, которые будут вписываться в 32 бита, являются IPv4 адресами:

>>> ipaddress.ip_address(3221225985)
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address(42540766411282592856903984951653826561)
IPv6Address('2001:db8::1')

Чтобы вызвать использование IPv4 или адресов IPv6, соответствующий классы может быть призван непосредственно. Это особенно полезно для принудительного создания адресов IPv6 для малых целых чисел:

>>> ipaddress.ip_address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv4Address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv6Address(1)
IPv6Address('::1')

Определение сетей

Адреса узлов обычно группируются в IP-сети, поэтому ipaddress предоставляет способ создания, проверки и управления определениями сетей. Объекты IP-сети создаются из строки, которые определяют диапазон адресов узлов, входящих в эту сеть. Простейшей формой для этой информации является пара «сетевой адрес/сетевой префикс», где префикс определяет количество ведущих битов, которые сравниваются для определения того, является ли адрес частью сети, и сетевой адрес определяет ожидаемое значение этих битов.

Что касается адресов, то предусмотрена функция фабрика, которая автоматически определяет правильную IP-версию:

>>> 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')

Сетевые объекты не могут иметь установленные биты узла. Практический результат этого состоит в том, что 192.0.2.1/24 не описывает сеть. Такие определения упоминаются, поскольку интерфейс возражает, так как IP на сетевом примечании обычно - используемый, чтобы описать сетевые интерфейсы компьютера в данной сети и описан далее в следующем разделе.

По умолчанию попытка создать сетевой объект с компанией битов хоста приведет к поднимаемому ValueError. Чтобы запросить, чтобы дополнительные биты вместо этого были принуждены к нулю, флаг strict=False может быть передан конструктору:

>>> 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')

В то время как форма строка предлагает значительно большую гибкость, сети также могут быть определены целыми числами, так же как адреса узлов. В этом случае считается, что сеть содержит только один адрес, идентифицированный целым числом, поэтому сетевой префикс включает в себя весь сетевой адрес:

>>> ipaddress.ip_network(3221225984)
IPv4Network('192.0.2.0/32')
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
IPv6Network('2001:db8::/128')

Как и в случае с адресами, создание определенного вида сети может быть принудительно выполнено путем прямого вызова конструктора класс вместо использования заводской функции.

Интерфейсы хоста

Как упомянуто чуть выше, если вы должны описать адрес в особой сети, ни адрес, ни сеть классы не достаточны. Примечание как 192.0.2.1/24 обычно - используемый сетевыми инженерами и людьми, которые пишут инструменты для брандмауэров и маршрутизаторов как сокращение от «хоста 192.0.2.1 в сети 192.0.2.0/24», соответственно, ipaddress обеспечивает ряд гибридных классы, которые связывают адрес с особой сетью. Интерфейс для создания идентичен интерфейсу для определения сетевых объектов, за исключением того, что адресная часть не ограничивается сетевым адресом.

>>> ipaddress.ip_interface('192.0.2.1/24')
IPv4Interface('192.0.2.1/24')
>>> ipaddress.ip_interface('2001:db8::1/96')
IPv6Interface('2001:db8::1/96')

Целочисленные входы принимаются (как и в случае сетей), и использование определенной IP-версии может быть принудительно выполнено путем прямого вызова соответствующего конструктора.

Проверка адресов/сетевых/интерфейсных объектов

Вы столкнулись с проблемой создания объекта IPv(4|6)(Интерфейс|Адрес|Сеть) поэтому, вероятно, вам нужно получить информацию о нем. ipaddress пытается сделать это простым и интуитивно понятным.

Извлечение IP-версии:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr6 = ipaddress.ip_address('2001:db8::1')
>>> addr6.version
6
>>> addr4.version
4

Получение сети из интерфейса:

>>> 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')

Определение количества отдельных адресов в сети:

>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.num_addresses
256
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.num_addresses
4294967296

Итерация через «используемые» адреса в сети:

>>> 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
...
192.0.2.252
192.0.2.253
192.0.2.254

Получение маски сети (т.е. установленных битов, соответствующих сетевому префиксу) или маски хоста (любых битов, не являющихся частью маски сети):

>>> 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')

Взрыв или сжатие адреса:

>>> addr6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0001'
>>> addr6.compressed
'2001:db8::1'
>>> net6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0000/96'
>>> net6.compressed
'2001:db8::/96'

Хотя IPv4 не поддерживает взрыв или сжатие, связанные объекты по-прежнему предоставляют соответствующие свойства, так что нейтральная к версии код может легко гарантировать, что наиболее лаконичная или наиболее детальная форма является используемый для IPv6 адресов, при этом по-прежнему правильно обрабатывая IPv4 адреса.

Сети как списки адресов

Иногда полезно рассматривать сети как списки. Это означает, что их можно индексировать так:

>>> net4[1]
IPv4Address('192.0.2.1')
>>> net4[-1]
IPv4Address('192.0.2.255')
>>> net6[1]
IPv6Address('2001:db8::1')
>>> net6[-1]
IPv6Address('2001:db8::ffff:ffff')

Это также означает, что сетевые объекты могут использовать подобный синтаксис теста членства в списке:

if address in network:
    # do something

Тестирование удержания выполняется эффективно на основе сетевого префикса:

>>> 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

Сравнения

ipaddress предоставляет несколько простых, надеюсь, интуитивно понятных способов сравнения объектов, где это имеет смысл:

>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True

Исключение TypeError возникает при попытке сравнения объектов различных версий или типов.

Использование IP-адресов с другими модулями

Другие модули, использующие IP-адреса (например, socket), обычно не принимают объекты непосредственно из этого модуля. Вместо этого они должны быть принуждены к целому числу или строка, которые примет другой модуль:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985

Получение дополнительных сведений при сбое создания сущности

Создавая объекты адреса/сети/интерфейса, используя агностические версией фабричные функции, о любых ошибках сообщат как ValueError с универсальным сообщением об ошибке, в котором просто говорится, что переданный в стоимости не был признан объектом того типа. Отсутствие определенной ошибки - то, потому что необходимо знать, является ли стоимость supposed, чтобы быть IPv4 или IPv6, чтобы обеспечить больше детали о том, почему это было отклонено.

Для поддержки сценариев использования, когда полезно иметь доступ к этой дополнительной детали, отдельные конструкторы класс фактически поднимают ValueError подклассы ipaddress.AddressValueError и ipaddress.NetmaskValueError, чтобы точно указать, какая часть определения не была правильно проанализирована.

Сообщения об ошибках являются значительно более подробными при непосредственном использовании конструкторов класс. Например:

>>> 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

Однако оба исключения модуля имеют ValueError в качестве родительского класса, поэтому, если вы не заинтересованы в конкретном типе ошибки, вы по- прежнему можете писать код, как показано ниже:

try:
    network = ipaddress.IPv4Network(address)
except ValueError:
    print('address/netmask is invalid for IPv4:', address)