111 lines
4.1 KiB
Bash
111 lines
4.1 KiB
Bash
#!/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
|