diff options
Diffstat (limited to 'test-env/net-sim')
| -rwxr-xr-x | test-env/net-sim/netsim.sh | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/test-env/net-sim/netsim.sh b/test-env/net-sim/netsim.sh new file mode 100755 index 0000000..8b0f68e --- /dev/null +++ b/test-env/net-sim/netsim.sh @@ -0,0 +1,489 @@ +#!/bin/bash +# +# Network Simulator for Hoods Gate Testing +# Isolated network environment with fake internet responses +# + +# Configuration +STATE_DIR="/tmp/netsim" +STATE_FILE="${STATE_DIR}/state" +NETNS_CLIENT="netsim_client" +NETNS_EXIT="netsim_exit" +NETNS_INTERNET="netsim_internet" + +# Network Configuration +BRIDGE_CLIENT="br-client" +BRIDGE_EXIT="br-exit" +BRIDGE_WAN="br-wan" # Bridge for WAN side connection +VETH_CLIENT_HOST="veth-c-host" +VETH_CLIENT_NS="veth-c-ns" +VETH_EXIT_HOST="veth-e-host" +VETH_EXIT_NS="veth-e-ns" +VETH_INET_EXIT="veth-i-exit" +VETH_INET_NS="veth-i-ns" +EXIT_WAN_IFACE="wan0" # Interface name inside exit KVM + +# IP Configuration +IP_CLIENT_BRIDGE="10.10.1.1/24" +IP_CLIENT_VM="10.10.1.10/24" +IP_EXIT_BRIDGE="10.10.2.1/24" +IP_EXIT_VM="10.10.2.10/24" +IP_EXIT_WAN="203.0.113.10/24" # WAN IP for exit node +IP_INET_GATEWAY="203.0.113.1/24" # Gateway in internet namespace + +RED=$'\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC=$'\033[0m' + +check_root() { + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Error: This script must be run as root${NC}" + exit 1 + fi +} + +fail() { + echo -e "${RED}$2${NC}" >&2 + exit $1 +} + +usage() { + cat << EOF + ▄▄▄ ▄▄ ██ + ███ ██ ██ ▀▀ + ██▀█ ██ ▄████▄ ███████ ▄▄█████▄ ████ ████▄██▄ + ██ ██ ██ ██▄▄▄▄██ ██ ██▄▄▄▄ ▀ ██ ██ ██ ██ + ██ █▄██ ██▀▀▀▀▀▀ ██ ▀▀▀▀██▄ ██ ██ ██ ██ + ██ ███ ▀██▄▄▄▄█ ██▄▄▄ █▄▄▄▄▄██ ▄▄▄██▄▄▄ ██ ██ ██ + ▀▀ ▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀ ▀▀ ▀▀ +*Network Simulator for Hoods Gate Testing${NC} + +Usage: + $0 setup [bandwidth] [latency] + Create network topology for Hoods Gate testing + bandwidth: in mbit (default: 100) + latency: in ms (default: 10) + Example: $0 setup 50 20 + + $0 status + Show current network configuration and statistics + + $0 logs + Show traffic logs from fake internet + + $0 adjust <bandwidth> <latency> + Adjust network conditions on the fly + Example: $0 adjust 10 100 + + $0 teardown + Remove all network configuration + + $0 test + Run connectivity tests + +Network Topology Created: + [Client KVM] ---> [Bridge] ---> [Exit KVM] ---> [Fake Internet] + 10.10.1.10 10.10.1.1 10.10.2.10 192.168.100.2 + +Instructions for KVMs: + Client KVM: Attach to bridge '${BRIDGE_CLIENT}', use IP 10.10.1.10/24, gateway 10.10.1.1 + Exit KVM: Attach to bridge '${BRIDGE_EXIT}', use IP 10.10.2.10/24, gateway 10.10.2.1 + SDN should route traffic to 192.168.100.1 + +EOF + exit 0 +} + +init_state() { + mkdir -p "${STATE_DIR}" + touch "${STATE_FILE}" +} + +# Setup network topology +setup_network() { + local bandwidth=${1:-100} + local latency=${2:-10} + + echo -e "${GREEN}Setting up network simulation...${NC}" + + # Clean up any existing setup + teardown_network 2>/dev/null + + echo -e "${BLUE}[1/7]${NC} Creating network namespaces..." + ip netns add ${NETNS_EXIT} 2>/dev/null + ip netns add ${NETNS_INTERNET} 2>/dev/null + + echo -e "${BLUE}[2/7]${NC} Creating bridges for KVM attachment..." + ip link add ${BRIDGE_CLIENT} type bridge + ip link add ${BRIDGE_EXIT} type bridge + ip link add ${BRIDGE_WAN} type bridge + ip addr add ${IP_CLIENT_BRIDGE} dev ${BRIDGE_CLIENT} + ip addr add ${IP_EXIT_BRIDGE} dev ${BRIDGE_EXIT} + ip link set ${BRIDGE_CLIENT} up + ip link set ${BRIDGE_EXIT} up + ip link set ${BRIDGE_WAN} up + + echo -e "${BLUE}[3/7]${NC} Creating veth pairs..." + # Exit namespace LAN side connection + ip link add ${VETH_EXIT_HOST} type veth peer name ${VETH_EXIT_NS} + ip link set ${VETH_EXIT_NS} netns ${NETNS_EXIT} + ip link set ${VETH_EXIT_HOST} master ${BRIDGE_EXIT} + ip link set ${VETH_EXIT_HOST} up + + # WAN bridge to internet namespace connection + ip link add veth-wan-br type veth peer name veth-wan-inet + ip link set veth-wan-br master ${BRIDGE_WAN} + ip link set veth-wan-inet netns ${NETNS_INTERNET} + ip link set veth-wan-br up + + # Internet namespace connection + ip link add ${VETH_INET_EXIT} type veth peer name ${VETH_INET_NS} + ip link set ${VETH_INET_EXIT} master ${BRIDGE_WAN} + ip link set ${VETH_INET_NS} netns ${NETNS_INTERNET} + ip link set ${VETH_INET_EXIT} up + + echo -e "${BLUE}[4/7]${NC} Configuring IP addresses..." + # Exit namespace LAN IP only (no WAN IP - that's on the KVM side) + ip netns exec ${NETNS_EXIT} ip addr add ${IP_EXIT_BRIDGE} dev ${VETH_EXIT_NS} + ip netns exec ${NETNS_EXIT} ip link set ${VETH_EXIT_NS} up + ip netns exec ${NETNS_EXIT} ip link set lo up + + # Internet namespace IPs (acts as the gateway) + ip netns exec ${NETNS_INTERNET} ip addr add ${IP_INET_GATEWAY} dev veth-wan-inet + ip netns exec ${NETNS_INTERNET} ip addr add 8.8.8.8/32 dev lo # Fake DNS server + ip netns exec ${NETNS_INTERNET} ip addr add 1.1.1.1/32 dev lo # Fake DNS server + ip netns exec ${NETNS_INTERNET} ip link set veth-wan-inet up + ip netns exec ${NETNS_INTERNET} ip link set ${VETH_INET_NS} up + ip netns exec ${NETNS_INTERNET} ip link set lo up + + echo -e "${BLUE}[5/7]${NC} Configuring routing..." + # Host routing for client bridge - NAT to exit bridge + iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -o ${BRIDGE_EXIT} -j MASQUERADE + echo 1 > /proc/sys/net/ipv4/ip_forward + + # Internet namespace routing (responds to everything, blackholes it) + ip netns exec ${NETNS_INTERNET} sysctl -w net.ipv4.ip_forward=1 >/dev/null + ip netns exec ${NETNS_INTERNET} ip route add default dev veth-wan-inet + + # Note: Exit KVM will handle routing from LAN to WAN interface + + echo -e "${BLUE}[6/7]${NC} Applying traffic control (${bandwidth}mbit, ${latency}ms)..." + apply_tc ${bandwidth} ${latency} + + echo -e "${BLUE}[7/7]${NC} Starting fake internet services..." + start_fake_internet + + # Save configuration + echo "bandwidth=${bandwidth}" > "${STATE_FILE}" + echo "latency=${latency}" >> "${STATE_FILE}" + echo "timestamp=$(date +%s)" >> "${STATE_FILE}" + + echo -e "${GREEN}✓ Network simulation setup complete!${NC}" + echo "" + echo -e "${YELLOW}Next steps:${NC}" + echo " 1. Attach Client KVM to bridge: ${BRIDGE_CLIENT}" + echo " Configure with IP: 10.10.1.10/24, Gateway: 10.10.1.1, DNS: 10.10.1.1" + echo "" + echo " 2. Attach Exit KVM with TWO network interfaces:" + echo " - LAN interface to bridge: ${BRIDGE_EXIT}" + echo " Configure with IP: 10.10.2.10/24" + echo " - WAN interface to bridge: ${BRIDGE_WAN}" + echo " This will appear as '${EXIT_WAN_IFACE}' inside the VM" + echo " Configure with IP: 203.0.113.10/24, Gateway: 203.0.113.1" + echo "" + echo " 3. SDN should route traffic OUT of the '${EXIT_WAN_IFACE}' interface" + echo " (Just like routing out eth0 on a normal machine)" + echo "" + echo " 4. Run '$0 test' to verify connectivity" +} + +# Apply traffic control +apply_tc() { + local bandwidth=$1 + local latency=$2 + + # Apply to client->exit connection + tc qdisc del dev ${VETH_EXIT_HOST} root 2>/dev/null + tc qdisc add dev ${VETH_EXIT_HOST} root handle 1: htb default 10 + tc class add dev ${VETH_EXIT_HOST} parent 1: classid 1:10 htb rate ${bandwidth}mbit + tc qdisc add dev ${VETH_EXIT_HOST} parent 1:10 handle 10: netem delay ${latency}ms + + # Apply to WAN bridge (exit->internet) + tc qdisc del dev ${VETH_INET_EXIT} root 2>/dev/null + tc qdisc add dev ${VETH_INET_EXIT} root handle 1: htb default 10 + tc class add dev ${VETH_INET_EXIT} parent 1: classid 1:10 htb rate ${bandwidth}mbit + tc qdisc add dev ${VETH_INET_EXIT} parent 1:10 handle 10: netem delay ${latency}ms +} + +# Start fake internet services +start_fake_internet() { + local log_file="${STATE_DIR}/traffic.log" + > "${log_file}" + + # DNS Server (responds with fake IPs) + ip netns exec ${NETNS_INTERNET} dnsmasq \ + --no-daemon \ + --listen-address=${IP_INET_GATEWAY%/*} \ + --address=/#/93.184.216.34 \ + --log-queries \ + --log-facility=${STATE_DIR}/dns.log \ + --no-resolv \ + --no-poll \ + >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/dns.pid" + + # HTTP/HTTPS responder + ip netns exec ${NETNS_INTERNET} python3 -c " +import socket +import threading +import datetime + +def log_request(proto, ip, port, data): + with open('${log_file}', 'a') as f: + f.write(f'{datetime.datetime.now()} | {proto} | {ip}:{port} | {len(data)} bytes\\n') + +def http_server(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('0.0.0.0', 80)) + sock.listen(5) + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request('HTTP', addr[0], addr[1], data) + response = b'HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\n<html><body><h1>Fake Internet Response</h1></body></html>' + conn.sendall(response) + conn.close() + except: pass + +def https_server(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('0.0.0.0', 443)) + sock.listen(5) + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request('HTTPS', addr[0], addr[1], data) + conn.close() + except: pass + +def generic_tcp(): + for port in [22, 21, 25, 3389, 3306, 5432, 6379]: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + sock.bind(('0.0.0.0', port)) + sock.listen(1) + threading.Thread(target=lambda s=sock, p=port: tcp_handler(s, p), daemon=True).start() + except: pass + +def tcp_handler(sock, port): + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request(f'TCP/{port}', addr[0], addr[1], data) + conn.close() + except: pass + +threading.Thread(target=http_server, daemon=True).start() +threading.Thread(target=https_server, daemon=True).start() +threading.Thread(target=generic_tcp, daemon=True).start() + +import time +while True: time.sleep(1) +" >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/http.pid" + + # ICMP responder (respond to pings) + ip netns exec ${NETNS_INTERNET} bash -c " + while true; do + ping -c 1 8.8.8.8 >/dev/null 2>&1 || true + sleep 1 + done + " >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/icmp.pid" +} + +# Show status +show_status() { + if [ ! -f "${STATE_FILE}" ]; then + echo -e "${YELLOW}No simulation running${NC}" + return + fi + + source "${STATE_FILE}" + + echo -e "${GREEN}Network Simulation Status${NC}" + echo "================================" + echo -e "Bandwidth: ${bandwidth}mbit" + echo -e "Latency: ${latency}ms" + echo -e "Running since: $(date -d @${timestamp})" + echo "" + + echo -e "${GREEN}Network Namespaces:${NC}" + ip netns list | grep netsim && echo -e "${GREEN}✓${NC} Namespaces active" || echo -e "${RED}✗${NC} Namespaces missing" + echo "" + + echo -e "${GREEN}Bridges (for KVM attachment):${NC}" + ip link show ${BRIDGE_CLIENT} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_CLIENT} - Client KVM (LAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_CLIENT} missing" + ip link show ${BRIDGE_EXIT} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_EXIT} - Exit KVM (LAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_EXIT} missing" + ip link show ${BRIDGE_WAN} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_WAN} - Exit KVM (WAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_WAN} missing" + echo "" + + echo -e "${GREEN}Fake Internet Services:${NC}" + [ -f "${STATE_DIR}/dns.pid" ] && kill -0 $(cat "${STATE_DIR}/dns.pid") 2>/dev/null && echo -e "${GREEN}✓${NC} DNS (port 53)" || echo -e "${RED}✗${NC} DNS not running" + [ -f "${STATE_DIR}/http.pid" ] && kill -0 $(cat "${STATE_DIR}/http.pid") 2>/dev/null && echo -e "${GREEN}✓${NC} HTTP/HTTPS (ports 80, 443)" || echo -e "${RED}✗${NC} HTTP not running" + echo "" + + echo -e "${GREEN}Traffic Statistics:${NC}" + if [ -f "${STATE_DIR}/traffic.log" ]; then + local total=$(wc -l < "${STATE_DIR}/traffic.log") + echo " Total requests: ${total}" + if [ ${total} -gt 0 ]; then + echo " Last 5 requests:" + tail -5 "${STATE_DIR}/traffic.log" | sed 's/^/ /' + fi + else + echo " No traffic logged yet" + fi +} + +# Show logs +show_logs() { + echo -e "${GREEN}Traffic Logs${NC}" + echo "================================" + + if [ -f "${STATE_DIR}/traffic.log" ]; then + cat "${STATE_DIR}/traffic.log" + else + echo "No traffic logged" + fi + + echo "" + echo -e "${GREEN}DNS Queries${NC}" + echo "================================" + + if [ -f "${STATE_DIR}/dns.log" ]; then + tail -20 "${STATE_DIR}/dns.log" + else + echo "No DNS queries logged" + fi +} + +# Adjust network conditions +adjust_network() { + local bandwidth=$1 + local latency=$2 + + if [ -z "$bandwidth" ] || [ -z "$latency" ]; then + fail 1 "Usage: $0 adjust <bandwidth> <latency>" + fi + + echo -e "${GREEN}Adjusting network conditions...${NC}" + echo " Bandwidth: ${bandwidth}mbit" + echo " Latency: ${latency}ms" + + apply_tc ${bandwidth} ${latency} + + # Update state file + sed -i "s/^bandwidth=.*/bandwidth=${bandwidth}/" "${STATE_FILE}" + sed -i "s/^latency=.*/latency=${latency}/" "${STATE_FILE}" + + echo -e "${GREEN}✓ Network conditions updated${NC}" +} + +# Run connectivity tests +run_tests() { + echo -e "${GREEN}Running connectivity tests...${NC}" + echo "" + + echo -e "${BLUE}Test 1: Ping fake internet gateway${NC}" + ping -c 3 ${IP_INET_GATEWAY%/*} 2>/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + echo "" + + echo -e "${BLUE}Test 2: HTTP request to fake internet${NC}" + ip netns exec ${NETNS_INTERNET} timeout 5 python3 -m http.server 8080 >/dev/null 2>&1 & + local pid=$! + sleep 1 + curl -s --max-time 5 http://${IP_INET_GATEWAY%/*}:8080 >/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + kill $pid 2>/dev/null + echo "" + + echo -e "${BLUE}Test 3: DNS resolution${NC}" + dig @${IP_INET_GATEWAY%/*} google.com +short +time=2 >/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + echo "" + + echo -e "${YELLOW}Note: For full testing, attach your KVMs and test from there${NC}" +} + +# Teardown network +teardown_network() { + echo -e "${GREEN}Tearing down network simulation...${NC}" + + # Kill services + [ -f "${STATE_DIR}/dns.pid" ] && kill $(cat "${STATE_DIR}/dns.pid") 2>/dev/null + [ -f "${STATE_DIR}/http.pid" ] && kill $(cat "${STATE_DIR}/http.pid") 2>/dev/null + [ -f "${STATE_DIR}/icmp.pid" ] && kill $(cat "${STATE_DIR}/icmp.pid") 2>/dev/null + + # Remove traffic control + tc qdisc del dev ${VETH_EXIT_HOST} root 2>/dev/null || true + + # Delete bridges + ip link set ${BRIDGE_CLIENT} down 2>/dev/null || true + ip link set ${BRIDGE_EXIT} down 2>/dev/null || true + ip link set ${BRIDGE_WAN} down 2>/dev/null || true + ip link del ${BRIDGE_CLIENT} 2>/dev/null || true + ip link del ${BRIDGE_EXIT} 2>/dev/null || true + ip link del ${BRIDGE_WAN} 2>/dev/null || true + + # Delete namespaces (this removes veth pairs automatically) + ip netns del ${NETNS_EXIT} 2>/dev/null || true + ip netns del ${NETNS_INTERNET} 2>/dev/null || true + + # Clean iptables + iptables -t nat -D POSTROUTING -s 10.10.1.0/24 -o ${BRIDGE_EXIT} -j MASQUERADE 2>/dev/null || true + + # Clean state + rm -rf "${STATE_DIR}" + + echo -e "${GREEN}✓ Teardown complete${NC}" +} + +# Main +check_root +init_state + +case "${1:-}" in + setup) + setup_network "${2:-100}" "${3:-10}" + ;; + status) + show_status + ;; + logs) + show_logs + ;; + adjust) + adjust_network "$2" "$3" + ;; + test) + run_tests + ;; + teardown) + teardown_network + ;; + *) + usage + ;; +esac + +exit 0 |
