#!/usr/bin/env bash
# srt-http-net-tuning.sh
# Kombiniertes UDP/SRT- und TCP/HTTP-Tuning für hohe Bitraten (MPTS) und viele Verbindungen.
# Usage:
#   sudo bash srt-http-net-tuning.sh apply                 # schreibt Sysctl-Tunings und lädt sie
#   sudo bash srt-http-net-tuning.sh restore               # entfernt die Tunings
#   sudo bash srt-http-net-tuning.sh show                  # zeigt relevante Kernel-/TCP-/UDP-Werte
#   sudo bash srt-http-net-tuning.sh limits-apply          # setzt hohe ulimit (FD) systemweit
#   sudo bash srt-http-net-tuning.sh limits-restore        # entfernt FD-Overrides
#   sudo bash srt-http-net-tuning.sh ethtool-tune eth0     # (optional) RSS/Coalescing/MTU für Mellanox
#
# Hinweis:
# - Werte sind konservativ für Systeme mit >=32 GiB RAM gewählt. Bei weniger RAM ggf. *_default kleiner wählen.
# - Für BBR Congestion Control siehe Kommentar in apply_sysctl().

set -euo pipefail

SYSCTL_FILE="/etc/sysctl.d/99-srt-http-net-tuning.conf"
FD_LIMITS_FILE="/etc/security/limits.d/99-high-fd.conf"
NOW="$(date +%Y%m%d%H%M%S)"

backup_file() {
  local f="$1"
  [[ -f "$f" ]] && cp -a "$f" "${f}.bak.${NOW}" && echo "Backup: ${f}.bak.${NOW}" || true
}

apply_sysctl() {
  local CONTENT
  CONTENT="$(cat <<'EOF'
#############################################
# High-Throughput Tuning: SRT/UDP + HTTP/TCP
# Ziel: viele Verbindungen, 10–40+ Gbps stabil
#############################################

##### Core Socket Buffers (Bytes)
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.core.rmem_default = 67108864
net.core.wmem_default = 67108864

##### UDP/SRT
# udp_mem: pages (min / pressure / max). Für viel RAM; ggf. kleiner wählen.
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384

##### TCP/HTTP Fenster & Puffer (Bytes)
net.ipv4.tcp_rmem = 4096 87380 268435456
net.ipv4.tcp_wmem = 4096 65536 268435456

##### Backlogs und Queues
net.core.netdev_max_backlog = 300000
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 16384

##### TIME-WAIT / Reconnect-Last
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

##### Keepalives: tote Verbindungen schneller erkennen
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

##### Ephemeral Ports für viele ausgehende Verbindungen
net.ipv4.ip_local_port_range = 10000 65000

##### SACK aktiv lassen; ECN optional (nur wenn Pfad/Peers es können)
net.ipv4.tcp_sack = 1
# net.ipv4.tcp_ecn = 1

##### (Optional) Moderne Staukontrolle (BBR). Nur aktivieren, wenn gewünscht:
# net.core.default_qdisc = fq
# net.ipv4.tcp_congestion_control = bbr
EOF
)"
  if [[ -f "$SYSCTL_FILE" ]]; then
    if diff -q <(printf "%s\n" "$CONTENT") "$SYSCTL_FILE" >/dev/null 2>&1; then
      echo "Sysctl bereits aktuell: $SYSCTL_FILE"
    else
      backup_file "$SYSCTL_FILE"
      printf "%s\n" "$CONTENT" > "$SYSCTL_FILE"
      echo "Sysctl aktualisiert: $SYSCTL_FILE"
    fi
  else
    printf "%s\n" "$CONTENT" > "$SYSCTL_FILE"
    echo "Sysctl erstellt: $SYSCTL_FILE"
  fi

  # Laden
  if command -v systemctl >/dev/null 2>&1; then
    sysctl --system >/dev/null
  else
    sysctl -p "$SYSCTL_FILE" >/dev/null
  fi
  echo "Sysctl-Werte geladen."
}

remove_sysctl() {
  if [[ -f "$SYSCTL_FILE" ]]; then
    rm -f "$SYSCTL_FILE"
    echo "Entfernt: $SYSCTL_FILE"
    if command -v systemctl >/dev/null 2>&1; then
      sysctl --system >/devnull 2>&1 || true
    else
      sysctl -p >/dev/null 2>&1 || true
    fi
    echo "Systemweite Defaults neu geladen (soweit vorhanden)."
  else
    echo "Nichts zu entfernen: $SYSCTL_FILE existiert nicht."
  fi
}

show_values() {
  echo "=== Aktuelle Kern- und TCP/UDP-Werte ==="
  # Kern
  sysctl -n net.core.rmem_max net.core.wmem_max net.core.rmem_default net.core.wmem_default \
           net.core.netdev_max_backlog net.core.somaxconn | awk '
    BEGIN{keys[1]="net.core.rmem_max";keys[2]="net.core.wmem_max";keys[3]="net.core.rmem_default";keys[4]="net.core.wmem_default";keys[5]="net.core.netdev_max_backlog";keys[6]="net.core.somaxconn"}
    {print keys[NR]": "$0}
  '
  echo "udp_mem: $(sysctl -n net.ipv4.udp_mem)"
  echo
  # TCP
  echo "tcp_rmem: $(sysctl -n net.ipv4.tcp_rmem)"
  echo "tcp_wmem: $(sysctl -n net.ipv4.tcp_wmem)"
  echo "tcp_max_syn_backlog: $(sysctl -n net.ipv4.tcp_max_syn_backlog)"
  echo "tcp_tw_reuse: $(sysctl -n net.ipv4.tcp_tw_reuse)"
  echo "tcp_fin_timeout: $(sysctl -n net.ipv4.tcp_fin_timeout)"
  echo "ip_local_port_range: $(sysctl -n net.ipv4.ip_local_port_range)"
  echo "tcp_keepalive_time/intvl/probes: $(sysctl -n net.ipv4.tcp_keepalive_time) / $(sysctl -n net.ipv4.tcp_keepalive_intvl) / $(sysctl -n net.ipv4.tcp_keepalive_probes)"
  # Drops/Stats
  echo
  echo "=== UDP/TCP-Statistiken (falls verfügbar) ==="
  if command -v netstat >/dev/null 2>&1; then
    netstat -su | grep -E 'Udp:|packet receive errors|receive buffer errors|send buffer errors' || true
    netstat -st | grep -E 'segments retransmited|retransmit|TCP' || true
  else
    echo "(netstat nicht gefunden)"
  fi
}

