1. Введение

В задании №13 нужно работать с IPv4-адресами и масками сети. В условии могут попросить определить адрес сети, перебрать адреса внутри сети, найти допустимую маску или посчитать адреса, двоичная запись которых обладает нужным свойством.

Задание проверяет умение:

  • понимать строение IPv4-адреса и маски сети;
  • переводить байты адреса в восьмибитную двоичную запись;
  • находить адрес сети с помощью поразрядной конъюнкции;
  • работать с библиотекой ipaddress;
  • перебирать адреса и маски и проверять двоичную запись адреса.

Основной способ решения на экзамене — короткая программа на Python с библиотекой ipaddress.

Но код становится понятным только после того, как разобраны адрес, маска и адрес сети.

2. Теория

IPv4-адрес

IPv4-адрес состоит из четырёх десятичных чисел от 0 до 255, разделённых точками. Каждая часть называется байтом и занимает 8 бит. Поэтому весь адрес содержит 32 бита.

1

Например, число 10 в одном байте записывается как 00001010, а число 8 — как 00001000.

Ведущие нули обязательны: без них невозможно правильно разделить 32-битную запись на четыре байта.

Маска сети

Маска — тоже 32-битное число. Единицы маски показывают, какая часть IP-адреса относится к адресу сети, а нули — какая часть относится к адресу узла внутри этой сети.

В корректной маске сначала идут все единицы, а затем все нули. Количество начальных единиц называют длиной префикса.

Например, маске 255.255.224.0 соответствует префикс /19.

1

Как получить адрес сети

Адрес сети получают, применяя операцию И к каждой паре соответствующих битов IP-адреса и маски.

В условии это может называться поразрядной конъюнкцией или поразрядным умножением.

1

Для адреса 10.8.248.131 и маски 255.255.224.0 получается адрес сети 10.8.224.0.

Биты узла, стоящие под нулями маски, обнуляются.

Библиотека ipaddress: основа кода

Модуль ipaddress входит в стандартную библиотеку Python и умеет создавать отдельные адреса и целые сети.

  • from ipaddress import * — подключает функции и классы модуля.
  • ad = ip_address('10.8.248.131') — создаёт отдельный IP-адрес.
  • net = ip_network('10.8.224.0/255.255.224.0') — создаёт сеть по адресу сети и маске.
  • net = ip_network('10.8.248.131/255.255.224.0', 0) — создаёт сеть по адресу узла; 0 означает strict=False.
  • net.network_address — возвращает адрес сети.
  • net.netmask — возвращает маску сети.
  • net.num_addresses — возвращает общее количество адресов сети.
  • for ip in net: — перебирает все IP-адреса сети.
  • f'{ip:b}' — получает 32-битную двоичную запись IP-адреса.
  • bin(int(ip))[2:].zfill(32) — другой способ получить ту же 32-битную запись.

Без второго аргумента ip_network ожидает именно адрес сети.

Если в условии дан адрес узла, пишем 0 или strict=False: тогда Python сам обнулит узловую часть и найдёт сеть.

Перебор адресов и масок

У IPv4-адреса 32 бита, поэтому длина маски может принимать 33 значения: от /0 до /32.

Если в маске mask единиц, то нулей в ней:

32 - mask

а число адресов сети равно:

2 в степени (32 - mask)

Для проверки количества единиц или нулей адрес переводят в двоичную строку и используют count.

Срезы [:16] и [16:] отделяют левые два байта от правых двух байтов.

bits = f'{ip:b}'

ones = bits.count('1')
zeros = bits.count('0')

left = bits[:16]
right = bits[16:]

Адреса компьютеров в сети

Если условие спрашивает все IP-адреса сети, перебираем:

for ip in net:

Адрес сети и широковещательный адрес тоже войдут в перебор.

Если нужен адрес компьютера, крайние адреса не подходят:

  • первый адрес компьютера — net[1];
  • последний адрес компьютера — net[-2].
1

Если условие спрашивает все IP-адреса сети, перебираем for ip in net — адрес сети и широковещательный адрес тоже войдут в перебор.

Если нужен адрес компьютера, крайние адреса не подходят: первый адрес компьютера — net[1], последний — net[-2].

net = ip_network('45.172.106.203/255.255.252.0', 0)

print(net[1])   # наименьший адрес компьютера
print(net[-2])  # наибольший адрес компьютера

3. Пошаговое решение задачи кодом

Разбор задачи

В терминологии сетей TCP/IP маской сети называют двоичное число, которое показывает, какая часть IP-адреса узла сети относится к адресу сети, а какая — к адресу узла в этой сети.

Адрес сети получается в результате применения поразрядной конъюнкции к заданному адресу узла и маске сети.

