Spec-Zone.ru › Python 3.10

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

author

Питер Муди

author

Ник Коглан

Обзор

Данный документ призван предоставить краткое введение в модуль ipaddress. Он предназначен в первую очередь для пользователей, не знакомых с терминологией сетевого администрирования, но также может быть полезен сетевым инженерам, желающим узнать, как 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)(Адрес|Сеть|Интерфейс), поэтому вы, вероятно, хотите получить информацию о нём. 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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/howto/ipaddress.html

Spec-Zone.ru

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