Введение в модуль 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-on-a-network обычно используется для описания сетевых интерфейсов компьютера в данной сети и более подробно описано в следующем разделе.
По умолчанию попытка создать сетевой объект с установленными битами хоста приведет к появлению 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)