Сеть задана IP-адресом 87.226.26.72 и маской сети 255.255.255.252.

Сколько в этой сети IP-адресов, у которых количество нулей в двоичной записи IP-адреса чётно?

В ответе укажите только число.

Код

from ipaddress import *  # подключаем библиотеку

k = 0  # количество подходящих адресов

# перебираем все адреса сети
for ip in ip_network('87.226.26.72/255.255.255.252', 0):
    ip2 = f'{ip:b}'  # переводим адрес в 32-битную строку

    # проверяем, что количество нулей чётно
    if ip2.count('0') % 2 == 0:
        k += 1  # учитываем подходящий адрес

print(k)  # выводим ответ

Ответ: 2

Проверка результата

Маска 255.255.255.252 соответствует префиксу /30, поэтому сеть содержит 2 в степени (32 - 30), то есть 4 адреса.

Из них условию отвечают два адреса.

IP-адресКоличество нулей в 32-битной записи
87.226.26.7218 — чётное, адрес подходит
87.226.26.7317 — нечётное
87.226.26.7417 — нечётное
87.226.26.7516 — чётное, адрес подходит

4. Практика

Задача 1. Досрочная волна 2024

Сеть задана IP-адресом 105.224.200.224 и сетевой маской 255.255.255.224.

Сколько в этой сети IP-адресов, для которых количество единиц в двоичной записи IP-адреса кратно 4?

В ответе укажите только число.

Решение на Python

from ipaddress import *

k = 0

for ip in ip_network('105.224.200.224/255.255.255.224', 0):
    ip2 = f'{ip:b}'

    if ip2.count('1') % 4 == 0:
        k += 1

print(k)

Ответ: 10

Задача 2

Сеть, в которой содержится узел c IP-адресом 116.242.A.26, задана маской сети 255.255.255.224, где A — некоторое допустимое для записи IP-адреса число.

Определите максимальное значение A, для которого для всех IP-адресов этой сети в двоичной записи IP-адреса суммарное количество единиц в левых двух байтах не меньше суммарного количества единиц в правых двух байтах.

Решение на Python

from ipaddress import *

answer = 0

for A in range(256):
    net = ip_network(f'116.242.{A}.26/255.255.255.224', 0)

    good = True

    for ip in net:
        ip2 = f'{ip:b}'

        if ip2[:16].count('1') < ip2[16:].count('1'):
            good = False
            break

    if good:
        answer = A

print(answer)

Переменная good остаётся равной True только тогда, когда условие выполнено для каждого адреса сети.

Значения A перебираются по возрастанию, поэтому последнее сохранённое значение является максимальным.

Ответ: 240

Задача 3

Для узла с IP-адресом 218.48.192.64 адрес сети равен 218.48.192.0.

Сколько существует различных возможных значений третьего слева байта маски, если известно, что в этой сети не менее 500 узлов?

Ответ запишите в виде десятичного числа.

Решение на Python

from ipaddress import *

third_bytes = set()

for mask in range(33):
    net = ip_network(f'218.48.192.64/{mask}', 0)

    if (str(net.network_address) == '218.48.192.0'
            and net.num_addresses >= 500):

        third = int(str(net.netmask).split('.')[2])
        third_bytes.add(third)

print(len(third_bytes))

Перебираются все 33 длины маски.

set сохраняет только различные значения третьего байта маски, поэтому в конце достаточно вывести длину множества.

Ответ: 6

Задача 4. Минимальное количество нулей маски

Для узла с IP-адресом 111.91.200.28 адрес сети равен 111.91.192.0.

Найдите наименьшее возможное количество нулей в двоичной записи маски подсети.

Решение на Python

from ipaddress import *

answers = []

for mask in range(33):
    net = ip_network(f'111.91.200.28/{mask}', 0)

    if str(net.network_address) == '111.91.192.0':
        answers.append(32 - mask)

print(min(answers))

mask — количество единиц маски. Следовательно, количество нулей равно 32 - mask.

Сохраняем результат для каждой подходящей маски и выбираем минимальный.

Ответ: 12

Задача 5. Адрес оканчивается на две единицы

Сеть задана IP-адресом 235.86.56.0 и маской сети 255.255.248.0.

Сколько в этой сети IP-адресов, двоичная запись которых оканчивается на две единицы?

Решение на Python

from ipaddress import *

k = 0

net = ip_network('235.86.56.0/255.255.248.0', 0)

for ip in net:
    ip2 = f'{ip:b}'

    if ip2[-2:] == '11':
        k += 1

print(k)

Срез ip2[-2:] берёт два последних символа двоичной строки.

Подходят адреса, у которых этот срез равен строке '11'.

Ответ: 512

Видео разбор

Так-же все основные прототипы представлены в видео разборе задания