Реализация VLSM (Variable Length Subnet Mask) на Python: сортировка подсетей по убыванию, выравнивание по границам и обработка переполнения. Попробуйте код!
При планировании сети часто требуется разделить блок CIDR на подсети разного размера. Например, головному офису нужно 100 адресов, филиалу А — 50, филиалу Б — 25, а DMZ — 10. Если делить на равные части, адреса будут расходоваться неэффективно. VLSM (Variable Length Subnet Masking) позволяет выделять каждой подсети свою маску, минимизируя потери. В этой статье мы разберём алгоритм, который лежит в основе VLSM-калькулятора.
Для каждой подсети в VLSM-плане требуется:
Первая задача решается простой формулой. Вторая — сортировкой требований по убыванию и последовательной упаковкой в адресное пространство.
В IPv4 количество полезных хостов в подсети = общее количество адресов минус 2 (адрес сети и широковещательный адрес). Нужно найти наименьшее n (биты хостов) такое, что 2^n - 2 >= запрошенных хостов.
import math
def ceil_log2(x):
return math.ceil(math.log2(x))
def vlsm_host_bits(hosts):
host_bits = ceil_log2(hosts + 2)
subnet_prefix = 32 - host_bits
subnet_size = 2 ** host_bits
return host_bits, subnet_prefix, subnet_sizeПередача hosts + 2 в ceil_log2 сразу учитывает вычет адреса сети и широковещательного адреса, не требуя отдельной обработки.
Стандартный подход для VLSM — размещать сначала самые крупные подсети. Если упаковывать от меньших к большим, после размещения крупной подсети могут остаться фрагменты, не кратные её размеру, что приведёт к потере адресов.
def calculate_vlsm(network_num, prefix, parent_total, requirements):
# Сортируем требования по убыванию
sorted_reqs = sorted(requirements, reverse=True)
current_num = network_num
subnets = []
for hosts in sorted_reqs:
host_bits, subnet_prefix, subnet_size = vlsm_host_bits(hosts)
# Выравнивание по границе подсети
aligned_num = ((current_num + subnet_size - 1) // subnet_size) * subnet_size
if aligned_num + subnet_size - 1 > network_num + parent_total - 1:
raise ValueError("Подсети не помещаются в родительскую сеть")
broadcast_num = aligned_num + subnet_size - 1
subnets.append({
'network': num_to_ip(aligned_num),
'mask': prefix_to_mask(subnet_prefix),
'broadcast': num_to_ip(broadcast_num),
'first_host': num_to_ip(aligned_num + 1),
'last_host': num_to_ip(broadcast_num - 1),
'usable_hosts': subnet_size - 2,
'total_addresses': subnet_size,
'prefix': subnet_prefix
})
current_num = aligned_num + subnet_size
return subnetsПодсети CIDR могут начинаться только с адреса, кратного их размеру. Например, /23 (512 адресов) может начинаться только с адреса, делящегося на 512. Следующая строка находит ближайшую допустимую границу:
aligned_num = ((current_num + subnet_size - 1) // subnet_size) * subnet_sizeДеление current_num (адрес сразу после предыдущей подсети) на subnet_size, округление вверх и умножение обратно даёт ближайший выровненный адрес, не меньший current_num.
Если выровненный адрес выходит за пределы родительской сети, алгоритм должен немедленно сообщить об ошибке, а не умалчивать проблему:
if aligned_num + subnet_size - 1 > network_num + parent_total - 1:
raise ValueError("Подсети не помещаются в родительскую сеть")Выравнивание по границам создаёт зазоры, поэтому даже если сумма хостов меньше общего числа адресов, план может не поместиться. Явная ошибка позволяет сразу понять, что требования несовместимы.
Алгоритм VLSM несложен, но важно правильно реализовать три вещи: сортировку по убыванию, выравнивание по границам и явное сообщение об ошибке при переполнении. Попробуйте написать свою реализацию на Python и протестируйте на разных наборах требований. Если хотите увидеть готовый инструмент, загляните в Torinoa Subnet Calculator.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →