#!/bin/bash
# monitor.sh — снимает полный срез атаки каждые $INTERVAL сек в Logs/. Крутится в tmux.
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; source "$SELF/../defender.conf"

mkdir -p "$LOGDIR"
LOG="$LOGDIR/attack-$(date +%Y%m%d).log"
CSV="$LOGDIR/metrics-$(date +%Y%m%d).csv"
SRV_PID=$(ss -tlnp 2>/dev/null | grep ":$PORT" | grep -oP 'pid=\K[0-9]+' | head -1)

[ -f "$CSV" ] || echo "ts,total,syn_recv,estab,close_wait,time_wait,uniq_ips,new_conn_s,rx_kbps,tx_kbps,pps_in,srv_cpu,srv_mem,srv_threads,acceptq,ban_reject_s,syn_drop_s,ipset_drop_s,ipset_size,conntrack" > "$CSV"

read RX0 TX0 < <(awk -v i=$IFACE '$0~i{gsub(/.*:/,"");print $1, $9}' /proc/net/dev)
PKT0=$(awk -v i=$IFACE '$0~i{gsub(/.*:/,"");print $2}' /proc/net/dev)
prev_reject=0; prev_drop=0; prev_ipset=0

echo "[monitor] -> $LOG + $CSV (каждые ${INTERVAL}с)"
while true; do
  TS=$(date '+%Y-%m-%d %H:%M:%S')
  SNAP=$(ss -tn "( sport = :$PORT )" 2>/dev/null)
  total=$(echo "$SNAP" | grep -c "$PORT")
  syn_recv=$(ss -tn state syn-recv "( sport = :$PORT )" 2>/dev/null | grep -c "$PORT")
  estab=$(echo "$SNAP" | grep -c ESTAB)
  cwait=$(echo "$SNAP" | grep -c CLOSE-WAIT)
  twait=$(ss -tn state time-wait "( sport = :$PORT )" 2>/dev/null | grep -c "$PORT")
  IPS=$(echo "$SNAP" | awk 'NR>1{split($5,a,":");print a[1]}' | grep -E '^[0-9]')
  uniq_ips=$(echo "$IPS" | sort -u | grep -c .)
  TOP=$(echo "$IPS" | sort | uniq -c | sort -rn | head -15)
  TOP_NET=$(echo "$IPS" | awk -F. '{print $1"."$2".0.0/16"}' | sort | uniq -c | sort -rn | head -8)

  read RX1 TX1 < <(awk -v i=$IFACE '$0~i{gsub(/.*:/,"");print $1, $9}' /proc/net/dev)
  PKT1=$(awk -v i=$IFACE '$0~i{gsub(/.*:/,"");print $2}' /proc/net/dev)
  rx_kbps=$(( (RX1-RX0)*8/1000/INTERVAL )); tx_kbps=$(( (TX1-TX0)*8/1000/INTERVAL ))
  pps_in=$(( (PKT1-PKT0)/INTERVAL )); RX0=$RX1; TX0=$TX1; PKT0=$PKT1

  if [ -n "$SRV_PID" ] && [ -d /proc/$SRV_PID ]; then
    read srv_cpu srv_mem < <(ps -o %cpu=,%mem= -p $SRV_PID | awk '{print $1, $2}')
    srv_threads=$(ls /proc/$SRV_PID/task 2>/dev/null | wc -l)
  else
    srv_cpu=DEAD; srv_mem=0; srv_threads=0; SRV_PID=$(ss -tlnp 2>/dev/null | grep ":$PORT" | grep -oP 'pid=\K[0-9]+' | head -1)
  fi
  acceptq=$(ss -tln "( sport = :$PORT )" 2>/dev/null | awk 'NR>1{print $2; exit}')

  reject_now=$(iptables -L $GUARD -v -n -x 2>/dev/null | awk '/connlimit/{print $1;exit}'); reject_now=${reject_now:-0}
  drop_now=$(iptables -L $GUARD -v -n -x 2>/dev/null | awk '/hashlimit/{print $1;exit}'); drop_now=${drop_now:-0}
  reject_s=$(( (reject_now-prev_reject)/INTERVAL )); [ $reject_s -lt 0 ] && reject_s=0
  drop_s=$(( (drop_now-prev_drop)/INTERVAL )); [ $drop_s -lt 0 ] && drop_s=0
  prev_reject=$reject_now; prev_drop=$drop_now
  ipset_now=$(iptables -L INPUT -v -n -x 2>/dev/null | awk '/match-set '"$SETNAME"'/{print $1;exit}'); ipset_now=${ipset_now:-0}
  ipset_drop_s=$(( (ipset_now-prev_ipset)/INTERVAL )); [ $ipset_drop_s -lt 0 ] && ipset_drop_s=0
  prev_ipset=$ipset_now
  ipset_size=$(ipset list $SETNAME 2>/dev/null | grep -c '^[0-9]')
  ct=$(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null || echo 0)

  {
    echo "======================================================================"
    echo "[$TS]  port $PORT"
    echo "CONNS  total=$total  syn-recv=$syn_recv  estab=$estab  close-wait=$cwait  time-wait=$twait  uniq-ip=$uniq_ips"
    echo "TRAFFIC  in=${rx_kbps}kbit/s  out=${tx_kbps}kbit/s  pkts-in=${pps_in}/s"
    echo "SERVER  cpu=${srv_cpu}%  mem=${srv_mem}%  threads=$srv_threads  accept-queue=$acceptq"
    echo "FIREWALL  connlimit-reset=${reject_s}/s  syn-drop=${drop_s}/s  ipset-drop=${ipset_drop_s}/s  ipset-bans=$ipset_size  conntrack=$ct"
    if [ "$total" -gt 0 ]; then
      echo "-- top source IPs (conns) --"; echo "$TOP"
      echo "-- top /16 subnets --"; echo "$TOP_NET"
    else
      echo "-- нет активных коннектов (флуд, если есть, режется ipset'ом до коннекта) --"
    fi
  } >> "$LOG"

  echo "$TS,$total,$syn_recv,$estab,$cwait,$twait,$uniq_ips,$((syn_recv/INTERVAL)),$rx_kbps,$tx_kbps,$pps_in,$srv_cpu,$srv_mem,$srv_threads,$acceptq,$reject_s,$drop_s,$ipset_drop_s,$ipset_size,$ct" >> "$CSV"
  sleep $INTERVAL
done
