Subnetting и CIDR — как делить IP-пространство
В прошлом уроке мы увидели, что IP-адрес — 32 бита, и что классов A/B/C недостаточно для разумного использования. Сейчас поговорим о том, как современные сети делят адресное пространство гибко: с помощью subnet masks и CIDR-нотации.
Это самая «арифметическая» часть курса networking. Считать subnet’ы вы будете каждый день: разрабатывая VPC в облаке, создавая namespace в Kubernetes, настраивая VPN. Понимание того, что значит /24, /16, /27 — базовое умение. Без него вы будете гадать, поместится ли ваш кластер в 10.0.0.0/22 или нет.
Что такое подсеть
Подсеть (subnet) — это группа IP-адресов с общим префиксом. Все адреса в подсети считаются «соседями» — между ними работает прямая L2-связность (через свитч), и пакеты не нужно маршрутизировать.
Пример: подсеть 192.168.1.0/24 — это адреса от 192.168.1.0 до 192.168.1.255. Их 256, из них:
192.168.1.0— адрес сети (Network address). Не назначается хосту.192.168.1.255— broadcast-адрес. Не назначается хосту.192.168.1.1—192.168.1.254— доступны для хостов. 254 полезных адреса.
Обозначение /24 называется CIDR-префиксом. Оно говорит, что первые 24 бита — это сетевая часть, а последние 8 бит — хостовая.
Subnet mask — альтернативная нотация
До CIDR использовали subnet mask — 32-битное число, в котором единицы стоят на месте сетевой части, нули — на месте хостовой. Для /24 маска 255.255.255.0:
11111111 11111111 11111111 00000000
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
255 255 255 0
^^^^^^^^^^^^^^^^^^^^^^^^^^
24 единицы = /24
Соответствие маски и CIDR-префикса:
| CIDR | Маска | Хостов |
|---|---|---|
| /8 | 255.0.0.0 | 16,777,214 |
| /16 | 255.255.0.0 | 65,534 |
| /24 | 255.255.255.0 | 254 |
| /25 | 255.255.255.128 | 126 |
| /26 | 255.255.255.192 | 62 |
| /27 | 255.255.255.224 | 30 |
| /28 | 255.255.255.240 | 14 |
| /29 | 255.255.255.248 | 6 |
| /30 | 255.255.255.252 | 2 |
| /32 | 255.255.255.255 | 1 (single host) |
Формула: для /N доступно 2^(32-N) - 2 адресов (минус network и broadcast).
В современных конфигах увидите оба формата:
# IP с маской через CIDR:
ip addr add 192.168.1.5/24 dev eth0
# То же с явной маской (старый стиль):
ifconfig eth0 192.168.1.5 netmask 255.255.255.0
# В AWS / GCP конфиге используется CIDR:
{ "vpc_cidr": "10.0.0.0/16", "subnet_cidr": "10.0.1.0/24" }
CIDR — современный стандарт. Маски — legacy. Если видите в туториале netmask 255.255.255.0, переводите в голове в /24.
Как посчитать диапазон подсети
Самая частая задача: дан CIDR-блок, какие в нём адреса? Например, 10.20.30.0/22. Сколько хостов? Где network, где broadcast?
Алгоритм:
- Найти размер подсети. Для
/22:2^(32-22) = 2^10 = 1024адреса. Минус 2 — получаем1022полезных хостов. - Найти, какие октеты затрагивает префикс. /22 = 16 (full first 2 octets) + 6 бит из третьего октета. Значит граница в третьем октете.
- Найти шаг (block size). В третьем октете 6 бит сетевой части и 2 бита хостовой. Шаг =
2^2 = 4. То есть подсети идут с шагом 4 в третьем октете:10.20.0.0/22,10.20.4.0/22,10.20.8.0/22, … - Найти, в какой подсети наш адрес.
10.20.30.0— значит30 / 4 = 7.5, ближайшее кратное 4 снизу —28. Значит подсеть10.20.28.0/22. То есть исходное10.20.30.0/22неточное — сеть фактически начинается с10.20.28.0. - Network и broadcast. Network =
10.20.28.0. Broadcast = network + (размер - 1) =10.20.28.0 + 1024 - 1 = 10.20.31.255. Полезный диапазон:10.20.28.1 - 10.20.31.254.
В Python это считается одной командой:
import ipaddress
net = ipaddress.IPv4Network('10.20.30.0/22', strict=False)
print(net.network_address) # 10.20.28.0
print(net.broadcast_address) # 10.20.31.255
print(net.num_addresses) # 1024
print(list(net.hosts())[:3]) # [IPv4Address('10.20.28.1'), '10.20.28.2', '10.20.28.3']
print(net.netmask) # 255.255.252.0
Параметр strict=False нужен, если адрес в строке — не network address (как в нашем примере: .30.0 не выровнен на границу /22).
Запомните несколько ключевых блок-сайзов, и большинство расчётов будут в голове:
- /30 — 4 адреса (2 полезных). Для point-to-point links.
- /29 — 8 (6 полезных). Мелкая подсеть.
- /28 — 16 (14). Для маленького отдела.
- /27 — 32 (30). Средний офис.
- /24 — 256 (254). Стандарт home/small office.
- /16 — 65,536. VPC в AWS обычно /16 или /20.
CIDR — что это, откуда и зачем
CIDR (Classless Inter-Domain Routing, RFC 1519, 1993) — это отказ от классов A/B/C в пользу произвольных префиксов. До CIDR:
- Если у вас 500 хостов, вам выдавали /16 (65K адресов).
- 64,500 адресов просто пропадали.
С CIDR:
- 500 хостов — /23 (512 адресов). Минимум потерь.
- 1000 хостов — /22.
- 100 хостов — /25.
CIDR также позволяет агрегацию (supernetting) в маршрутизации. Например, у провайдера 16 блоков /24 подряд от 10.20.0.0/24 до 10.20.15.0/24. Вместо 16 записей в routing table можно положить одну: 10.20.0.0/20 (включает всё). Это сильно уменьшает размер глобальных routing tables.
Разделение /24 на меньшие подсети — пример
У вас есть 192.168.1.0/24 (256 адресов). Хотите разделить на 4 равные части.
Шаги:
- 4 подсети =
2^2, значит нужно «отдать» 2 бита из хостовой части в сетевую. Было /24, стало /26. - Каждая /26 =
2^(32-26) = 2^6 = 64адреса. - Шаг между подсетями — 64 в последнем октете.
Получаем:
| Подсеть | Network | Broadcast | Хосты |
|---|---|---|---|
| /26 #1 | 192.168.1.0 | 192.168.1.63 | .1 - .62 |
| /26 #2 | 192.168.1.64 | 192.168.1.127 | .65 - .126 |
| /26 #3 | 192.168.1.128 | 192.168.1.191 | .129 - .190 |
| /26 #4 | 192.168.1.192 | 192.168.1.255 | .193 - .254 |
Каждая подсеть теперь имеет 62 полезных адреса. Достаточно для 4 отдельных команд / VLAN-ов / окружений.
В Python:
import ipaddress
parent = ipaddress.IPv4Network('192.168.1.0/24')
for subnet in parent.subnets(new_prefix=26):
print(f'{subnet} | hosts: {subnet.network_address + 1} - {subnet.broadcast_address - 1}')
# 192.168.1.0/26 | hosts: 192.168.1.1 - 192.168.1.62
# 192.168.1.64/26 | hosts: 192.168.1.65 - 192.168.1.126
# 192.168.1.128/26 | hosts: 192.168.1.129 - 192.168.1.190
# 192.168.1.192/26 | hosts: 192.168.1.193 - 192.168.1.254
Это работает для любого CIDR-блока. Хотите разделить /22 (1024 адреса) на 4 части по /24 — parent.subnets(new_prefix=24).
Variable-length subnetting (VLSM)
CIDR позволяет не только равные, но и неравные подсети. Это называется VLSM (Variable Length Subnet Masking). Идея: у вас в офисе есть отделы разного размера, незачем всем давать одинаковый кусок.
Пример: дано 10.0.0.0/22 (1024 адреса). Нужно:
- VLAN dev: 500 хостов
- VLAN ops: 100 хостов
- VLAN guests: 50 хостов
- VLAN management: 10 хостов
Решение (берём подсети от больших к меньшим):
| VLAN | Нужно | Размер | CIDR | Network | Broadcast |
|---|---|---|---|---|---|
| dev | 500 | 512 | /23 | 10.0.0.0 | 10.0.1.255 |
| ops | 100 | 128 | /25 | 10.0.2.0 | 10.0.2.127 |
| guests | 50 | 64 | /26 | 10.0.2.128 | 10.0.2.191 |
| mgmt | 10 | 16 | /28 | 10.0.2.192 | 10.0.2.207 |
Остаток: 10.0.2.208 - 10.0.3.255 = 304 адреса свободно для будущих VLAN.
VLSM — стандартная практика в дизайне VPC в облаках, в офисных сетях, в провайдерских инфраструктурах.
Longest prefix match — как роутер выбирает маршрут
Когда роутер получает пакет, он смотрит в routing table и выбирает запись с самым длинным префиксом, которая покрывает dst IP. Это правило называется longest prefix match.
Пример routing table:
10.0.0.0/8 via 192.168.100.1
10.0.5.0/24 via 192.168.100.2
10.0.5.100/32 via 192.168.100.3
Пакет идёт на 10.0.5.100:
- Подходят все три записи.
- Самый длинный префикс —
/32. - Пакет идёт через
192.168.100.3.
Пакет идёт на 10.0.5.200:
- Подходят
/8и/24./32не подходит (это другой адрес). - Самый длинный —
/24. - Пакет идёт через
192.168.100.2.
Пакет идёт на 10.5.5.5:
- Подходит только
/8. - Пакет идёт через
192.168.100.1.
Этот принцип везде — от маленького домашнего роутера до BGP в глобальном интернете. Об этом будет урок «routing basics» в модуле 6.
Default gateway и /0
0.0.0.0/0 — особенный префикс. Покрывает все возможные IP. Это так называемый default route — куда отправлять пакеты, если ни одна более специфичная запись не подходит.
# Linux routing table:
ip route show
# default via 192.168.1.1 dev eth0 proto dhcp
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.42
# 169.254.0.0/16 dev eth0 scope link
# default -- это псевдоним для 0.0.0.0/0
Когда вы пингуете 8.8.8.8:
8.8.8.8не входит в192.168.1.0/24(не локальная сеть).- Не входит в
169.254.0.0/16. - Падает на
default— идёт через192.168.1.1.
Без default gateway машина может ходить только по локальной сети. Интернет недоступен.
Попробуй сам
# 1. Посмотреть свои подсети:
ip addr show
# inet 192.168.1.42/24 ... -- ваш IP и маска
# 2. Посмотреть routing table:
ip route show
# Вы увидите вашу подсеть и default gateway
# 3. Посчитать подсеть в Python:
python3 -c "
import ipaddress
net = ipaddress.IPv4Network('10.0.0.0/22', strict=False)
print(f'Network: {net.network_address}')
print(f'Broadcast: {net.broadcast_address}')
print(f'Hosts: {net.num_addresses - 2}')
print(f'Mask: {net.netmask}')
"
# 4. Поделить подсеть на 8 равных:
python3 -c "
import ipaddress
parent = ipaddress.IPv4Network('10.0.0.0/24')
for s in parent.subnets(prefixlen_diff=3):
print(s)
"
# Вывод: 8 подсетей /27 по 32 адреса каждая
# 5. Проверить, входит ли IP в подсеть:
python3 -c "
import ipaddress
ip = ipaddress.IPv4Address('192.168.1.100')
net = ipaddress.IPv4Network('192.168.1.0/24')
print(ip in net)
"
# 6. Сколько / какой CIDR нужен для N хостов:
# 100 хостов -> минимум /25 (126 полезных)
# 1000 хостов -> минимум /22 (1022 полезных)
# Формула: bits = ceil(log2(N+2)), prefix = 32-bits
# 7. Полезный калькулятор онлайн (для проверки):
# https://www.calculator.net/ip-subnet-calculator.html
Полезно вручную пройтись по нескольким /24 -> /26 разбиениям, чтобы прочувствовать арифметику. После 5-10 примеров будете считать в голове.