Spec-Zone.ru › Python 3.12

Введение в модуль 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 не поддерживает полную и сокращённую запись, соответствующие объекты всё равно предоставляют нужные свойства, благодаря чему код, не зависящий от версии IP, может легко использовать наиболее краткую или наиболее полную форму записи адресов 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/howto/ipaddress.html

Spec-Zone.ru

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