March 25, 2026
You might need to capture some network packets and replay them, for example when testing software.
This is made easier with the help of tools like tcpdump and tcpreplay, but it’s not always as easy as just running the tool, because the values of various fields need to be correct for packets to be processed by your network stack.
In this post, I’ll walk through capturing IP traffic on a specific UDP port on a remote host, and then replaying the packet capture locally on Linux.
In this post, I assume you’re familiar with the command line, and with networking concepts (packet headers, Ethernet, IP, UDP). Though to be honest, if you’re not, this post probably isn’t interesting for you. I also assume a GNU/Linux environment.
This is just one command: make sure tcpdump is installed, and then run it with the correct options.
I’m capturing traffic coming into the interface eth0 on UDP port 1234, so I would run the following
sudo tcpdump -nli eth0 --print -w my-capture.pcap udp port 1234
The options are:
-n: don’t resolve host addresses, port numbers, etc. to names-l: line buffer the output (so output shows up faster)-i eth0: capture traffic on interface eth0--print: print the packets even when we write to a file (otherwise they would not be shown)-w my-capture.pcap: write captured packets to the file my-capture.pcapudp port 1234: the pcap-filter expression to capture traffic on UDP port 1234Then, generate the traffic you want to capture, and stop tcpdump with Ctrl-C.
Copy the packet capture to the local host (e.g. with scp, sftp, rsync…), and we can now inspect and replay this capture. The file can be opened in Wireshark if you want to inspect the packets visually.
After inspecting the capture, I realized there are some packets at the start that I want to skip.
Specifically, I don’t need the first 3 packets.
To remove them, I use editcap, which comes with Wireshark:
editcap my-capture.pcap edited-capture.pcap 1-3
You can list individual packet numbers or packet ranges to be excluded.
By default, those packets do not get written to the output file.
If you want to list only packets that should be written and exclude all others, use the -r flag.
Replaying a packet capture locally is a bit more involved, because the fields in the various headers of the packets (Ethernet, IP) have to be correct in order for your network stack to process them. You can’t just yeet the packets at the loopback interface and hope everything works. Good news is, the utilities available on Linux make this easy.
Make sure iproute2 is installed; we’ll use it to set up virtual Ethernet interfaces through which we’ll send the packets.
veth (virtual Ethernet) devices come in pairs: think of them as an Ethernet cable, where each device is one end of the cable.
Let’s create a veth pair (if veth-src or veth-dst already exist on your system – check it with ip -brief link – use different names):
sudo ip link add veth-src type veth peer name veth-dst
Then bring them up:
sudo ip link set veth-src up
sudo ip link set veth-dst up
And add IP addresses (if these ranges are already used, change them – check with ip -brief address):
sudo ip addr add 10.66.66.1/24 dev veth-src
sudo ip addr add 10.66.66.2/24 dev veth-dst
Check their status with ip -brief link, you should see something like this:
veth-dst@veth-src UP 12:48:fb:e9:98:70 <BROADCAST,MULTICAST,UP,LOWER_UP>
veth-src@veth-dst UP 66:36:89:bc:9d:71 <BROADCAST,MULTICAST,UP,LOWER_UP>
The second column is the MAC address; this will almost certainly be different for you, but remember the MAC address for veth-dst@veth-src; you will need this for the replay.
First, let’s listen on the expected port on veth-dst using netcat (you might need to install it, I’m using netcat-openbsd):
nc -l -u 10.66.66.2 1234
We are listening on the IP address associated with veth-dst – the expected destination of the packets, i.e. the receiving end.
The terminal will seem to hang and nothing will print; this is good, because netcat is waiting for packets.
In another terminal, let’s start the replay:
sudo tcpreplay-edit \
--intf1=veth-src \
--dstipmap=0.0.0.0/0:10.66.66.2 \
--enet-dmac=MAC \
edited-capture.pcap
Here, we use tcpreplay-edit, which live-edits the packets while replaying them, and provide the following options:
--intf1=veth-src: the primary output interface, where we want to send the packets--dstipmap=0.0.0.0/0:10.66.66.2: how we want to rewrite the destination IP. In this case, we map all IP addresses (the CIDR 0.0.0.0/0) to the IP address of the receiving end veth-dst (10.66.66.2)--enet-dmac=MAC: how we want to rewrite the destination MAC address in the Ethernet header. Replace MAC with the MAC address of veth-dst.In the terminal running netcat, you should now see data appearing (it might not be readable though, depending on how much of it contains printable characters). You now have a working packet replay that you can receive and process locally; it’ll either stop when the end of the capture is reached, or you can interrupt it earlier with Ctrl-C.
You only need to delete one of the interfaces with sudo ip link del veth-src; the other side will be removed automatically.