Найти в Дзене

Работа с модулем ipaddress

В предыдущей статье мы познакомились с моделью OSI и узнали, как данные путешествуют по сети. Мы выяснили, что каждое устройство в сети имеет свой IP-адрес, а для разделения адреса на сетевую и узловую части используется маска подсети. Теперь пришло время научиться работать с этими понятиями на практике. Для успешного решения 13 задания ЕГЭ по информатике требуется уверенно понимать работу IP-адресации: уметь определять адрес сети по адресу узла и маске, находить широковещательный адрес, вычислять количество хостов в сети. Часть заданий можно решать вручную, но гораздо эффективнее и быстрее использовать специализированный модуль Python — ipaddress. В этой статье мы сначала разберёмся с теорией: что такое маска подсети, как работает CIDR-нотация, как вручную находить адрес сети и узла. А затем познакомимся с модулем ipaddress и его основными функциями. Мы уже знаем, что IP-адрес состоит из двух частей: адреса сети и адреса узла. Но как понять, где проходит граница между ними? Для этого
Оглавление

Введение

В предыдущей статье мы познакомились с моделью OSI и узнали, как данные путешествуют по сети. Мы выяснили, что каждое устройство в сети имеет свой IP-адрес, а для разделения адреса на сетевую и узловую части используется маска подсети. Теперь пришло время научиться работать с этими понятиями на практике.

Для успешного решения 13 задания ЕГЭ по информатике требуется уверенно понимать работу IP-адресации: уметь определять адрес сети по адресу узла и маске, находить широковещательный адрес, вычислять количество хостов в сети. Часть заданий можно решать вручную, но гораздо эффективнее и быстрее использовать специализированный модуль Python — ipaddress.

В этой статье мы сначала разберёмся с теорией: что такое маска подсети, как работает CIDR-нотация, как вручную находить адрес сети и узла. А затем познакомимся с модулем ipaddress и его основными функциями.

Маска подсети

Мы уже знаем, что IP-адрес состоит из двух частей: адреса сети и адреса узла. Но как понять, где проходит граница между ними? Для этого используется маска подсети.

Маска подсети — это 32-битное число, которое записывается так же, как IP-адрес: четыре числа от 0 до 255, разделённые точками. Например: 255.255.255.0.

Принцип работы маски простой. В двоичном виде маска состоит из единиц и нулей. Единицы указывают на биты, которые относятся к адресу сети, а нули — на биты адреса узла.

Посмотрим на маску 255.255.255.0 в двоичном виде:

11111111.11111111.11111111.00000000

Здесь 24 единицы и 8 нулей. Это значит, что первые 24 бита IP-адреса (три октета) определяют сеть, а последние 8 бит (один октет) — конкретное устройство в этой сети.

У маски есть важное ограничение: единицы должны идти строго подряд, без пропусков. Сначала все единицы, потом все нули. Поэтому маска 255.255.0.255 недопустима — в ней нули стоят между единицами.

Из-за этого правила каждый октет маски может принимать только определённые значения:

-2

Никакие другие значения в маске встретиться не могут. Например, число 131 (10000011 в двоичной системе) содержит ноль между единицами, поэтому не может быть частью маски.

CIDR-нотация

Писать маску полностью каждый раз неудобно. Поэтому придумали CIDR-нотацию (Classless Inter-Domain Routing) — компактный способ записи.

В CIDR-нотации после IP-адреса через косую черту указывается префикс — количество единиц в маске. Например:

  • 192.168.1.0/24 означает маску с 24 единицами, то есть 255.255.255.0
  • 10.0.0.0/8 означает маску 255.0.0.0 (8 единиц)
  • 172.16.0.0/16 означает маску 255.255.0.0

Префикс может быть от 0 до 32. Чем больше префикс, тем меньше устройств может быть в сети.

Количество адресов в сети вычисляется по формуле: N = 232-префикс

Например, в сети с префиксом /24 будет 28 = 256 адресов. Но не все из них доступны для устройств — два адреса всегда зарезервированы: адрес сети и широковещательный адрес. Значит, для устройств остаётся 254 адреса.

В таблице ниже приведено соответствие длины префикса, маски подсети, количества адресов в сети и количества хостов в сети.

-3

Вычисляем адреса вручную

Прежде чем переходить к Python, давайте разберёмся, как находить адрес сети, адрес узла и широковещательный адрес вручную. Это поможет понять логику работы модуля ipaddress.

Адрес сети

Допустим, у нас есть устройство с IP-адресом 192.168.1.67 и маской 255.255.255.0. Как найти адрес сети?

Для этого выполняем побитовую конъюнкцию (И) между IP-адресом и маской. Переведём оба числа в двоичный вид и применим операцию И к каждой паре битов:

