#!/usr/bin/env bash # ═══════════════════════════════════════════════════════════════════════════ # tc/netem Network Profile Manager for Tactical Shooter # # Applies tc qdisc netem rules to simulate real-world network conditions # on the game server's port. Requires root (sudo). # # Usage: # sudo ./netem_profiles.sh apply lan # 0% loss, near-zero latency # sudo ./netem_profiles.sh apply dsl # 0.5% loss, 20ms ±5ms # sudo ./netem_profiles.sh apply cellular # 2% loss, 60ms ±20ms # sudo ./netem_profiles.sh apply satellite # 1% loss, 600ms ±50ms # sudo ./netem_profiles.sh apply congested # 5% loss, 100ms ±40ms, reorder # sudo ./netem_profiles.sh apply warzone # 10% loss, 200ms ±60ms, burst # sudo ./netem_profiles.sh apply extreme # 25% loss, 500ms ±100ms # sudo ./netem_profiles.sh apply edge # 50% loss, 1000ms ±200ms (catastrophic) # sudo ./netem_profiles.sh off # Remove all netem rules # sudo ./netem_profiles.sh status # Show current rules # # Requires: iproute2 (tc), root, IFACE (default: eth0), PORT (default: 7777) # ═══════════════════════════════════════════════════════════════════════════ set -euo pipefail # ─── Config ──────────────────────────────────────────────────────────────── IFACE="${NETEM_IFACE:-eth0}" PORT="${NETEM_PORT:-7777}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ─── Color ───────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # ─── Help / Usage ────────────────────────────────────────────────────────── usage() { echo "Usage: $0 {apply |off|status}" echo "" echo "Profiles:" echo " lan 0% loss, 1ms latency" echo " dsl 0.5% loss, 20ms ± 5ms jitter" echo " cellular 2% loss, 60ms ±20ms jitter" echo " satellite 1% loss, 600ms ±50ms jitter" echo " congested 5% loss, 100ms ±40ms jitter, reorder 5%" echo " warzone 10% loss, 200ms ±60ms jitter, 1% dupe, reorder 2%" echo " extreme 25% loss, 500ms ±100ms jitter, reorder 10%" echo " edge 50% loss, 1000ms ±200ms jitter (catastrophic)" exit 1 } # ─── Profile definitions ─────────────────────────────────────────────────── declare -A PROFILES # Format: loss lat jitter reorder reorder_corr dup dup_corr # loss: percentage (0-100) # lat: one-way latency in ms # jitter: ±ms # reorder: percentage of packets reordered # reorder_corr: correlation factor for reorder (0-100) # dup: duplicate percentage # dup_corr: correlation factor for duplicate PROFILES["lan"]="loss 0% delay 1ms 0ms" PROFILES["dsl"]="loss 0.5% delay 20ms 5ms" PROFILES["cellular"]="loss 2% delay 60ms 20ms" PROFILES["satellite"]="loss 1% delay 600ms 50ms" PROFILES["congested"]="loss 5% delay 100ms 40ms reorder 5% 50%" PROFILES["warzone"]="loss 10% delay 200ms 60ms 25% duplicate 1% 25%" PROFILES["extreme"]="loss 25% delay 500ms 100ms reorder 10% 50%" PROFILES["edge"]="loss 50% delay 1000ms 200ms" # ─── Apply netem to a specific port via iptables + tc mirror ─────────────── apply_netem() { local profile_name="$1" local profile_params="${PROFILES[$profile_name]:-}" if [ -z "$profile_params" ]; then echo -e "${RED}Unknown profile: $profile_name${NC}" usage fi echo -e "${CYAN}Applying netem profile: ${YELLOW}$profile_name${NC}" echo -e " Interface: $IFACE Port: $PORT" echo -e " Parameters: $profile_params" echo "" # Remove existing qdisc on the root sudo tc qdisc del dev "$IFACE" root 2>/dev/null || true # Apply netem to ALL traffic on the interface (targeted filtering is complex) # To target specific ports, you'd use iptables + ifb mirroring, but for # dev/testing, applying to the full interface is sufficient when run on # a dedicated test machine. sudo tc qdisc add dev "$IFACE" root netem $profile_params echo -e "${GREEN}✓ Profile '$profile_name' applied${NC}" show_status } # ─── Remove all netem rules ──────────────────────────────────────────────── remove_netem() { echo -e "${YELLOW}Removing all netem rules on $IFACE...${NC}" sudo tc qdisc del dev "$IFACE" root 2>/dev/null || true echo -e "${GREEN}✓ Netem rules removed (interface returned to normal)${NC}" } # ─── Show current status ─────────────────────────────────────────────────── show_status() { echo "" echo -e "${CYAN}Current qdisc on $IFACE:${NC}" sudo tc qdisc show dev "$IFACE" 2>/dev/null || echo " (no qdisc configured — interface is clean)" echo "" echo -e "${CYAN}Filter by port $PORT (iptables):${NC}" sudo iptables -L netem -n 2>/dev/null || echo " (no netem iptables chain)" } # ─── Main ────────────────────────────────────────────────────────────────── if [ $# -lt 1 ]; then usage fi case "$1" in apply) if [ $# -lt 2 ]; then echo -e "${RED}Error: 'apply' requires a profile name${NC}" echo "" usage fi apply_netem "$2" ;; off|remove|clear) remove_netem ;; status) show_status ;; *) usage ;; esac