Spec-Zone.ru › Python 3.14

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

автор:

Peter Moody

автор:

Nick Coghlan

Обзор

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

Создание объектов адресов, сетей и интерфейсов

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

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

Читателям, не знакомым с 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)(Address|Network|Interface), поэтому, вероятно, захотите получить о нём некоторую информацию. В 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 с общим сообщением о том, что переданное значение не распознано как объект указанного типа. Отсутствие конкретной ошибки объясняется тем, что для более подробного объяснения причины отклонения необходимо знать, должен ли объект быть 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)

© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/howto/ipaddress.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API