Add uprobe_ssl_write_entry/uretprobe_ssl_write_exit to capture server HTTP
responses via SSL_write with direction=1. Implement full HPACK decoder
(RFC 7541 static table, multi-byte integers, literal representations) for
HTTP/2 header extraction. Add AcceptCache mapping {tgid,fd}→SessionKey
from accept4 events as authoritative source for SSL correlation when BPF
ssl_conn_map has src_ip=0. Add ip_total_length to tcp_syn_event BPF struct.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
142 lines
5.4 KiB
Bash
Executable File
142 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# generate-traffic.sh — Generate HTTPS/HTTP traffic from a VM endpoint
|
|
#
|
|
# Called by run-e2e-test.sh via:
|
|
# vagrant ssh $vm -- "source /tmp/e2e-traffic.env && bash /ja4-platform/tests/vm/generate-traffic.sh"
|
|
#
|
|
# Environment variables (from /tmp/e2e-traffic.env):
|
|
# HITS — Number of HTTPS requests (required)
|
|
# HITS_HTTP — Number of HTTP requests (default: 0)
|
|
# TARGET_IPS — Space-separated list of endpoint IPs (required)
|
|
# SNI_HOSTS — Space-separated list of SNI hostnames (required)
|
|
# TLS_FLAGS — curl TLS flags e.g. "--tlsv1.2 --tlsv1.3" (required)
|
|
# SRC_IP_COUNT — Number of source IPs to rotate (default: 1)
|
|
# =============================================================================
|
|
set -uo pipefail
|
|
|
|
HITS="${HITS:-0}"
|
|
HITS_HTTP="${HITS_HTTP:-0}"
|
|
TARGET_IPS=(${TARGET_IPS:-})
|
|
SNI_HOSTS=(${SNI_HOSTS:-platform.test})
|
|
TLS_FLAGS="${TLS_FLAGS:---tlsv1.2 --tlsv1.3}"
|
|
SRC_IP_COUNT="${SRC_IP_COUNT:-1}"
|
|
|
|
if [ "$HITS" -eq 0 ] && [ "$HITS_HTTP" -eq 0 ]; then
|
|
echo "0/0"
|
|
exit 0
|
|
fi
|
|
|
|
# ── Collect source IPs from eth0 ──
|
|
SRC_IPS=($(ip -4 addr show eth0 2>/dev/null | awk '/inet / {sub(/\/.*/, "", $2); print $2}'))
|
|
if [ ${#SRC_IPS[@]} -eq 0 ]; then
|
|
echo "0/${HITS}" > /dev/stderr
|
|
exit 1
|
|
fi
|
|
|
|
# ── User-Agent pools ──
|
|
UA_BROWSER=(
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36"
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15"
|
|
"Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"
|
|
)
|
|
UA_BOT=(
|
|
"python-requests/2.32.3"
|
|
"curl/8.9.1"
|
|
"Go-http-client/2.0"
|
|
"python-httpx/0.28.1"
|
|
"Googlebot/2.1"
|
|
)
|
|
PATHS=("/" "/health" "/data" "/api/users" "/api/v1/status" "/api/v1/metrics" \
|
|
"/login" "/logout" "/api/search" "/static/main.js" "/static/style.css" \
|
|
"/favicon.ico" "/robots.txt" "/sitemap.xml" "/api/v2/data" "/admin")
|
|
|
|
ok=0
|
|
err=0
|
|
|
|
# ── HTTPS traffic ──
|
|
if [ "$HITS" -gt 0 ]; then
|
|
for i in $(seq 1 "$HITS"); do
|
|
idx=$((i - 1))
|
|
target_ip="${TARGET_IPS[$((idx % ${#TARGET_IPS[@]}))]}"
|
|
sni_host="${SNI_HOSTS[$((idx % ${#SNI_HOSTS[@]}))]}"
|
|
path="${PATHS[$((idx % ${#PATHS[@]}))]}"
|
|
|
|
# Rotate methods: GET(50%), POST(20%), PUT(10%), DELETE(10%), HEAD(10%)
|
|
case $((i % 10)) in
|
|
0|1|2|3|4) method="GET" ;;
|
|
5|6) method="POST" ;;
|
|
7) method="PUT" ;;
|
|
8) method="DELETE" ;;
|
|
9) method="HEAD" ;;
|
|
esac
|
|
|
|
# 70% browser UA, 30% bot UA
|
|
if [ $((i % 10)) -lt 7 ]; then
|
|
ua="${UA_BROWSER[$((idx % ${#UA_BROWSER[@]}))]}"
|
|
else
|
|
ua="${UA_BOT[$((idx % ${#UA_BOT[@]}))]}"
|
|
fi
|
|
|
|
# Build curl flags
|
|
resolve_flag="--resolve ${sni_host}:443:${target_ip}"
|
|
extra_flags="${resolve_flag} ${TLS_FLAGS}"
|
|
|
|
# Rotate source IPs if multiple are available
|
|
if [ ${#SRC_IPS[@]} -gt 1 ] && [ "$SRC_IP_COUNT" -gt 1 ]; then
|
|
src_ip="${SRC_IPS[$((idx % SRC_IP_COUNT))]}"
|
|
if [ -n "$src_ip" ]; then
|
|
extra_flags="${extra_flags} --interface ${src_ip}"
|
|
fi
|
|
fi
|
|
|
|
case $method in
|
|
POST)
|
|
curl -sf -k ${extra_flags} -X POST "https://${sni_host}${path}" \
|
|
-H "User-Agent: ${ua}" -H "Content-Type: application/json" \
|
|
-d '{"test":1,"seq":'$i'}' \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok=$((ok + 1)) || err=$((err + 1)) ;;
|
|
PUT)
|
|
curl -sf -k ${extra_flags} -X PUT "https://${sni_host}${path}" \
|
|
-H "User-Agent: ${ua}" \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok=$((ok + 1)) || err=$((err + 1)) ;;
|
|
DELETE)
|
|
curl -sf -k ${extra_flags} -X DELETE "https://${sni_host}${path}" \
|
|
-H "User-Agent: ${ua}" \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok=$((ok + 1)) || err=$((err + 1)) ;;
|
|
HEAD)
|
|
curl -sf -k ${extra_flags} -I "https://${sni_host}${path}" \
|
|
-H "User-Agent: ${ua}" \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok=$((ok + 1)) || err=$((err + 1)) ;;
|
|
*)
|
|
curl -sf -k ${extra_flags} "https://${sni_host}${path}" \
|
|
-H "User-Agent: ${ua}" \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok=$((ok + 1)) || err=$((err + 1)) ;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
# ── HTTP traffic (port 80) ──
|
|
ok_http=0
|
|
if [ "$HITS_HTTP" -gt 0 ]; then
|
|
for i in $(seq 1 "$HITS_HTTP"); do
|
|
idx=$((i - 1))
|
|
# Round-robin across target IPs for HTTP too
|
|
target_ip="${TARGET_IPS[$((idx % ${#TARGET_IPS[@]}))]}"
|
|
path="${PATHS[$((idx % ${#PATHS[@]}))]}"
|
|
|
|
# HTTP: use target_ip directly (no --resolve needed for HTTP)
|
|
curl -sf "http://${target_ip}${path}" \
|
|
--connect-timeout 5 --max-time 10 \
|
|
>/dev/null 2>&1 && ok_http=$((ok_http + 1)) || true
|
|
done
|
|
fi
|
|
|
|
# Output: HTTPS_ok/HTTPS_total HTTP_ok/HTTP_total
|
|
echo "${ok}/${HITS} ${ok_http}/${HITS_HTTP}" |