summaryrefslogtreecommitdiff
path: root/test-env
diff options
context:
space:
mode:
Diffstat (limited to 'test-env')
-rw-r--r--test-env/README.md110
-rwxr-xr-xtest-env/net-sim/netsim.sh489
-rwxr-xr-xtest-env/provision-vm.sh118
3 files changed, 717 insertions, 0 deletions
diff --git a/test-env/README.md b/test-env/README.md
new file mode 100644
index 0000000..ef2bb0a
--- /dev/null
+++ b/test-env/README.md
@@ -0,0 +1,110 @@
+# Technical Specification: Automated KVM Provisioning for Hoods Gate Testing
+
+This document outlines the architecture and implementation of a shell-based
+automation utility for deploying Kernel-based Virtual Machines (KVM) on Ubuntu
+Server using virt-install. This approach is designed for rapid iteration in
+kernel development and network stack testing where total environment isolation
+is required.
+
+## Overview
+
+The goal of this implementation is to provide a reproducible, command-line
+driven interface for deploying headless virtual machines. By utilizing a Linux
+Bridge and Serial Console redirection, developers can perform destructive
+kernel operations while maintaining persistent access to the debug output,
+bypassing the overhead of a graphical user interface.
+
+## System Requirements & Prerequisites
+
+The host system must support hardware virtualization (Intel VT-x or AMD-V) and
+have the following packages installed:
+
+- qemu-kvm: Backend emulator
+- libvirt-daemon-system: Management system
+- virinst: CLI Utility for provisioning
+- bridge-utils: Necessary for L2 network bridging
+
+### Storage Persistence
+
+Virtual disk images created by this script are stored in
+/var/lib/libvirt/images/. During kernel testing, if the filesystem becomes
+corrupted due to experimental kernel modules, the storage must be manually
+purged or overwritten.
+
+### Network Configuration
+
+The host must have a bridge interface (e.g., br0) configured via Netplan.
+This allows the Guest VM to obtain its own IP address and provides a
+transparent environment for testing custom networking protocols or firewall
+rules.
+
+## Implementation
+
+The script, [provision-lab.sh](./provision-vm.sh), automates the
+virt-install process for isolated kernel and network testing. It utilizes local
+ISO media and libosinfo detection to ensure hardware-optimized environments
+while configuring the serial console for direct TTY access.
+
+## Operational Procedures
+
+### VM Creation
+
+Execute the script with optional parameters for resource allocation:
+
+```shell
+sudo ./provision-vm.sh <name> <vcpus> <ram_in_mb>
+```
+
+### Interacting with the Guest Kernel
+
+Since the VM is configured without a graphics card, the standard virsh console
+command is used to attach to the guest's serial port. This is essential for
+capturing Kernel Panics or Early Printk output that would otherwise be lost.
+
+Attach to Console:
+
+```shell
+virsh console <vm-name>
+```
+
+Detach from Console (Press):
+
+```
+CTRL + ]
+```
+
+### Modifying Kernel Parameters
+
+To test specific kernel options (e.g., disabling KASLR or isolating CPUs),
+edit the VM configuration directly via the XML descriptor:
+
+```shell
+virsh edit <vm-name>
+```
+
+Locate the <cmdline> tag within the <os> section to append your required flags.
+
+### Network Analysis
+
+Use tcpdump -i br0 on the host to monitor raw packet traffic moving through the
+VM's virtual interface.
+
+```shell
+tcpdump -i br0
+```
+
+## Lifecycle Management
+
+Operation|Command|Description
+Start|virsh start <name>|Powers on the virtual machine
+Stop|virsh shutdown <name>|Sends an ACPI power signal for a graceful exit
+Destroy|virsh destroy <name>|Equivalent to pulling the power plug (immediate stop)
+Undefine|virsh undefine <name> --remove-all-storage|Completely removes the VM and its disk images
+
+## Technical Considerations
+
+When performing kernel modifications, the following behaviors should be expected:
+
+- _Storage Persistence_: Changes made to the filesystem persist unless the --remove-all-storage flag is used during deletion.
+- _Network Isolation_: By using a bridge, the VM possesses its own MAC address. Ensure the host firewall (ufw/iptables) is configured to allow traffic across the bridge.
+- _Instruction Set Passthrough_: If the kernel testing requires specific CPU instructions (e.g., AES-NI), append --cpu host-passthrough to the virt-install command.
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
diff --git a/test-env/provision-vm.sh b/test-env/provision-vm.sh
new file mode 100755
index 0000000..97bfb32
--- /dev/null
+++ b/test-env/provision-vm.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# Default Configuration
+VM_NAME="kernel-test-lab"
+VCPUS=2
+RAM=4096
+DISK_SIZE=20
+BRIDGE_INT="virbr0"
+ISO_DIR="/var/lib/libvirt/images"
+ISO_PATH=""
+
+#OS_VARIANT=$(osinfo-query os | grep -i "$(basename "$ISO_PATH" | cut -d'-' -f1)" | head -n1 | awk '{print $1}')
+# more than likely this will be set to none or "" see: https://linux.die.net/man/1/virt-install
+OS_VARIANT=""
+
+usage() {
+ cat << EOF
+┌──────────────────────────────────────────────────────────┐
+│░█░█░█░█░█▄█░░░█▀█░█▀▄░█▀█░█░█░▀█▀░█▀▀░▀█▀░█▀█░█▀█░█▀▀░█▀▄│
+│░█▀▄░▀▄▀░█░█░░░█▀▀░█▀▄░█░█░▀▄▀░░█░░▀▀█░░█░░█░█░█░█░█▀▀░█▀▄│
+│░▀░▀░░▀░░▀░▀░░░▀░░░▀░▀░▀▀▀░░▀░░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀░▀│
+└──────────────────────────────────────────────────────────┘
+*KVM Provisioning Utility for Isolated Kernel and Network Testing.
+
+Usage: sudo $0 [OPTIONS]
+
+Options:
+ -n NAME Name of the Virtual Machine (Default: kernel-test-lab)
+ -c CPUS Number of vCPUs to allocate (Default: 2)
+ -m RAM Memory allocation in MB (Default: 4096)
+ -d DISK Disk size in GB (Default: 20)
+ -b BRIDGE Host bridge interface (Default: virbr0)
+ -i ISO Absolute path to ISO (Bypasses interactive selection)
+ -ov OSVARIANT Optimize the guest configuration for a specific os variant (Bypasses Interactive selection)
+ -h Display this technical manual
+
+Example:
+ sudo ./provision-lab.sh -n kernel-dev-01 -c 4 -m 8192 -d 40
+EOF
+ exit 0
+}
+
+if [[ $EUID -ne 0 ]]; then
+ echo "Error: Execution requires root privileges." >&2
+ exit 1
+fi
+
+while getopts "n:c:m:d:b:i:ov:h" opt; do
+ case ${opt} in
+ n) VM_NAME=$OPTARG ;;
+ c) VCPUS=$OPTARG ;;
+ m) RAM=$OPTARG ;;
+ d) DISK_SIZE=$OPTARG ;;
+ b) BRIDGE_INT=$OPTARG ;;
+ i) ISO_PATH=$OPTARG ;;
+ ov) OS_VARIANT=$OPTARG ;;
+ h) usage ;;
+ *) usage ;;
+ esac
+done
+
+if [[ -z "$ISO_PATH" ]]; then
+ echo "--- KVM Lab Provisioner ---"
+ echo "Scanning $ISO_DIR for installation media..."
+
+ mapfile -t ISO_LIST < <(ls "$ISO_DIR"/*.iso 2>/dev/null)
+
+ if [ ${#ISO_LIST[@]} -eq 0 ]; then
+ echo "Error: No ISO files detected in $ISO_DIR" >&2
+ exit 1
+ fi
+
+ select opt in "${ISO_LIST[@]}" "Cancel"; do
+ if [[ "$opt" == "Cancel" ]]; then exit 0; fi
+ if [[ -f "$opt" ]]; then
+ ISO_PATH="$opt"
+ break
+ else
+ echo "Invalid selection."
+ fi
+ done
+fi
+
+if [[ -z "$OS_VARIANT" ]]; then
+ echo "Need a OS Variant..."
+ read OS_VARIANT
+fi
+
+echo "Provisioning Details:"
+echo " Name: $VM_NAME"
+echo " vCPUs: $VCPUS"
+echo " RAM: $RAM MB"
+echo " Storage: $DISK_SIZE GB"
+echo " Bridge: $BRIDGE_INT"
+echo " ISO: $ISO_PATH"
+echo " Profile: $OS_VARIANT"
+
+# error was: ERROR Kernel arguments are only supported with location or kernel installs
+# changed from location to cdrom; can't pass kernel args unless you use --location
+# --extra-args "console=ttyS0,115200n8 serial" \
+virt-install \
+ --name "$VM_NAME" \
+ --vcpus "$VCPUS" \
+ --memory "$RAM" \
+ --disk "size=$DISK_SIZE,format=qcow2" \
+ --os-variant "$OS_VARIANT" \
+ --location "$ISO_PATH" \
+ --extra-args "console=ttyS0,115200n8 serial" \
+ --network "bridge=$BRIDGE_INT" \
+ --graphics none \
+ --console pty,target_type=serial \
+ --check all=off \
+ --hvm \
+ --virt-type kvm \
+ --noreboot
+
+echo "--- Provisioning Sequence Complete ---"
+echo "To begin testing: virsh start $VM_NAME --console"