net/tools/subnet-calc.sh

111 lines
4.1 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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