IP-адрес:    11000000.10101000.00000001.01000011  (192.168.1.67)
И
Маска:      
11111111.11111111.11111111.00000000  (255.255.255.0)
=
Адрес сети: 
11000000.10101000.00000001.00000000  (192.168.1.0)

Правило простое: где в маске единица — оставляем бит из IP-адреса, где ноль — ставим ноль. Результат: адрес сети — 192.168.1.0.

Адрес хоста

Чтобы найти адрес хоста в сети, нужно сначала инвертировать маску (заменить единицы на нули и наоборот), а затем выполнить побитовую конъюнкцию с IP-адресом:

IP-адрес:              11000000.10101000.00000001.01000011  (192.168.1.67)
И
Инверсия маски:       
00000000.00000000.00000000.11111111
=
Номер узла:           
00000000.00000000.00000000.01000011  (0.0.0.67)

Адрес хоста — 0.0.0.67. Это устройство номер 67 в сети 192.168.1.0.

Широковещательный адрес

Широковещательный адрес используется для отправки сообщений всем устройствам в сети. Чтобы его найти, выполняем побитовую дизъюнкцию (ИЛИ) между адресом сети и инвертированной маской:

Адрес сети:            11000000.10101000.00000001.00000000  (192.168.1.0)
ИЛИ
Инверсия маски:       
00000000.00000000.00000000.11111111(0.0.0.255)
=
Широковещательный:    
11000000.10101000.00000001.11111111  (192.168.1.255)

В этой сети широковещательный адрес — 192.168.1.255.

Маска сети

Иногда в задачах требуется определить маску, зная адрес сети и адрес узла. Логика такая: сравниваем оба адреса побитово. Там, где биты совпадают с адресом сети — ставим единицу в маске, там, где появляется индивидуальная часть узла — ставим ноль.

Например, адрес сети 192.168.0.0, адрес узла 192.168.0.67. Переводим в двоичный вид:

Адрес сети:  11000000.10101000.00000000.00000000

Адрес узла:  11000000.10101000.00000000.01000011

Видим, что первые 24 бита одинаковые, а последние 8 — разные. Значит, маска: 255.255.255.0 или /24.

Но необязательно вычислять все эти значения вручную, ведь есть встроенный в Python модуль ipaddress, который содержит множество полезных функций для работы с сетями. Давайте познакомимся с этими функциями поближе.

Модуль ipaddress

Модуль содержит три основных класса для работы с IP-адресами:

  • ip_address() — для работы с отдельным IP-адресом
  • ip_network() — для работы с сетью целиком
  • ip_interface() — для работы с конкретным устройством в сети

Разберём каждый из них подробно.

Работа с IP-адресом

Функция ip_address() создаёт объект IP-адреса. Она принимает адрес в виде строки или целого числа:

-4

Любой IP-адрес — это просто 32-битное число. Адрес 192.168.1.3 в десятичном виде равен 3232235779. Функции int() и str() позволяют переводить адрес из одного формата в другой:

-5

Для задач ЕГЭ часто нужно получить двоичное представление адреса. Сделать это можно через f-строки:

-6

Запись «:032b» означает: вывести число в двоичном формате (b), дополнив нулями слева до 32 символов (032).

Работа с сетью

Функция ip_network() создаёт объект сети. Она принимает адрес сети с маской (в любом формате):

-7

Обратите внимание: функция ожидает именно адрес сети, где все биты узловой части равны нулю. Если передать адрес узла, возникнет ошибка!

Чтобы автоматически обнулить узловую часть, используйте параметр strict=False:

-8

Теперь можно получить всю информацию о сети через атрибуты объекта:

-9

Метод hosts() возвращает генератор всех доступных адресов для устройств (без адреса сети и широковещательного):

-10

Объект IP-сети поддерживает протокол итерации, следовательно, можно как перебирать все адреса в сети в цикле, так и обращаться к ним через квадратные скобки (метод __getitem__()).

Переберём все адреса в сети с узлом 192.168.1.3 и префиксом /30:

-11

Создадим объект IP-адреса 192.168.1.5 и проверим, принадлежат ли он сети 192.168.1.0/28:

-12

Но такие проверки лучше делать с использованием объекта IP-интерфейса.

Работа с интерфейсом

Функция ip_interface() — самая универсальная. Она создаёт объект, который объединяет информацию об адресе узла и о сети, к которой он принадлежит.

-13

В отличие от ip_network(), функция ip_interface() не требует параметра strict=False — она изначально предназначена для работы с адресами узлов.

С помощью интерфейса удобно проверять, принадлежит ли адрес определённой сети:

-14

Практические примеры

Давайте закрепим знания на нескольких примерах, типичных для заданий ЕГЭ.

