Введение в модуль ipaddress
- автор
-
Питер Муди
- автор
-
Ник Коглэн
Обзор
Данный документ призван предоставить краткое введение в модуль ipaddress. Он предназначен прежде всего для пользователей, не знакомых с терминологией IP-сетей, но может быть полезен и сетевым инженерам, желающим получить общее представление о том, как ipaddress представляет концепции адресации IP-сетей.
Создание объектов адреса/сети/интерфейса
Поскольку ipaddress является модулем для проверки и обработки IP-адресов, первым делом вам нужно создать некоторые объекты. Вы можете использовать ipaddress для создания объектов из строк и целых чисел.
Примечание об IP-версиях
Для читателей, не слишком знакомых с адресацией IP, важно знать, что протокол Internet Protocol (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.11/howto/ipaddress.html