Building a Deterministic PCAP Replay Testbed with Open vSwitch

When writing detection rules for network threats, testing them against static packet capture files with command-line tools often hides edge cases. Stream reassembly buffers, protocol parsers, and flow timeouts behave differently when processing live network frames versus scanning offline files sequentially. To validate network intrusion signatures and telemetry pipelines accurately, you need an isolated, deterministic replay engine that simulates real wire traffic.
In this guide, we will build a dedicated network replay environment using Open vSwitch (OVS) and tcpreplay on a Linux host. This setup isolates network traffic from your local LAN while preserving original packet dynamics and timing.
Architecture and Virtual Network Setup
The core concept relies on an Open vSwitch bridge (br-replay) acting as a virtual software switch. We create two virtual Ethernet (veth) pairs: one for injecting replayed packets and another for monitoring traffic with an Intrusion Detection System (IDS) sensor.
Start by creating the virtual switch and disabling Spanning Tree Protocol (STP) to prevent unnecessary control frames from cluttering the capture stream:
sudo ovs-vsctl add-br br-replay
sudo ovs-vsctl set bridge br-replay stp_enable=false
Next, create two veth interface pairs. The veth-inject pair handles packet injection from tcpreplay, while veth-mon connects directly to your detection engine.
# Create injection pair
sudo ip link add veth-inject-tx type veth peer name veth-inject-rx
sudo ip link set veth-inject-tx up
sudo ip link set veth-inject-rx up
# Create monitoring pair
sudo ip link add veth-mon-tx type veth peer name veth-mon-rx
sudo ip link set veth-mon-tx up
sudo ip link set veth-mon-rx up
# Attach receiver interfaces to the OVS bridge
sudo ovs-vsctl add-port br-replay veth-inject-rx
sudo ovs-vsctl add-port br-replay veth-mon-tx
To ensure every injected frame reaches the monitoring port, set up a port mirror on the OVS bridge mirroring all ingress traffic on veth-inject-rx directly to veth-mon-tx:
sudo ovs-vsctl -- --id=@m create mirror name=replay-mirror \
select-dst-port=@inject_port select-src-port=@inject_port \
output-port=@mon_port \
-- set bridge br-replay mirrors=@m \
-- --id=@inject_port get port veth-inject-rx \
-- --id=@mon_port get port veth-mon-tx
Normalizing and Injecting Packet Captures
Raw packet captures harvested from threat intelligence feeds or sandbox runs often contain hardcoded MAC addresses, VLAN tags, or invalid checksums that virtual switches might drop. Before pushing packets onto veth-inject-tx, normalize the capture file using tcprewrite.
First, rewrite the destination MAC address to a broadcast address or a specific dummy address so Open vSwitch forwards the frame predictably, and recalculate layer 4 checksums:
tcprewrite --infile=malware_sample.pcap \
--outfile=normalized_sample.pcap \
--dmac=ff:ff:ff:ff:ff:ff \
--fixcsum
Once normalized, send the traffic into the virtual switch using tcpreplay. Controlling the replay speed is critical: replaying too fast can cause interface buffer drops, while replaying too slow distorts timing-sensitive detection rules.
# Replay at original capture speed
sudo tcpreplay --intf1=veth-inject-tx --realtime normalized_sample.pcap
# Replay at a fixed rate (e.g., 50 Mbps) for stress testing
sudo tcpreplay --intf1=veth-inject-tx --mbps=50 normalized_sample.pcap
Hooking Up Detection Engines and Automation
With traffic flowing across the OVS bridge, configure your network sensor to listen on veth-mon-rx. Enable promiscuous mode on the monitoring interface so it captures all mirrored frames:
sudo ip link set veth-mon-rx promisc on
Now run your detection process bound directly to veth-mon-rx. When tcpreplay runs, the sensor processes the Ethernet frames in real time as if they were arriving from a physical tap. The TCP stream reassembly engine builds network state, tracks session timeouts, and triggers alerts against protocol anomalies exactly as it would in production.
To automate rule verification, wrap this workflow in a script: load updated detection signatures, trigger tcpreplay against the target PCAP, parse the generated alerts file, and assert that the expected Signature ID (SID) fired without secondary noise.
This local virtual switch harness prevents rogue traffic from leaking into your home subnet while providing a reliable, repeatable testbed for continuous detection engineering.
Photo by Sebastian Bednarek on Unsplash.