#!/bin/bash # ============================================================ # Калькулятор подсетей для экзамена 09.02.06 (python3 + ipaddress) # # ЗАПУСК: # bash tools/subnet-calc.sh <базовая_сеть> [имя=ЧИСЛО ...] # # ЧИСЛО — это АДРЕСА, а не хосты: считаются адрес сети и broadcast. # Если нужен именно объём ХОСТОВ — добавьте суффикс h, например: # srv=30 -> 30 адресов (включая сеть и broadcast) # srv=28h -> 28 хостов + 2 спецадреса = 30 адресов # # ПРИМЕР (адреса подбираются последовательно из базовой сети): # bash tools/subnet-calc.sh 10.0.0.0/24 hq_srv=32 hq_cli=16 mgmt=8 br_srv=16 br_link=2 # # Для туннеля и линка BR-FW<->BR-RTR достаточно 2 адресов хостов: # link=2 -> маска /30 (4 адреса: сеть, 2 хоста, broadcast) # # Пример вывода готовой таблицы — просто скопировать в отчёт (таблица 2). # ============================================================ if ! command -v python3 >/dev/null 2>&1; then echo "Нужен python3. Установите: dnf install -y python3" exit 1 fi python3 - "$@" <<'PY' import sys import ipaddress def usage_and_exit(): print("Использование: subnet-calc.sh <базовая_сеть> [имя=число_адресов ...]") print("Суффикс h означает число ХОСТОВ (например srv=28h -> 28 хостов).") sys.exit(1) def parse_size(value): value = value.strip().lower() hosts = value.endswith("h") if hosts: value = value[:-1] try: count = int(value) except ValueError: usage_and_exit() if count < 1: usage_and_exit() need = count + 2 if hosts else count return count, need def main(): if len(sys.argv) < 2: usage_and_exit() try: base = ipaddress.ip_network(sys.argv[1], strict=False) except ValueError: print("Некорректная базовая сеть:", sys.argv[1]) usage_and_exit() print("Базовая сеть:", base, "| всего адресов:", base.num_addresses) print() cursor = int(base.network_address) last = int(base.broadcast_address) rows = [] overflow = False for item in sys.argv[2:]: name, sep, raw = item.partition("=") if not sep: print("Ошибка: параметр без '=' ->", item) usage_and_exit() count, need = parse_size(raw) prefix = base.max_prefixlen - (need - 1).bit_length() # Выравниваем начало подсети по границе новой маски step = 1 << (base.max_prefixlen - prefix) cursor = (cursor + step - 1) // step * step net = ipaddress.ip_network((cursor, prefix), strict=False) if int(net.broadcast_address) > last: overflow = True rows.append([ name, str(net.network_address) + "/" + str(prefix), str(net.netmask), str(net.network_address + 1) if need > 2 else str(net.network_address), str(net.broadcast_address - 1) if need > 2 else str(net.broadcast_address), str(net.broadcast_address), str(net.num_addresses), ]) cursor = int(net.broadcast_address) + 1 if overflow: print("ВНИМАНИЕ: подсетей не хватило внутри базовой сети!") print() headers = ["Назначение", "Сеть", "Маска", "Первый хост", "Последний хост", "Broadcast", "Адресов"] widths = [max(len(headers[i]), max(len(r[i]) for r in rows)) for i in range(len(headers))] line = "-+-".join("-" * w for w in widths) print(" | ".join(h.ljust(w) for h, w in zip(headers, widths))) print(line) for row in rows: print(" | ".join(c.ljust(w) for c, w in zip(row, widths))) main() PY