Пример 1: Найти адрес сети

Условие: Даны IP-адрес узла 172.16.45.200, маска 255.255.240.0. Найти адрес сети.

Начнём с того, что импортируем нужную функцию из модуля ipaddress. Поскольку нам дан адрес конкретного устройства (узла) и маска, лучше всего подойдёт функция ip_interface():

-15

Теперь создаём объект интерфейса. Для этого передаём в функцию строку, содержащую IP-адрес и маску через косую черту:

-16

В этот момент Python автоматически вычисляет все параметры сети: адрес сети, широковещательный адрес, префикс и так далее. Нам остаётся только обратиться к нужному атрибуту.

Чтобы получить адрес сети, сначала обращаемся к атрибуту network (который содержит информацию о сети), а затем к его атрибуту network_address:

-17

Соберём всё вместе и запустим:

-18

Откуда взялось число 32 в третьем октете? Давайте проверим вручную. Маска 255.255.240.0 в двоичном виде выглядит так:
11111111.11111111.11110000.00000000

Третий октет маски — 240, что в двоичном виде равно 11110000. Это значит, что из третьего октета IP-адреса в адрес сети попадают только первые 4 бита.

Третий октет нашего IP-адреса — 45, в двоичном виде это 00101101. Применяем побитовую конъюнкцию с маской:

00101101  (45)
11110000  (240)
──────────
00100000  (32)

Получаем 32. Именно это значение и показал нам Python.

Пример 2: Определить количество узлов в сети

Условие: Дана сеть 10.0.0.0/12. Нужно определить, сколько устройств может быть подключено к этой сети.

Здесь нам дан адрес сети с префиксом, поэтому используем функцию ip_network():

-19

Создаём объект сети, передавая адрес с префиксом:

-20

Теперь можем узнать общее количество адресов в сети через атрибут num_addresses:

-21

Но не все адреса можно использовать для устройств. Два адреса всегда зарезервированы: адрес сети (первый) и широковещательный адрес (последний). Поэтому для устройств доступно на два адреса меньше:

-22

Полный код:

-23

Проверим это вычисление. Префикс /12 означает, что 12 бит отведены под адрес сети, а оставшиеся 32 — 12 = 20 бит — под адреса узлов. Количество возможных комбинаций из 20 бит равно 220 = 1 048 576. Всё сходится.

Пример 3: Проверить принадлежность адреса сети

Условие: Дана сеть 192.168.100.0/26. Нужно проверить, принадлежит ли ей адрес 192.168.100.70.

Для решения нам понадобятся две функции: ip_network() для создания объекта сети и ip_address() для создания объекта проверяемого адреса:

-24

Создаём объект сети:

-25

Создаём объект IP-адреса, который хотим проверить:

-26

Теперь используем оператор in, чтобы проверить, входит ли адрес в сеть. Объект сети поддерживает эту проверку, поэтому можно писать просто ip in net:

-27

Полный код:

-28

Почему адрес не принадлежит сети? Давайте разберёмся. Префикс /26 означает, что под адреса узлов отведено 32 — 26 = 6 бит. Это даёт 26 = 64 адреса в сети.

Сеть начинается с адреса 192.168.100.0, значит, она содержит адреса от 192.168.100.0 до 192.168.100.63 (всего 64 адреса: от 0 до 63 включительно).

Адрес 192.168.100.70 больше 63, поэтому он выходит за пределы этой сети. Скорее всего, он принадлежит следующей сети — 192.168.100.64/26.

Давайте это проверим:

-29

Теперь адрес найден в правильной сети.

Пример 4: Найти все параметры сети по адресу узла

Условие: Известно, что компьютер имеет IP-адрес 192.168.50.139 и маску 255.255.255.192. Найти адрес сети, широковещательный адрес, диапазон доступных адресов для устройств.

Это комплексная задача, где нужно получить сразу несколько параметров. Используем ip_interface():

-30

Создаём объект интерфейса:

-31

Через атрибут network получаем доступ ко всем параметрам сети. Выведем их по очереди:

-32

Чтобы найти диапазон доступных адресов, воспользуемся методом hosts(). Он возвращает генератор всех адресов, которые можно назначить устройствам (без адреса сети и широковещательного). Преобразуем генератор в список, чтобы взять первый и последний элементы:

-33

Полный код:

-34

Из результата видно, что наш компьютер с адресом 192.168.50.139 находится в сети, которая начинается с 192.168.50.128 и заканчивается на 192.168.50.191. Для устройств доступны адреса от .129 до .190, то есть 62 адреса.

Это лишь малая часть задач, которые могут встретиться на экзамене. В следующей статье мы подробно разберём типизацию 13 заданий ЕГЭ по информатике и научимся их решать с помощью ipaddress.