#!/bin/bash
# guard.sh — per-IP лимиты коннектов/SYN в ядре. Идемпотентно (пересобирает цепочку).
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; source "$SELF/../defender.conf"

iptables -D INPUT -p tcp --dport $PORT -j $GUARD 2>/dev/null || true
iptables -F $GUARD 2>/dev/null || true
iptables -N $GUARD 2>/dev/null || true
iptables -F $GUARD

# ongoing sessions — не трогаем
iptables -A $GUARD -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN

# per-IP кэп одновременных коннектов
iptables -A $GUARD -p tcp --syn -m connlimit --connlimit-above $CONNLIMIT --connlimit-mask 32 \
    -j REJECT --reject-with tcp-reset

# per-IP rate-limit новых SYN
iptables -A $GUARD -p tcp --syn -m hashlimit \
    --hashlimit-name ${SETNAME}_syn --hashlimit-mode srcip \
    --hashlimit-above ${SYN_RATE}/second --hashlimit-burst $SYN_BURST \
    --hashlimit-htable-expire 60000 -j DROP

# ИСПРАВЛЕНИЕ #4: глобальный лимит новых SYN на порт.
# Защита от распределённого флуда: даже если каждый IP укладывается в SYN_RATE,
# суммарный поток новых соединений всё равно режется на уровне ядра.
# Значения: 500 новых коннектов/сек, burst 1000 — подберите под реальный трафик.
iptables -A $GUARD -p tcp --syn \
    -m limit --limit 500/second --limit-burst 1000 -j RETURN
iptables -A $GUARD -p tcp --syn -j DROP

iptables -A $GUARD -j RETURN

iptables -I INPUT 1 -p tcp --dport $PORT -j $GUARD
echo "[guard] TCP: connlimit>$CONNLIMIT reset, syn>${SYN_RATE}/s burst $SYN_BURST drop, global SYN cap 500/s"

# ==========================================================================
# UDP-guard — отдельная цепочка на боевой UDP-порт.
# Раньше все лимиты висели только на TCP (--dport $PORT), а UDP-порт был вне
# периметра: бан по TCP не блокировал тот же IP на UDP, и пакетный флуд по UDP
# ничем не ограничивался. connlimit/conntrack к UDP неприменимы (нет соединений),
# поэтому режем по частоте пакетов: per-IP hashlimit + глобальный кэп на порт.
# ==========================================================================
UDP_GUARD="${GUARD}_UDP"

iptables -D INPUT -p udp --dport $UDP_PORT -j $UDP_GUARD 2>/dev/null || true
iptables -F $UDP_GUARD 2>/dev/null || true
iptables -N $UDP_GUARD 2>/dev/null || true
iptables -F $UDP_GUARD

# per-IP rate-limit пакетов
iptables -A $UDP_GUARD -p udp -m hashlimit \
    --hashlimit-name ${SETNAME}_udp --hashlimit-mode srcip \
    --hashlimit-above ${UDP_RATE}/second --hashlimit-burst $UDP_BURST \
    --hashlimit-htable-expire 60000 -j DROP

# глобальный кэп пакетов на порт (анти-распределённый флуд).
# Модуль limit не умеет скорости выше ~10k/сек (внутренний счётчик в jiffies),
# поэтому используем hashlimit БЕЗ srcip — тогда лимит общий на всю цепочку.
# --hashlimit-above задаёт «сверх сколько дропать», поэтому логика инвертирована:
# пакеты выше глобального капа дропаем сразу, остальное уходит в RETURN ниже.
iptables -A $UDP_GUARD -p udp -m hashlimit \
    --hashlimit-name ${SETNAME}_udpg \
    --hashlimit-above ${UDP_GLOBAL_RATE}/second --hashlimit-burst $UDP_GLOBAL_BURST \
    --hashlimit-htable-expire 60000 -j DROP

iptables -A $UDP_GUARD -j RETURN

iptables -I INPUT 1 -p udp --dport $UDP_PORT -j $UDP_GUARD
echo "[guard] UDP: pkt>${UDP_RATE}/s burst $UDP_BURST drop, global cap ${UDP_GLOBAL_RATE}/s"
