#!/bin/bash
# Script to tune network stack and interface settings
# Usage: iface-tuning.sh [interface]
# If no interface is specified, the script uses the default gateway interface.

set -euo pipefail

# Maximum per-socket RX/TX buffer to apply
BUFFERS=4096

# --- Find default interface if none given ---
DEFAULTIF=$(ip route show default 2>/dev/null | awk '{print $5}' | head -n1 || true)
IFACE=${1:-"$DEFAULTIF"}

if [[ -z "$IFACE" ]]; then
    echo "Error: No interface specified and no default route found." >&2
    exit 1
fi

if ! command -v ethtool >/dev/null; then
    echo "Error: ethtool not found, cannot tune NIC." >&2
    exit 1
fi

echo "[INFO] Using interface: $IFACE"

# --- Apply sysctl settings (temporary until reboot) ---
sysctl --load - >/dev/null 2>&1 <<'EOF'
# --- Listen / backlog ---
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_syncookies = 1

# --- Socket buffers ---
net.ipv4.tcp_rmem = 4096 262144 536870912
net.ipv4.tcp_wmem = 4096 262144 536870912
net.core.rmem_max = 536870912
net.core.wmem_max = 536870912
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.optmem_max = 65536
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_sack = 1

# --- Backlog / processing ---
net.core.netdev_max_backlog = 500000
net.core.netdev_budget = 60000

# --- Ephemeral ports / TIME_WAIT ---
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_max_tw_buckets = 200000

# --- VM / kernel memory ---
vm.max_map_count = 1048576
vm.overcommit_memory = 1
vm.swappiness = 1

# --- IPC / shared memory ---
kernel.shmmax = 68719476736
EOF

echo "[INFO] Applied sysctl settings"

# --- Detect physical cores per socket ---
SOCKETS=$(lscpu -p=SOCKET | grep -v '^#' | sort -u | wc -l)
PHYSICAL_CORES=$(lscpu -p=CORE,SOCKET | grep -v '^#' | sort -u | wc -l)
CORES_PER_SOCKET=$(( PHYSICAL_CORES / SOCKETS ))

echo "[INFO] $CORES_PER_SOCKET physical cores per socket (used for NIC queues)"

# --- Get RX/TX buffer limits from NIC ---
RX_MAX=$(ethtool -g "$IFACE" 2>/dev/null | awk '/RX:/ {print $2; exit}' || echo 0)
TX_MAX=$(ethtool -g "$IFACE" 2>/dev/null | awk '/TX:/ {print $2; exit}' || echo 0)

# Scale buffers per queue
RX=$(( RX_MAX < BUFFERS ? RX_MAX : BUFFERS ))
TX=$(( TX_MAX < BUFFERS ? TX_MAX : BUFFERS ))

echo "[INFO] Setting buffers: RX=$RX, TX=$TX"

# --- Apply NIC settings ---
ethtool -G "$IFACE" rx "$RX" tx "$TX" >/dev/null 2>&1 || echo "[WARN] Failed to set RX/TX buffers"
ethtool -K "$IFACE" gro on gso on tso on lro on >/dev/null 2>&1 || echo "[WARN] Failed to set offload options"
ethtool -C "$IFACE" rx-usecs 50 tx-usecs 50 rx-frames 0 tx-frames 0 rx-usecs-irq 25 rx-frames-irq 0 tx-usecs-irq 25 tx-frames-irq 0 >/dev/null 2>&1 || echo "[WARN] Failed to set coalescing"
ethtool -L "$IFACE" combined "$CORES_PER_SOCKET" >/dev/null 2>&1 || echo "[WARN] Failed to set queue count"

echo "[INFO] NIC tuning complete on $IFACE"
