Вводное пособие по модулю ipaddress
- author
-
Питер Муди
- author
-
Ник Коглэн
Обзор
Данный документ предназначен для введения в модуль ipaddress. Он в первую очередь рассчитан на пользователей, незнакомых с терминологией IP-сетей, но может быть полезен и сетевым инженерам, желающим ознакомиться с тем, как ipaddress представляет концепции адресации IP-сетей.
Создание объектов адреса/сети/интерфейса
Поскольку ipaddress является модулем для проверки и обработки IP-адресов, первым делом необходимо создать некоторые объекты. Вы можете использовать ipaddress для создания объектов из строк и целых чисел.
Примечание по версиям 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/howto/ipaddress.html