From a02dbea6d8b19f9fe1fa04d327f0f6244d2a7364 Mon Sep 17 00:00:00 2001 From: kimoi Date: Fri, 6 Nov 2026 16:46:46 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B8=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20(tools)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/subnet-calc.sh | 110 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tools/subnet-calc.sh diff --git a/tools/subnet-calc.sh b/tools/subnet-calc.sh new file mode 100644 index 0000000..8f3616c --- /dev/null +++ b/tools/subnet-calc.sh @@ -0,0 +1,110 @@ +#!/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