apply_limits() {
  local CONTENT
  CONTENT="$(cat <<'EOF'
# Hohe File-Descriptor-Limits für viele HTTP/SRT-Verbindungen
* soft nofile 262144
* hard nofile 262144
root soft nofile 262144
root hard nofile 262144
EOF
)"
  if [[ -f "$FD_LIMITS_FILE" ]]; then
    if diff -q <(printf "%s\n" "$CONTENT") "$FD_LIMITS_FILE" >/dev/null 2>&1; then
      echo "FD-Limits bereits aktuell: $FD_LIMITS_FILE"
    else
      backup_file "$FD_LIMITS_FILE"
      printf "%s\n" "$CONTENT" > "$FD_LIMITS_FILE"
      echo "FD-Limits aktualisiert: $FD_LIMITS_FILE"
    fi
  else
    printf "%s\n" "$CONTENT" > "$FD_LIMITS_FILE"
    echo "FD-Limits erstellt: $FD_LIMITS_FILE"
  fi
  echo "Hinweis: Systemd-Services brauchen meist einen LimitNOFILE-Override."
  echo "Beispiel: mkdir -p /etc/systemd/system/astrad.service.d && printf '[Service]\nLimitNOFILE=262144\n' > /etc/systemd/system/astrad.service.d/override.conf && systemctl daemon-reload && systemctl restart astrad"
}

remove_limits() {
  if [[ -f "$FD_LIMITS_FILE" ]]; then
    rm -f "$FD_LIMITS_FILE"
    echo "Entfernt: $FD_LIMITS_FILE"
  else
    echo "Nichts zu entfernen: $FD_LIMITS_FILE existiert nicht."
  fi
}

# Sehr vorsichtige, generische Mellanox-Optimierung (ConnectX-3 Pro):
# - Setzt 16 combined Queues (wenn erlaubt)
# - Moderates Interrupt-Coalescing
# - Jumbo MTU 9000 (nur anwenden, wenn End-to-End unterstützt)
ethtool_tune() {
  local IFACE="${1:-}"
  if [[ -z "$IFACE" ]]; then
    echo "Usage: $0 ethtool-tune <iface>"; exit 1
  fi
  if ! command -v ethtool >/dev/null 2>&1; then
    echo "ethtool nicht gefunden. Installiere es (apt install ethtool) und wiederhole den Aufruf."
    exit 1
  fi
  echo ">>> RSS/Queues erhöhen (falls unterstützt) ..."
  if ethtool -l "$IFACE" 2>/dev/null | grep -q 'Combined'; then
    sudo ethtool -L "$IFACE" combined 16 || true
  fi
  echo ">>> Interrupt Coalescing moderat setzen ..."
  sudo ethtool -C "$IFACE" rx-usecs 25 rx-frames 64 tx-usecs 25 tx-frames 64 || true
  echo ">>> Offloads prüfen ..."
  ethtool -k "$IFACE" | grep -E 'gro|gso|tso|lro' || true
  echo ">>> (Optional) Jumbo MTU 9000 setzen – nur wenn End-to-End unterstützt!"
  echo "    Beispiel-Befehl (manuell ausführen, wenn gewünscht): ip link set dev $IFACE mtu 9000"
}

post_hint() {
  cat <<'HINT'

Hinweise & nächste Schritte:
- Die Sysctl-Tunings sind aktiv. Prüfe unter Last:
    netstat -su           # UDP-Drops
    netstat -st           # TCP Retransmits
    ss -n state established | wc -l   # Anzahl TCP-Verbindungen
- Für SRT: Latenz/Timeout anpassen (z. B. latency=200, timeout ~10–15s).
- Für HTTP: Stelle sicher, dass dein Dienst (Astra/HAProxy/NGINX) hohe FD-Limits hat (LimitNOFILE).
- Optional: RSS/Queues/Coalescing der Mellanox-Karte per 'ethtool-tune <iface>' justieren.
- Optional (wenn gewünscht): BBR aktivieren (siehe auskommentierte Zeilen in der Sysctl-Datei).
HINT
}

cmd="${1:-apply}"
case "$cmd" in
  apply)
    apply_sysctl
    show_values
    post_hint
    ;;
  restore)
    remove_sysctl
    show_values
    ;;
  show)
    show_values
    ;;
  limits-apply)
    apply_limits
    ;;
  limits-restore)
    remove_limits
    ;;
  ethtool-tune)
    ethtool_tune "${2:-}"
    ;;
  *)
    echo "Usage: $0 {apply|restore|show|limits-apply|limits-restore|ethtool-tune <iface>}"
    exit 1
    ;;
esac
