#!/usr/bin/env bash # benchmark_improvements.sh # Tests each DPI-resistance improvement independently against baseline. # Metrics: avg/P95 latency, jitter (std dev), download Mbps, upload Mbps. set -euo pipefail XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64" SOCKS_PORT=11083 REMOTE_IP="83.99.190.32" VENV="/home/alvis/ai-xray/venv" RESULTS_FILE="/home/alvis/ai-xray/improvement_results.md" LATENCY_SAMPLES=20 # Current config values PUBLIC_KEY="58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw" PRIVATE_KEY="KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY" UUID_B="6e422ab5-070a-43f6-8241-38cd56d23d24" SID_B="6036d37d12c443c4" XHTTP_PATH="/xt-6036d37d" DEST="www.delfi.lv:443" SNI="www.delfi.lv" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' hdr() { echo ""; echo -e "${BOLD}${CYAN}══ $1 ══${NC}"; } pass() { echo -e " ${GREEN}✓${NC} $1"; } fail() { echo -e " ${RED}✗${NC} $1"; } info() { echo -e " ${YELLOW}→${NC} $1"; } XRAY_PID="" cleanup() { if [[ -n "$XRAY_PID" ]]; then kill "$XRAY_PID" 2>/dev/null || true wait "$XRAY_PID" 2>/dev/null || true XRAY_PID="" fi } trap 'cleanup; rm -f /tmp/bench-cfg.json' EXIT # ── Remote inbound updater ───────────────────────────────────────────────── remote_update() { local stream_json="$1" local uuid="$2" source "$VENV/bin/activate" python3 << PYEOF import requests, json import urllib3; urllib3.disable_warnings() s = requests.Session(); s.verify = False BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR" s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"}) stream = $stream_json client = {"id": "$uuid", "flow": "", "email": "bench", "limitIp": 0, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": "", "subId": "", "comment": ""} payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443, "listen": "", "protocol": "vless", "settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}), "streamSettings": json.dumps(stream), "sniffing": json.dumps({"enabled": False}), "remark": "inbound-443", "expiryTime": 0} r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload) ok = r.json().get("success") print(f" Remote updated: {ok}") PYEOF } restore_remote() { remote_update '{ "network": "xhttp", "security": "reality", "realitySettings": { "show": False, "dest": "'"$DEST"'", "serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"], "privateKey": "'"$PRIVATE_KEY"'", "shortIds": ["'"$SID_B"'", "48b4c16249ad44ff", ""] }, "xhttpSettings": { "path": "'"$XHTTP_PATH"'", "host": "", "mode": "auto", "extra": {"xPaddingBytes": "100-1000", "xmux": { "maxConcurrency": "16-32", "maxConnections": 0, "cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0, "hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}} } }' "$UUID_B" sleep 2 } # ── Core benchmark runner ────────────────────────────────────────────────── bench() { local label="$1" local cfg_file="$2" # Validate if ! "$XRAY_BIN" -test -c "$cfg_file" &>/dev/null; then fail "Config invalid — skipping" echo "$label|INVALID|—|—|—|—|—" >> /tmp/bench.tsv return fi # Start xray cleanup "$XRAY_BIN" -c "$cfg_file" >/tmp/bench-xray.log 2>&1 & XRAY_PID=$! sleep 2 if ! kill -0 "$XRAY_PID" 2>/dev/null; then fail "Xray failed to start" echo "$label|FAIL|—|—|—|—|—" >> /tmp/bench.tsv return fi # Connectivity check local exit_ip exit_ip=$(curl -s --socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 15 \ https://api.ipify.org 2>/dev/null || echo "FAIL") if [[ "$exit_ip" != "$REMOTE_IP" ]]; then fail "No connectivity (exit IP: $exit_ip)" cleanup echo "$label|NO-CONN|—|—|—|—|—" >> /tmp/bench.tsv return fi pass "Connected via $exit_ip" # Latency — N samples info "Latency ($LATENCY_SAMPLES samples)..." local lats=() for i in $(seq 1 $LATENCY_SAMPLES); do local ms ms=$(curl -s -o /dev/null -w "%{time_total}" --socks5-hostname 127.0.0.1:$SOCKS_PORT \ --max-time 8 https://www.gstatic.com/generate_204 2>/dev/null \ | awk '{printf "%d", $1*1000}') if [[ -n "$ms" && "$ms" -gt 0 ]]; then lats+=("$ms") fi done local n=${#lats[@]} avg=0 p95=0 jitter=0 min=0 max=0 if [[ $n -gt 0 ]]; then local sorted=($(printf '%s\n' "${lats[@]}" | sort -n)) min=${sorted[0]}; max=${sorted[-1]} local sum=0; for v in "${lats[@]}"; do sum=$((sum+v)); done avg=$((sum/n)) local p95i=$((n*95/100)); [[ $p95i -ge $n ]] && p95i=$((n-1)) p95=${sorted[$p95i]} # Jitter = std dev (via python for float math) local csv_lats csv_lats=$(printf '%s,' "${lats[@]}" | sed 's/,$//') jitter=$(python3 -c " import math d=[$csv_lats] m=sum(d)/len(d) print(int(math.sqrt(sum((x-m)**2 for x in d)/len(d)))) ") fi pass "Latency: avg=${avg}ms p95=${p95}ms jitter=${jitter}ms (n=$n)" # Download 10MB info "Download 10MB..." local dl_out dl_out=$(curl -s -o /dev/null -w "%{size_download} %{time_total}" \ --socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \ "https://speed.cloudflare.com/__down?bytes=10485760" 2>/dev/null || echo "0 1") local dl_bytes dl_time dl_mbps=0 dl_bytes=$(echo "$dl_out" | awk '{print $1}') dl_time=$(echo "$dl_out" | awk '{print $2}') if [[ "${dl_bytes:-0}" -gt 1000000 ]]; then dl_mbps=$(echo "scale=1; $dl_bytes*8/$dl_time/1000000" | bc) pass "Download: ${dl_mbps} Mbps" else fail "Download failed" fi # Upload 5MB info "Upload 5MB..." local ul_out ul_out=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | \ curl -s -o /dev/null -w "%{size_upload} %{time_total}" \ --socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \ -X POST --data-binary @- https://httpbin.org/post 2>/dev/null || echo "0 1") local ul_bytes ul_time ul_mbps=0 ul_bytes=$(echo "$ul_out" | awk '{print $1}') ul_time=$(echo "$ul_out" | awk '{print $2}') if [[ "${ul_bytes:-0}" -gt 100000 ]]; then ul_mbps=$(echo "scale=1; $ul_bytes*8/$ul_time/1000000" | bc) pass "Upload: ${ul_mbps} Mbps" else fail "Upload failed" ul_mbps=0 fi echo "$label|OK|$avg|$p95|$jitter|$dl_mbps|$ul_mbps" >> /tmp/bench.tsv cleanup sleep 1 } # ══════════════════════════════════════════════════════════════════════════ # CONFIGS # ══════════════════════════════════════════════════════════════════════════ # Baseline config writer helper write_xhttp_cfg() { local fp="$1" use_frag="$2" host_hdr="$3" path="$4" sni_val="$5" local frag_sockopt="" [[ "$use_frag" == "yes" ]] && frag_sockopt='"sockopt": {"dialerProxy": "frag-chain1"},' cat > /tmp/bench-cfg.json << EOF { "log": {"loglevel": "error"}, "inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}], "outbounds": [ { "tag": "proxy", "protocol": "vless", "settings": {"vnext": [{"address": "share.alogins.net", "port": 443, "users": [{"id": "$UUID_B", "flow": "", "encryption": "none"}]}]}, "streamSettings": { $frag_sockopt "network": "xhttp", "security": "reality", "realitySettings": { "fingerprint": "$fp", "serverName": "$sni_val", "publicKey": "$PUBLIC_KEY", "shortId": "$SID_B", "spiderX": "/" }, "xhttpSettings": { "path": "$path", "host": "$host_hdr", "mode": "auto", "extra": { "xPaddingBytes": "100-1000", "xmux": {"maxConcurrency": "16-32", "maxConnections": 0, "cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0, "hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"} } } } }, { "tag": "frag-chain1", "protocol": "freedom", "settings": {"fragment": {"packets": "tlshello", "length": "100-200", "interval": "10-20"}}, "streamSettings": {"sockopt": {"dialerProxy": "frag-chain2"}} }, { "tag": "frag-chain2", "protocol": "freedom", "settings": {"fragment": {"packets": "1-3", "length": "1-5", "interval": "1-2"}} }, {"tag": "direct", "protocol": "freedom"} ] } EOF } # ══════════════════════════════════════════════════════════════════════════ # MAIN # ══════════════════════════════════════════════════════════════════════════ echo "label|status|avg_ms|p95_ms|jitter_ms|dl_mbps|ul_mbps" > /tmp/bench.tsv # Ensure baseline remote config is active info "Ensuring baseline remote config..." restore_remote # ── 0. BASELINE ──────────────────────────────────────────────────────────── hdr "0. BASELINE (current config)" write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI" bench "0-baseline" /tmp/bench-cfg.json # ── 1. fingerprint=randomized ────────────────────────────────────────────── hdr "1. fingerprint: randomized" write_xhttp_cfg "randomized" "no" "" "$XHTTP_PATH" "$SNI" bench "1-fp-randomized" /tmp/bench-cfg.json # ── 2. fingerprint=firefox ──────────────────────────────────────────────── hdr "2. fingerprint: firefox" write_xhttp_cfg "firefox" "no" "" "$XHTTP_PATH" "$SNI" bench "2-fp-firefox" /tmp/bench-cfg.json # ── 3. + TLS ClientHello fragment chain ─────────────────────────────────── hdr "3. fragment chain on TLS ClientHello" write_xhttp_cfg "chrome" "yes" "" "$XHTTP_PATH" "$SNI" bench "3-fragment-chain" /tmp/bench-cfg.json # ── 4. + host header = SNI domain ───────────────────────────────────────── hdr "4. host header = www.delfi.lv" write_xhttp_cfg "chrome" "no" "www.delfi.lv" "$XHTTP_PATH" "$SNI" bench "4-host-header" /tmp/bench-cfg.json # ── 5. realistic XHTTP path + host header (needs server update) ─────────── hdr "5. realistic path /api/v2/stream + host header" info "Updating remote path to /api/v2/stream ..." remote_update '{ "network": "xhttp", "security": "reality", "realitySettings": { "show": False, "dest": "'"$DEST"'", "serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"], "privateKey": "'"$PRIVATE_KEY"'", "shortIds": ["'"$SID_B"'", ""] }, "xhttpSettings": { "path": "/api/v2/stream", "host": "www.delfi.lv", "mode": "auto", "extra": {"xPaddingBytes": "100-1000", "xmux": { "maxConcurrency": "16-32", "maxConnections": 0, "cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0, "hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}} } }' "$UUID_B" sleep 2 write_xhttp_cfg "chrome" "no" "www.delfi.lv" "/api/v2/stream" "$SNI" bench "5-realistic-path" /tmp/bench-cfg.json restore_remote # ── 6. SNI = e-klase.lv (highest throughput in SNI test) ────────────────── hdr "6. SNI = e-klase.lv (top SNI from previous benchmark)" write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "e-klase.lv" bench "6-sni-eklase" /tmp/bench-cfg.json # ── 7. SNI = www.lmt.lv ─────────────────────────────────────────────────── hdr "7. SNI = www.lmt.lv" write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "www.lmt.lv" bench "7-sni-lmt" /tmp/bench-cfg.json # ── 8. BBR check + enable on remote ─────────────────────────────────────── hdr "8. BBR congestion control on remote server" source "$VENV/bin/activate" BBR_STATUS=$(python3 << 'PYEOF' import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("83.99.190.32", username="juris", password="VitaIeva2A.") _, out, _ = ssh.exec_command("lxc exec xray -- sysctl net.ipv4.tcp_congestion_control 2>/dev/null") cc = out.read().decode().strip() _, out2, _ = ssh.exec_command("lxc exec xray -- sysctl net.core.default_qdisc 2>/dev/null") qd = out2.read().decode().strip() print(f"cc={cc} | qd={qd}") ssh.close() PYEOF ) info "Current: $BBR_STATUS" if echo "$BBR_STATUS" | grep -q "bbr"; then info "BBR already enabled — testing as-is" write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI" bench "8-bbr-already-on" /tmp/bench-cfg.json else info "Enabling BBR on remote container..." python3 << 'PYEOF' import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("83.99.190.32", username="juris", password="VitaIeva2A.") for cmd in [ "lxc exec xray -- sysctl -w net.core.default_qdisc=fq", "lxc exec xray -- sysctl -w net.ipv4.tcp_congestion_control=bbr", "lxc exec xray -- sysctl -w net.core.rmem_max=16777216", "lxc exec xray -- sysctl -w net.core.wmem_max=16777216", ]: _, out, err = ssh.exec_command(cmd) print(f" {cmd.split('--')[1].strip()}: {(out.read()+err.read()).decode().strip()}") ssh.close() PYEOF sleep 1 write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI" bench "8-bbr-enabled" /tmp/bench-cfg.json fi # ── Restore remote to canonical state ───────────────────────────────────── hdr "Restoring remote to baseline" restore_remote pass "Remote restored" # ══════════════════════════════════════════════════════════════════════════ # SUMMARY # ══════════════════════════════════════════════════════════════════════════ hdr "RESULTS SUMMARY" echo "" printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" "Test" "Avg ms" "P95 ms" "Jitter ms" "DL Mbps" "UL Mbps" printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" "────────────────────────────────" "───────" "───────" "─────────" "───────" "───────" BASELINE_AVG=0; BASELINE_P95=0; BASELINE_JIT=0; BASELINE_DL=0; BASELINE_UL=0 while IFS='|' read -r label status avg p95 jit dl ul; do [[ "$label" == "label" ]] && continue if [[ "$status" == "OK" ]]; then if [[ "$label" == "0-baseline" ]]; then BASELINE_AVG=$avg; BASELINE_P95=$p95; BASELINE_JIT=$jit BASELINE_DL=$dl; BASELINE_UL=$ul printf "${BOLD}%-32s${NC} %-7s %-7s %-9s %-9s %-9s\n" \ "$label" "${avg}ms" "${p95}ms" "${jit}ms" "${dl}" "${ul}" else # Delta indicators local_delta_avg="" local_delta_jit="" local_delta_dl="" [[ -n "$BASELINE_AVG" && "$BASELINE_AVG" -gt 0 ]] && { diff=$((avg - BASELINE_AVG)) [[ $diff -lt 0 ]] && local_delta_avg="${GREEN}${diff}${NC}" || local_delta_avg="${RED}+${diff}${NC}" } printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" \ "$label" "${avg}ms" "${p95}ms" "${jit}ms" "${dl}" "${ul}" fi else printf "${RED}%-32s${NC} %s\n" "$label" "$status" fi done < /tmp/bench.tsv # Write markdown { cat << MDEOF # DPI Resistance Improvement Benchmark **Date**: $(date '+%Y-%m-%d %H:%M') **Baseline**: VLESS+XHTTP+Reality, fingerprint=chrome, SNI=www.delfi.lv, path=/xt-6036d37d **Latency samples per test**: $LATENCY_SAMPLES **Jitter**: standard deviation of latency samples ## Results | Test | Avg ms | P95 ms | Jitter ms | DL Mbps | UL Mbps | Notes | |------|--------|--------|-----------|---------|---------|-------| MDEOF while IFS='|' read -r label status avg p95 jit dl ul; do [[ "$label" == "label" ]] && continue notes="" case "$label" in 0-baseline) notes="Current active config" ;; 1-fp-randomized) notes="uTLS fingerprint rotated per connection" ;; 2-fp-firefox) notes="Firefox uTLS profile" ;; 3-fragment-chain) notes="TLS ClientHello split 100-200B + micro-frag 1-5B" ;; 4-host-header) notes="HTTP Host header = www.delfi.lv" ;; 5-realistic-path) notes="Path=/api/v2/stream + Host header" ;; 6-sni-eklase) notes="SNI switched to e-klase.lv" ;; 7-sni-lmt) notes="SNI switched to www.lmt.lv" ;; 8-bbr*) notes="BBR congestion control on remote" ;; esac if [[ "$status" == "OK" ]]; then echo "| $label | ${avg}ms | ${p95}ms | ${jit}ms | ${dl} | ${ul} | $notes |" else echo "| $label | — | — | — | — | — | $status |" fi done < /tmp/bench.tsv cat << 'MDEOF' ## What Each Test Changes - **fingerprint=randomized**: uTLS fingerprint rotated per connection — defeats fingerprint-based blocking - **fingerprint=firefox**: Firefox uTLS profile instead of Chrome - **fragment chain**: TLS ClientHello split into 100-200B chunks, then micro-fragmented 1-5B + noise — defeats handshake DPI - **host header**: Sets HTTP `Host:` header to match SNI — makes request look more legitimate - **realistic path**: Changes XHTTP path from synthetic to `/api/v2/stream` with matching host header - **SNI e-klase.lv / lmt.lv**: Alternative SNIs from previous benchmark (dest stays www.delfi.lv) - **BBR**: Linux BBR congestion control + larger TCP buffers on remote — improves throughput under loss MDEOF } > "$RESULTS_FILE" echo "" echo -e "${BOLD}Results: $RESULTS_FILE${NC}"