🌐 Концепция IP-адресов на примере Python-модуля ipaddress
Рассказываем, как работать с IP-адресами классического протокола IPv4 в теории и на практике – в коде Python. Показываем, как проверить связь между группой IP-адресов и их соответствие подсетям и диапазонам частных IP-адресов.
Вводные сведения об IP-адресах
В самом грубом представлении IP-адрес – это просто число. В случае протокола IPv4 IP-адрес– это 32-разрядное целое число, используемое для представления хоста в сети. То есть существует 232 возможных IPv4 адреса – от 0 до 4 294 967 295. IPv4-адреса записывают в виде четырех октетов – целых чисел, разделенных точками:
Каждый октет – это один байт, число от 0 до 255. То есть максимальный адрес равен
255.255.255.255
, а минимальный – 0.0.0.0
.
Далее мы рассмотрим, как
модуль ipaddress
выполняет преобразования адреса так, что нам не приходится отвлекаться на строение адреса.
Модуль ipaddress
Получим внешний IP-адрес нашего компьютера для работы с ним в командной строке. В Linux это делается так:
Этот запрос узнает наш IP-шник на сайте ifconfig.me. Сайт также выдает множество другой полезной информации о вашем сетевом подключении.
Теперь откроем интерпретатор Python. Чтобы создать объект Python с инкапсулированным адресом, создадим класс IPv4Address
:
Передача строки "220.14.9.37"
в конструктор IPv4Address
– наиболее распространенный подход, но класс может
принимать и другие типы:
Адрес можно распаковать в требуемую форму:
Экземпляры IPv4Address
являются хэшируемыми и могут использоваться в качестве
ключей словаря:
Класс IPv4Address
также реализует
методы, позволяющие проводить сравнения:
Можно использовать любой стандартный оператор сравнения целочисленных значений адресных объектов.
IP-сети и интерфейсы
Сеть – это набор
IP-адресов. Сети описываются и отображаются как непрерывные диапазоны адресов.
Например, сеть может соответствовать диапазону 192.4.2.0
– 192.4.2.255
, т. е.
включать 256 адресов. Если нужно это отобразить в краткой форме, используется нотация CIDR.
В CIDR сеть
определяется с помощью сетевого адреса и префикса <network_address>/<prefix>
:
В данном случае префикс равен 24. Префикс – это количество ведущих битов, соответствующих входящим в сеть адресам. Ведущие биты отсчитываются слева направо.
Пример: входит ли адрес 192.4.2.12 в сеть 192.4.2.0/24?
Ответ: да, так как ведущие 24 бита адреса 192.4.2.12
– это первые три октета: 192.4.2
. Последний октет соответствует последним 8 битам 32-битного IP-адреса.
Воспользуемся netmask
для маскирования
битов в сравниваемых
адресах.
На рисунке ниже показано, как сравниваются ведущие биты, чтобы определить, является ли адрес частью сети.
Последние 8 бит в
192.4.2.12
маскируются нулем и игнорируются при сравнении.
Рассмотрим еще один важный тип адреса – широковещательный.
Это единственный адрес, который может использоваться для связи со всеми хостами сети:
Чаще всего вы будете сталкиваться с длиной префикса кратной 8.
Любое целое число от 0 до 32 является допустимым, но такой вариант встречается реже:
Перебор IP-адресов в цикле
Класс IPv4Network
позволяет
перебирать отдельные адреса в цикле for:
Инструмент
net.hosts()
возвращает генератор, выдающий адреса, исключая сетевые и
широковещательные:
Подсети IP-адресов
Подсеть – это часть IP-сети:
В коде выше small_net
содержит 16 адресов, а big_net
– 65 536.
Распространенный способ разбиения на подсети – это увеличение длины префикса на 1:
К счастью, IPv4Network
расчеты подсетей поддерживаются встроенным методом subnets()
:
В передаваемом subnets()
аргументе можно задать, каким должен быть новый префикс:
Специальные диапазоны IP-адресов
Администрация адресного пространства Интернет (Internet Assigned Numbers Authority, IANA) совместно с Инженерном советом Интернета (Internet Engineering Task Force, IETF) осуществляют надзор за распределением диапазонов адресов. Реестр подобных адресов – важная таблица, которая описывает, для каких целей зарезервированы диапазоны IPv4-адресов.
К примеру, это частные IP-адреса, используемые для внутренней связи между устройствами в сети, не требующей подключения к интернету:
Случайным образом выберем
адрес – 10.243.156.214
. Относится ли этот адрес к приватным? Для этого проверим, попадает ли он в диапазон cети 10.0.0.0/8
:
Другой специальный тип
адреса – это локальный адрес связи, состоящий из блока 169.254.0.0/16
. Примером
может служить Amazon Time Sync Service, доступный для инстансов AWS EC2 по адресу
169.254.169.123
. Данный пул также использует Windows для
выдачи адресов сетевым адаптерам при отсутствии интернета от провайдера.
Модуль ipaddress
предоставляет набор свойств
для проверки того, относится ли адрес к специальным:
Вот еще несколько зарезервированных сетей:
0.0.0.0/8
– адреса источников пакетов «своей» сети;127.0.0.0/8
– используется для локального хоста;169.254.0.0/16
– внутренние адреса;198.18.0.0/15
– для бенчмаркинга сетей.
Что происходит внутри ipaddress
В дополнение к хорошо документированному API, исходный код CPython и класс IPv4Address показывают некоторые отличные идеи, как улучшить собственный код.
Компоновщик
Модуль ipaddress
использует преимущества шаблона проектирования «Компоновщик». Класс IPv4Address
представляет собой компоновщик, который оборачивает обычное целое число.
Каждый экземпляр
IPv4Address
имеет атрибут _ip
, число типа int
. Многие свойства и методы класса определяются
значением этого атрибута:
Атрибут _ip
отвечает
за создание int(addr)
. Цепочка вызовов выглядит следующим образом:
Продемонстрируем силу ._ip
путем расширения класса IPv4Address
:
Добавление .__and__()
позволяет использовать бинарный оператор &
, чтобы применять маску к
IP-адресу:
Метод __and__()
позволяет использовать либо другой IPv4Address, либо непосредственно int в
качестве маски. Поскольку MyIPv4 является подклассом IPv4Address, проверка
isinstance()
в данном случае вернет True
.
Помимо перегрузки оператора, есть возможность добавить новые свойства:
В методе binary_repr
(строка 8), используется .packed
для преобразования IP-адреса в массив байтов, который
затем форматируется, как строковое представление бинарной формы.
В from_binary_repr
, вызов int(re.sub(r"[^01]", "", binary_repr), 2)
(строка 14) состоит
из двух частей:
- удаление из входящей строки всего, кроме нулей и единиц;
- анализ результата с помощью
int(<string>, 2)
.
Методы binary_repr()
и from_binary_repr()
позволяют проводить двустороннюю конвертацию:
Таким образом, мы разобрали несколько способов использования преимуществ шаблона IP-as-integer, который может помочь расширить функциональность IPv4Address с небольшим количеством дополнительного кода.
Заключение
Если вам нравится язык Python и вы хотите детально овладеть стандартной библиотекой, у нас есть множество родственных публикаций:
- Как хранить объекты Python со сложной структурой (о модуле pickle)
- Итерируем правильно: 20 приемов использования в Python модуля itertools
- Не изобретать велосипед, или Обзор модуля collections в Python
- Назад в будущее: практическое руководство по путешествию во времени с Python
- Как подружить Python и базы данных SQL. Подробное руководство