diff --git a/tools/traffic-generator.sh b/tools/traffic-generator.sh new file mode 100755 index 0000000..8bab0de --- /dev/null +++ b/tools/traffic-generator.sh @@ -0,0 +1,371 @@ +#!/usr/bin/env bash +set -euo pipefail + +TARGET="" +IFACE="" +REQUESTS=50 +INTERVAL_MS=50 +STOP_ON_KEYPRESS=0 +PROTOCOLS_CSV="all" +DNS_PORT=53 +HTTP_PORT=8080 +HTTPS_PORT=8443 +TCP_ECHO_PORT=9000 +UDP_ECHO_PORT=9001 +IPERF_TCP_PORT=5201 +IPERF_UDP_PORT=5202 + +STOP=0 +MAIN_PID=$$ +KEYPRESS_PID="" + +usage() { + cat < --iface [options] + +Required: + --target Target host running services or receiving probes + --iface Source interface (used by arping/nping where applicable) + +Optional: + --protocols Comma-separated protocol set or 'all' + Available: arp,icmp,http,https,dns,tcp_echo,udp_echo, + tcp_syn,tcp_flags,udp_raw,traceroute, + iperf_tcp,iperf_udp,ssh_probe,ftp_probe, + smtp_probe,ntp_probe,mdns_query + Default: all + --requests Requests per selected protocol (default: 50) + --interval-ms Delay between requests in ms (default: 50) + --stop-on-keypress Stop loop when any key is pressed + + --dns-port DNS port (default: 53) + --http-port HTTP port (default: 8080) + --https-port HTTPS port (default: 8443) + --tcp-echo-port TCP echo/custom port (default: 9000) + --udp-echo-port UDP echo/custom port (default: 9001) + --iperf-tcp-port iPerf TCP server port (default: 5201) + --iperf-udp-port iPerf UDP server port (default: 5202) + +Examples: + $0 --target 10.0.0.2 --iface enp3s0 --protocols icmp,http,https,dns --requests 100 + $0 --target 10.0.0.2 --iface enp3s0 --protocols all --requests 500 --stop-on-keypress +USAGE +} + +log() { + printf '[%s] %s\n' "$(date +'%H:%M:%S')" "$*" +} + +has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +sleep_ms() { + local ms="$1" + local sec=$((ms / 1000)) + local rem=$((ms % 1000)) + sleep "$(printf '%s.%03d' "$sec" "$rem")" +} + +maybe_wait() { + if [[ "$INTERVAL_MS" -gt 0 ]]; then + sleep_ms "$INTERVAL_MS" + fi +} + +start_keypress_listener() { + if [[ "$STOP_ON_KEYPRESS" -ne 1 ]]; then + return + fi + + if [[ ! -t 0 ]]; then + log "--stop-on-keypress requested, but no interactive TTY detected; ignoring" + STOP_ON_KEYPRESS=0 + return + fi + + trap 'STOP=1' SIGUSR1 + log "Press any key to stop traffic generation" + ( + while [[ "$STOP" -eq 0 ]]; do + IFS= read -r -s -n 1 _key /dev/null 2>&1 || true + fi +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --target) + TARGET="${2:-}" + shift 2 + ;; + --iface) + IFACE="${2:-}" + shift 2 + ;; + --protocols) + PROTOCOLS_CSV="${2:-all}" + shift 2 + ;; + --requests) + REQUESTS="${2:-50}" + shift 2 + ;; + --interval-ms) + INTERVAL_MS="${2:-50}" + shift 2 + ;; + --stop-on-keypress) + STOP_ON_KEYPRESS=1 + shift + ;; + --dns-port) + DNS_PORT="${2:-53}" + shift 2 + ;; + --http-port) + HTTP_PORT="${2:-8080}" + shift 2 + ;; + --https-port) + HTTPS_PORT="${2:-8443}" + shift 2 + ;; + --tcp-echo-port) + TCP_ECHO_PORT="${2:-9000}" + shift 2 + ;; + --udp-echo-port) + UDP_ECHO_PORT="${2:-9001}" + shift 2 + ;; + --iperf-tcp-port) + IPERF_TCP_PORT="${2:-5201}" + shift 2 + ;; + --iperf-udp-port) + IPERF_UDP_PORT="${2:-5202}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 1 + ;; + esac + done +} + +assert_inputs() { + if [[ -z "$TARGET" || -z "$IFACE" ]]; then + usage + exit 1 + fi + + if ! [[ "$REQUESTS" =~ ^[0-9]+$ ]] || [[ "$REQUESTS" -lt 1 ]]; then + echo "--requests must be an integer >= 1" >&2 + exit 1 + fi + + if ! [[ "$INTERVAL_MS" =~ ^[0-9]+$ ]]; then + echo "--interval-ms must be an integer >= 0" >&2 + exit 1 + fi +} + +run_arp() { + has_cmd arping || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + arping -I "$IFACE" -c 1 "$TARGET" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_icmp() { + has_cmd ping || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + ping -c 1 -W 1 "$TARGET" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_http() { + has_cmd curl || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + curl -s "http://${TARGET}:${HTTP_PORT}/" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_https() { + has_cmd curl || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + curl -sk "https://${TARGET}:${HTTPS_PORT}/" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_dns() { + if has_cmd dig; then + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + dig @"$TARGET" -p "$DNS_PORT" example.com A +tries=1 +time=1 +short >/dev/null 2>&1 || true + maybe_wait + done + fi +} + +run_tcp_echo() { + has_cmd nc || return 0 + for i in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + printf 'tcp-echo-%s\n' "$i" | nc -w1 "$TARGET" "$TCP_ECHO_PORT" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_udp_echo() { + has_cmd nc || return 0 + for i in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + printf 'udp-echo-%s\n' "$i" | nc -u -w1 "$TARGET" "$UDP_ECHO_PORT" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_tcp_syn() { + if has_cmd nping; then + nping --interface "$IFACE" --tcp -p "$TCP_ECHO_PORT" --flags syn -c "$REQUESTS" "$TARGET" >/dev/null 2>&1 || true + fi +} + +run_tcp_flags() { + if has_cmd nping; then + nping --interface "$IFACE" --tcp -p "$TCP_ECHO_PORT" --flags syn,ack,fin,rst,psh,urg -c "$REQUESTS" "$TARGET" >/dev/null 2>&1 || true + fi +} + +run_udp_raw() { + if has_cmd nping; then + nping --interface "$IFACE" --udp -p "$UDP_ECHO_PORT" --data-length 256 -c "$REQUESTS" "$TARGET" >/dev/null 2>&1 || true + fi +} + +run_traceroute() { + has_cmd traceroute || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + traceroute -n -m 5 "$TARGET" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_iperf_tcp() { + has_cmd iperf3 || return 0 + iperf3 -c "$TARGET" -p "$IPERF_TCP_PORT" -t 10 >/dev/null 2>&1 || true +} + +run_iperf_udp() { + has_cmd iperf3 || return 0 + iperf3 -c "$TARGET" -p "$IPERF_UDP_PORT" -u -b 100M -t 10 >/dev/null 2>&1 || true +} + +run_port_probe() { + local port="$1" + has_cmd nc || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + nc -z -w1 "$TARGET" "$port" >/dev/null 2>&1 || true + maybe_wait + done +} + +run_ssh_probe() { run_port_probe 22; } +run_ftp_probe() { run_port_probe 21; } +run_smtp_probe() { run_port_probe 25; } +run_ntp_probe() { + has_cmd nc || return 0 + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + printf 'ntp?\n' | nc -u -w1 "$TARGET" 123 >/dev/null 2>&1 || true + maybe_wait + done +} +run_mdns_query() { + if has_cmd dig; then + for _ in $(seq 1 "$REQUESTS"); do + [[ "$STOP" -eq 1 ]] && return + dig @224.0.0.251 -p 5353 _services._dns-sd._udp.local PTR +tries=1 +time=1 +short >/dev/null 2>&1 || true + maybe_wait + done + fi +} + +dispatch_protocol() { + local proto="$1" + log "Running protocol: ${proto}" + case "$proto" in + arp) run_arp ;; + icmp) run_icmp ;; + http) run_http ;; + https) run_https ;; + dns) run_dns ;; + tcp_echo) run_tcp_echo ;; + udp_echo) run_udp_echo ;; + tcp_syn) run_tcp_syn ;; + tcp_flags) run_tcp_flags ;; + udp_raw) run_udp_raw ;; + traceroute) run_traceroute ;; + iperf_tcp) run_iperf_tcp ;; + iperf_udp) run_iperf_udp ;; + ssh_probe) run_ssh_probe ;; + ftp_probe) run_ftp_probe ;; + smtp_probe) run_smtp_probe ;; + ntp_probe) run_ntp_probe ;; + mdns_query) run_mdns_query ;; + *) + log "Unknown protocol '${proto}', skipping" + ;; + esac +} + +main() { + parse_args "$@" + assert_inputs + + local protocols=(arp icmp http https dns tcp_echo udp_echo tcp_syn tcp_flags udp_raw traceroute iperf_tcp iperf_udp ssh_probe ftp_probe smtp_probe ntp_probe mdns_query) + + if [[ "$PROTOCOLS_CSV" != "all" ]]; then + IFS=',' read -r -a protocols <<<"$PROTOCOLS_CSV" + fi + + start_keypress_listener + trap cleanup EXIT + + log "Target=${TARGET}, iface=${IFACE}, requests=${REQUESTS}, protocols=${PROTOCOLS_CSV}, stop_on_keypress=${STOP_ON_KEYPRESS}" + for proto in "${protocols[@]}"; do + [[ "$STOP" -eq 1 ]] && break + dispatch_protocol "$proto" + done + + log "Traffic generation complete" +} + +main "$@" diff --git a/tools/traffic-target.sh b/tools/traffic-target.sh new file mode 100755 index 0000000..62103bf --- /dev/null +++ b/tools/traffic-target.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +set -euo pipefail + +HTTP_PORT=8080 +HTTPS_PORT=8443 +TCP_ECHO_PORT=9000 +UDP_ECHO_PORT=9001 +IPERF_TCP_PORT=5201 +IPERF_UDP_PORT=5202 +DNS_PORT=5353 +WORKDIR="/tmp/mitm-traffic-target" +CERT_DAYS=2 + +PIDS=() + +usage() { + cat < HTTP port (default: 8080) + --https-port HTTPS port (default: 8443) + --tcp-echo-port TCP echo port (default: 9000) + --udp-echo-port UDP echo port (default: 9001) + --iperf-tcp-port iPerf TCP server port (default: 5201) + --iperf-udp-port iPerf UDP server port (default: 5202) + --dns-port Lightweight DNS UDP port (default: 5353) + --workdir Working directory (default: /tmp/mitm-traffic-target) + --help Show this help + +Notes: + - DNS service is a lightweight UDP responder using socat (not full DNS). + - For DNS on port 53, run as root/cap_net_bind_service or choose high port and set generator --dns-port. +USAGE +} + +log() { + printf '[%s] %s\n' "$(date +'%H:%M:%S')" "$*" +} + +has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +need_cmds() { + local missing=0 + for cmd in "$@"; do + if ! has_cmd "$cmd"; then + echo "Missing command: $cmd" >&2 + missing=1 + fi + done + if [[ "$missing" -ne 0 ]]; then + echo "Install missing tools and retry." >&2 + exit 1 + fi +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --http-port) + HTTP_PORT="${2:-8080}"; shift 2 ;; + --https-port) + HTTPS_PORT="${2:-8443}"; shift 2 ;; + --tcp-echo-port) + TCP_ECHO_PORT="${2:-9000}"; shift 2 ;; + --udp-echo-port) + UDP_ECHO_PORT="${2:-9001}"; shift 2 ;; + --iperf-tcp-port) + IPERF_TCP_PORT="${2:-5201}"; shift 2 ;; + --iperf-udp-port) + IPERF_UDP_PORT="${2:-5202}"; shift 2 ;; + --dns-port) + DNS_PORT="${2:-5353}"; shift 2 ;; + --workdir) + WORKDIR="${2:-/tmp/mitm-traffic-target}"; shift 2 ;; + -h|--help) + usage; exit 0 ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 1 + ;; + esac + done +} + +start_bg() { + local name="$1" + shift + "$@" >"${WORKDIR}/${name}.log" 2>&1 & + local pid=$! + PIDS+=("$pid") + log "Started ${name} (pid=${pid})" +} + +cleanup() { + log 'Stopping services...' + for pid in "${PIDS[@]:-}"; do + kill "$pid" >/dev/null 2>&1 || true + done + wait >/dev/null 2>&1 || true + log "Stopped. Logs in ${WORKDIR}" +} + +create_cert() { + if [[ -f "${WORKDIR}/cert.pem" && -f "${WORKDIR}/key.pem" ]]; then + return + fi + + openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout "${WORKDIR}/key.pem" \ + -out "${WORKDIR}/cert.pem" \ + -days "$CERT_DAYS" \ + -subj '/CN=mitm-traffic-target.local' >/dev/null 2>&1 +} + +main() { + parse_args "$@" + + need_cmds python3 openssl socat + if ! has_cmd iperf3; then + log 'iperf3 not found, iperf services will be skipped' + fi + + mkdir -p "$WORKDIR" + trap cleanup EXIT INT TERM + + create_cert + + log "Workdir: ${WORKDIR}" + log 'Starting target services...' + + start_bg http python3 -m http.server "$HTTP_PORT" --directory "$WORKDIR" + start_bg https openssl s_server -quiet -accept "$HTTPS_PORT" -cert "${WORKDIR}/cert.pem" -key "${WORKDIR}/key.pem" + + start_bg tcp_echo socat "TCP-LISTEN:${TCP_ECHO_PORT},reuseaddr,fork" SYSTEM:'cat' + start_bg udp_echo socat "UDP-LISTEN:${UDP_ECHO_PORT},reuseaddr,fork" SYSTEM:'cat' + + if has_cmd iperf3; then + start_bg iperf_tcp iperf3 -s -p "$IPERF_TCP_PORT" + start_bg iperf_udp iperf3 -s -p "$IPERF_UDP_PORT" + fi + + # Lightweight DNS-like UDP responder to generate DNS-shaped traffic. + # It echoes fixed bytes and is intended only for packet-generation tests. + start_bg dns_dummy socat "UDP-LISTEN:${DNS_PORT},reuseaddr,fork" SYSTEM:'printf "\\x81\\x80"' + + cat <