Spec-Zone.ru › Python 3.12

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

author:

Питер Муди

author:

Ник Коглан

Обзор

Данный документ призван предоставить краткое введение в модуль 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–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