Releases: appneta/tcpreplay
Release list
v4.6.1
Tcpreplay 4.6.1 is a security and hardening release. Its centerpiece is a new continuous fuzzing pipeline — three libFuzzer targets covering the pcap parser, the fragroute rules engine, and the tcpprep --services parser, submitted to OSS-Fuzz — which found and fixed nine defects in its first days running, on top of three new GHSA security advisories reported directly and a round of UBSan/ASan hardening across the packet parsers.
Highlights
- Continuous fuzzing via OSS-Fuzz — three new libFuzzer targets (
fuzz_pcap,fuzz_fragroute,fuzz_services) run continuously against every commit; this release fixes all nine issues they found in their first run, from a heap-buffer-overflow write to a resource-exhaustion hang - 3 new security advisories fixed, two of them CVSS 7.0+ (High)
- UBSan now runs alongside ASan in CI, catching a class of misaligned-access bugs that ASan alone doesn't — the fix also made wire-format header structs
packed, removing systemic misaligned access across the packet parsers --xdp-batch-sizeremoved — measurement on 100GigE found no benefit from raising it above the default of 1, now that AF_XDP sends pipeline across the whole umem (from 4.6.0)
Security
Three new advisories, all reported by tao pan (@pant0m):
| Advisory | CVSS | Severity | Component |
|---|---|---|---|
| GHSA-4hqm-8v2c-9pwj | 7.1 | High | Three heap out-of-bounds reads in tcpliveplay's pcap parsing — one can leak adjacent heap memory onto the live network |
| GHSA-pmmx-m8p5-969j | 7.0 | High | Inverted bounds check in the dlt_radiotap plugin — an 8-year-old >=/<= typo that made an oversized memcpy() run exactly when it was guaranteed to overflow. Not reachable through the bundled CLIs, which clamp caplen; only third-party code linking libtcpedit directly on unclamped input is affected |
| GHSA-p3f2-88rg-9mch | 6.1 | Medium | Heap out-of-bounds read in tcpprep cache-file parsing — the file's packets_per_byte field was never cross-checked against the hardcoded indexing stride every reader actually uses |
Continuous fuzzing: nine issues found and fixed
This cycle added test/fuzz/ — three libFuzzer targets built against ASan/UBSan and submitted to OSS-Fuzz (#1092, #1101, #1103). Every issue below was found by that pipeline before it ever reached a user, and none had a public GitHub issue filed for it (#1118) — they're listed here for the first time now that fixes are shipped.
| Finding | Crash type | Fix |
|---|---|---|
ip_chaff_apply() heap-buffer-overflow write |
2-byte OOB write | A buffer-capacity calculation assumed the IP header started 2 bytes earlier than it actually does (missing a PKT_BUF_ALIGN offset), overstating room by exactly 2 bytes — libdnet wrote an inserted IP option 2 bytes past the heap allocation |
ip_chaff_apply() pointer corruption → OOB read |
Out-of-bounds read | The option-insert path used libdnet's return value unconditionally, including on failure, silently shifting a packet pointer out of bounds on every failed call; a rules file stacking several ip_chaff opt directives could walk it far enough to underflow a length calculation downstream |
mod_apply() exponential packet growth |
Timeout (>60s) | Duplicating rules (ip_chaff, tcp_chaff) double the packet queue on every application; stacking ~30 of them in a rules file turns one packet into gigabytes of output. Now bounded at a hard packet-count cap between rule applications |
mod_close() never called |
Direct memory leak | A fully-implemented cleanup function that freed the whole parsed rule chain was never wired up — leaked on every fragroute_close() call, not just in fuzzing |
rand_t leak in ip_frag/tcp_seg |
Direct memory leak | Both modules store their RNG handle in a file-scope static rather than per-rule heap state; a repeated directive overwrote the handle without closing the previous one |
pktq_shuffle() on an empty queue |
Unexpected exit | An empty-queue edge case was misdiagnosed as an allocation failure and called exit() |
get_l2len_protocol() on an unsupported DLT |
Unexpected exit | The one error path in the function that called exit() instead of returning an error code, like every other branch |
raw_ip_opt_parse() too-few-tokens |
Null dereference | A tcp_opt raw rule with one fewer token than ip_opt raw needs slipped past a caller-side length check and read past the token array |
delay_apply() on an empty queue |
Null dereference | Same empty-queue class as pktq_shuffle() above, in a different module |
Other fixes
- UBSan hardening: fixed misaligned MPLS label parsing and a negative left shift in fragroute's
ip_ttl; wire-format header structs are nowpacked, fixing systemic misaligned access flagged once UBSan ran in CI (#1100, #1104) - fragroute: fixed a use-after-free in
pkt_close()on repeated context setup (#1102) - txring: fixed
TX_RINGfailing outright on interfaces with an MTU of ~1365 or less, e.g. 1280, the IPv6 minimum (#1090) - common: fixed an out-of-bounds write from an undersized
struct bpf_programstub onHAVE_LIBBPFbuilds, hit bytcpprep/tcpbridge's-foption (#1121) - fragroute: fixed an uninitialized-stack read in
mod_open()'s debug logging — an intermittent crash under--dbug(#1124) - tcpedit: fixed 17
-Waddress-of-packed-memberwarnings left by the packed-struct fix above (#1122) - test: added a dependency-free unit test suite under
test/unit, run bymake checkandctest(#1079) - build: minor compilation fixes and warning cleanups found while building with meson
warning_level=3(#1116, contributed by @GabrielGanne)
Credits
Thanks to tao pan (@pant0m) for the three security reports behind this release, and to @GabrielGanne for compilation cleanups. The remaining nine fixes came from the new OSS-Fuzz pipeline this release also introduces.
What's Changed
- xdp: remove
--xdp-batch-size— measurement on 100GigE found no benefit from raising it above the default of 1 (#1128) - tcpedit: fix 17
-Waddress-of-packed-memberwarnings left by the packed-struct fix (#1122, #1127) - common: fix out-of-bounds write from an undersized
struct bpf_programstub onHAVE_LIBBPFbuilds (#1121, #1125) - fragroute: fix uninitialized-stack read in
mod_open()'s debug logging under--dbug(#1124) - fix:
flow_decode()over-required bytes for a minimal ICMP/ICMPv6 header (#1119, #1120) - fix(fuzz): don't auto-file public GitHub issues from OSS-Fuzz findings (#1092, #1118)
- fix(fuzz): build a minimal libpcap for OSS-Fuzz targets instead of linking the system one (#1092, #1117)
- ci: run UBSan alongside ASan; mark wire-format header structs packed (#1101, #1104, #1105, #1112)
- test: add libFuzzer targets for the pcap, fragroute rules, and tcpprep
--servicesparsers; submit to OSS-Fuzz (#1092, #1103, #1113, #1114, #1115) - txring: fix TX_RING failing outright on interfaces with an MTU of ~1365 or less (#1090, #1107, #1109, #1111)
- test: add a dependency-free unit test suite under
test/unit, run bymake check/ctest(#1079, #1091, #1099, #1106) - fragroute: fix a use-after-free in
pkt_close()on repeated context setup (#1102) - build: minor compilation fixes and warning cleanups found building with meson
warning_level=3(#1116) - Security: fix inverted bounds check in
dlt_radiotap_get_80211()— GHSA-pmmx-m8p5-969j (#1141) - Security: fix three heap OOB reads in
tcpliveplay's pcap parsing — GHSA-4hqm-8v2c-9pwj (#1140) - Security: reject cache files whose
packets_per_bytedoesn't match the hardcoded indexing stride — GHSA-p3f2-88rg-9mch (#1139) - fragroute: bound
mod_apply()against exponential packet-count growth (OSS-Fuzz 546146015, #1138) - fragroute: fix a
rand_tleak inip_frag_open()/tcp_seg_open()on a repeated directive (OSS-Fuzz 545925322, #1137) - fragroute: wire up
mod_close(), implemented but never called (OSS-Fuzz 545818605, #1136) - fragroute: fix a null-deref in
delay_apply()on an empty packet queue (OSS-Fuzz 545965632, #1135) - fragroute: fix a null-deref in
raw_ip_opt_parse()on atcp_opt rawrule with too few tokens (OSS-Fuzz 545904184, #1134) - fragroute: fix pointer corruption in
ip_chaff_apply()on a failed option insert (OSS-Fuzz 545925319, #1133) - fragroute: fix a heap-buffer-overflow in
ip_chaff_apply()'s option insertion (OSS-Fuzz 545965630, #1132) - common: fix an Unexpected-exit in
get_l2len_protocol()on an unsupported DLT type (OSS-Fuzz 545718476, #1131) - fragroute: fix an Unexpected-exit in
pktq_shuffle()on an empty packet queue (OSS-Fuzz 545904182, #1130)
Verifying the download
The .tar.gz/.tar.xz tarballs are accompanied by detached PGP signatures (.asc), signed with the tcpreplay release key (tcpreplay@appneta.com, fingerprint 84E4FA215C934A7D97DC76D5E9E2149793BDE17E):
gpg --verify tcpreplay-4.6.1.tar.xz.asc tcpreplay-4.6.1.tar.xz
Full Changelog: v4.6.0...v4.6.1
v4.6.0
Tcpreplay 4.6.0 is a major release built around three things: a new CMake build system, two high-throughput packet-injection backends that now genuinely reach line rate, and a set of security fixes on top of 4.5.5. It also folds in a correctness pass on the TX_RING send path, several new CLI features, and a jumbo-frame bug that had been silently truncating packets.
Highlights
- Completely updated documentation site — tcpreplay.appneta.com now has a modern, task-oriented guide: installation, quickstart, performance testing, fast-path backends, and a reference page per tool
- CMake build, alongside the existing autotools flow (
cmake -B build && cmake --build build) - AF_XDP (
--xdp) now reaches line rate on 1GigE — from an initial ~13k pps to ~305k pps (941 Mbps) on commodity e1000 hardware, ahead of the default injector - io_uring (
--io-uring) batches sends instead of one syscall per packet — ~11% less CPU per packet --raw— a new PF_INET/SOCK_RAW injection path that sends through the local IP stack--loss— random percent packet-loss simulation for testing against non-ideal network conditions- 3 new security advisories fixed on top of 4.5.5
- Jumbo frames fixed — a long-standing bug truncated any frame beyond an MTU that needed more than one page of ring buffer to a flat 4096 bytes
- TX_RING reliability fixed — the default Linux send path could silently drop and reorder packets; both are now fixed
New features
| Feature | Flag | Notes |
|---|---|---|
| io_uring injection | --io-uring |
Async submission via Linux io_uring; portable to any modern kernel |
| AF_XDP injection | --xdp |
Kernel-bypass via an XDP socket; falls back automatically if the adapter can't support it |
| — queue selection | --xdp-queue |
Bind to a specific adapter queue instead of always queue 0 |
| — strict mode | --xdp-no-fallback |
Fail rather than silently fall back — for benchmarking, where a silent change of injector would change what's measured |
| — batch tuning | --xdp-batch-size |
For very high-speed links (100GigE+) with small packets; not normally needed |
| Raw IP socket injection | --raw |
Routes through the kernel's IP stack (routing, netfilter); IPv4-only, rebuilds L2 framing |
| Packet-loss simulation | --loss |
Independently drops each packet at a given 0–100% probability (contributed by @dsseng) |
| Raw IP (L3-only) interfaces | — | Replay onto WireGuard/tun-style interfaces with no L2 header |
| Static library | libtcpreplay |
The replay engine is now installable as a standalone C library |
| CMake build | — | Full feature parity with autotools; every --enable-*/--with-* has a CMake equivalent |
Performance: getting AF_XDP to actually earn its name
--xdp was close to unusable in practice — it worked on some adapters and not others, occasionally hung, and where it did run, underperformed the default injector by orders of magnitude. Chasing that down turned up four separate, compounding bugs:
- No attach-mode fallback. AF_XDP socket setup never told the kernel which mode to bind in, so it silently required native driver XDP support — absent on
e1000,e1000e, and plenty of other common adapters — with no fallback to generic (SKB) mode. - Unbounded hangs. Two send-path loops retried forever with no timeout, so a driver that accepted the socket but never actually transmitted wedged the whole replay in an unkillable 100% CPU spin.
- A ring descriptor leak in
--xdp-batch-size. Reserved-but-unused TX descriptors were never returned, so any batch size that didn't evenly divide the pcap's packet count caused throughput to collapse — measured as low as 120 pps. - No pipelining. The send path indexed its packet buffer modulo the batch size, so it had to block until an entire batch fully completed before it could prepare the next one — a full TX completion round-trip per packet.
With all four fixed — plus a --loop bug that desynced the ring after the first pass and wedged, --xdp-queue, and the auto-fallback above — a real-world before/after on a 1GigE e1000:
| pps | throughput | |
|---|---|---|
--xdp, before this work |
13,344 | 41 Mbps |
--xdp, fully fixed |
305,168 | 941 Mbps |
| default injector (TX_RING), for reference | 242,979 | 749 Mbps |
That's a 23× improvement, and AF_XDP now saturates a 1GigE link where the default injector doesn't.
io_uring got a smaller but genuine fix along the way: it previously issued one io_uring_enter() syscall per packet — no better than a plain send() loop — plus extra bookkeeping on top, so it was slower than not using it at all. Submissions now batch 64 deep with strict in-order delivery, cutting syscall count 64× for roughly 11% less CPU per packet.
Reliability and correctness fixes
- TX_RING was silently dropping and reordering packets (the default Linux send path). It filled ring frames out of order, which both misordered packets on the wire and permanently stranded any frame the kernel hadn't finished with — discarded at teardown after already being counted as successfully sent. In the worst case, a short replay reported 100% of packets sent while transmitting none. Fixed by filling the ring strictly in order and draining it before statistics are read.
- Jumbo frames truncated to 4096 bytes. A block-sizing bug in the TX_RING setup divided the frame size back down to a single page after correctly growing the block to fit the MTU, silently mangling any capture with frames larger than ~4KB.
--xdphung indefinitely rather than failing when AF_XDP couldn't be used, and returned success on some fatal send errors, hiding failures from CI and scripts.- Assorted build-system correctness fixes:
configure.accould enable AF_XDP support from a bare link test without the matching headers, producing a build that failed partway through; cached CMake feature probes could report a library missing after it had since been installed, with no indication a re-probe was needed.
Security
Three new advisories are fixed beyond 4.5.5, all reported by tinyb0y:
| Advisory | Severity | Component |
|---|---|---|
| GHSA-fwcr-mqg6-hqmx — CVSS 7.8 | High | Stack buffer overflow in the tcpprep --services file parser |
| GHSA-m6w7-8497-g9c9 — CVSS 7.7 | High | Heap out-of-bounds read via --pktlen with --preload-pcap |
| GHSA-5q26-7fxx-v8fh — CVSS 7.1 | High | Heap out-of-bounds access in ARP address rewriting |
(Everyone already on 4.5.5 has the fragroute and get_layer4_v6 advisories that were fixed alongside these — those were backported there separately, so they aren't new here.)
SECURITY.md is now at the repo root, picked up by GitHub's Security tab, and documents the disclosure policy and preferred reporting channel.
Build system
- CMake (
cmake -B build && cmake --build build) is now the primary, recommended way to build the suite, with feature-for-feature parity to the autotools./configureflags. - GNU AutoGen is no longer required to build from a git checkout (only
python3andasciidoctor); it remains needed only for one legacy template file. - Release tarballs ship pre-built files for both build systems, so a tarball build needs neither python3 nor asciidoctor.
Credits
Thanks to @dsseng for --loss, @Steve-Tech for reporting and diagnosing the jumbo-frame truncation, and tinyb0y for the security research behind this release's advisories.
What's Changed
- xdp: revert the top-speed default for
--xdp-batch-sizeback to 1 (#1084) — now that sends pipeline across the whole umem, a batch of 1 already reaches line rate on a 1GigE e1000 (941 Mbps / 305k pps).--xdp-batch-sizestays available for links this can't yet saturate — 100GigE and up, especially with small packets — where a deeper batch may still help - xdp: pipeline AF_XDP sends by spreading packets over the whole umem instead of recycling the first
batch_sizeframes (#1084) — the send path no longer waits for each batch to complete before preparing the next packet, only when the next batch would reuse a frame still in flight. The umem is 4096 frames whether used or not, so this costs no extra memory; at a batch of 64 only 1.5% of it was being used. End-of-replay draining now covers AF_XDP too, so the reported counts describe what actually reached the wire - xdp: batch AF_XDP sends 64 deep by default when replaying at top speed (#1084) — the send path waits for each batch to complete before preparing the next packet, so a batch of one costs a full TX completion round-trip per packet: ~13k pps on an e1000 against ~254k at 64. Paced replays keep a batch of 1, since
--pps/--mbps/--multiplierare only honoured there - xdp: fix
--xdp-batch-sizecollapsing throughput (#1084) — a full batch of TX descriptors was reserved but only the filled ones submitted, so libxdp's cached producer index crept ahead on every short batch until the ring looked full;--xdp-batch-size=64against a 179-packet pcap ran at 120 pps, now ~685k. Unused reservations are handed back - xdp: fix
--xdpdelivering only the first--loopiteration and then wedging (#1082) — the TX ring's cached producer/consumer indices were zeroed between loops, desynchronising them from the kernel's and stranding every later descriptor; libxdp maintains them itself - xdp: add
--xdp-queueto select which adapter queue the AF_XDP socket binds to, instead of always using queue 0 (#1082) - xdp: fall back to the default injection method, with a warning, when AF_XDP cannot be set up on the adapter, rather than failing outright;
--xdp-no-fallbackrestores the hard error for benchmarking, wh...
v4.5.5
This update contains only security fixes and their test coverage.
All of these bugs are present in 4.5.4 and earlier. The get_layer4_v6 over-read is reached by reading an untrusted pcap with any of the tools. The remainder are reached through tcprewrite --fragroute=<rules-file> and require only an attacker-influenced fragroute rules file — no crafted packet content. Builds without libdnet (fragroute support disabled) are unaffected.
What's Changed
- SECURITY: fix heap buffer over-read in get_layer4_v6() (GHSA-jj65-mrgg-f5fx, CWE-125). get_ipv6_next() (src/common/get.c) validates the header it is given, but the pointer it returns is only checked with "ptr > end_ptr" — so it may be exactly end_ptr, or leave fewer bytes than a tcpr_ipv6_ext_hdr_base needs. get_layer4_v6() then read ip_nh straight off that pointer, one byte past the packet buffer, for a crafted IPv6 packet whose extension header chain runs to the end of the captured data. Reachable from tcpprep, tcprewrite and tcpreplay when reading an untrusted pcap. A bounds check is now applied at the three sites that dereference such a pointer. This was reported in 2024 against 4.4.4 and marked patched, but the reporter's proof-of-concept still reproduced on 4.5.4. Reported by msxfXF.
- SECURITY: fix stack buffer overflow in the fragroute rules-file parser (GHSA-777w-9599-w8g4, CWE-787). On a successful parse, mod_open() (src/fragroute/mod.c) accumulated a " -> -> ..." diagnostic of every parsed rule into a BUFSIZ (8192) local, then copied it into the caller's errbuf with an unbounded sprintf(); callers size errbuf at FRAGROUTE_ERRBUF_LEN (1024), so a rules file with a few hundred valid one-word directives overflowed a stack buffer in an ancestor frame (tcprewrite's main()) by several kilobytes, before any packet was processed. The message was written only on the success path, where errbuf is never read, so the whole diagnostic has been removed; the remaining errbuf writes in mod_open() are now bounded with snprintf(). Reported by tinyb0y.
- SECURITY: fix out-of-bounds write on an empty fragroute rules file (GHSA-p7xp-4gj2-x56c, CWE-787, CWE-191). The same removed diagnostic trimmed its trailing " -> " with buf[strlen(buf) - 4]. A rules file that parsed cleanly but produced no rules (empty, or only comments and blank lines) left buf empty, so the index underflowed size_t and wrote before the start of the buffer. Found while reviewing GHSA-777w-9599-w8g4; fixed by the same removal.
- SECURITY: fix off-by-one heap buffer overflow in the fragroute ip_chaff module (GHSA-m655-53p4-6qm8, CWE-193, CWE-787). ip_chaff_apply() (src/fragroute/mod_ip_chaff.c) called rand_strset() with (pkt_end - pkt_ip_data + 1), writing one byte past the end of the allocation pkt_dup() had just made; the call precedes the subtype switch, so it ran for every ip_chaff invocation (dup, opt, or a numeric TTL) on any ordinary IP packet. Also fixed in the same path: pkt_dup() (src/fragroute/pkt.c) never initialized the duplicate's pkt_buf_size, which ip_chaff's opt subtype then passed to ip_add_option() as a capacity bound, and its out-of-memory path freed the source packet — still linked in the caller's pktq — with free() rather than releasing the half-built duplicate with the matching brel(). Reported by tinyb0y.
- SECURITY: fix off-by-one heap buffer overflow in the fragroute tcp_chaff module (GHSA-v8c4-9w98-9v6v, CWE-193, CWE-787). tcp_chaff_apply() (src/fragroute/mod_tcp_chaff.c) had the identical defect in the sibling module that GHSA-m655-53p4-6qm8 covers, calling rand_strset() with (pkt_end - pkt_tcp_data + 1). Triggered by any tcp_chaff directive against an ordinary TCP packet. Found while auditing the ip_chaff fix.
- SECURITY: fix heap buffer overflow via negative fragment/segment size in the fragroute ip_frag and tcp_seg modules (GHSA-27v4-xhfx-g2rx, CWE-787, CWE-190). Both modules parsed their size argument with strtol() and rejected only zero; ip_frag's "must be a multiple of 8" check also passed negative multiples (-8 % 8 == 0 in C). A negative size then defeated the "does this fragment fit" guard (a signed comparison against a positive ptrdiff_t) and reached memcpy() as a huge implicitly-converted size_t, in ip_frag_apply_ipv4(), ip_frag_apply_ipv6() and tcp_seg_apply(). Both modules now require 1..IP_LEN_MAX at rules-file parse time, which also rejects strtol() overflow truncating to a negative int. Reported by tinyb0y.
- fragroute: check pkt_new()/pkt_dup() for failure in the ip_frag, tcp_seg and tcp_chaff modules. Six call sites dereferenced the returned packet immediately, so an allocation failure crashed on a NULL pointer instead of aborting the rule.
- fragroute: don't free a rule that is still linked into the rule list. mod_open()'s cleanup freed its last-allocated rule unconditionally; this was harmless only because the removed success-path diagnostic left the variable NULL, and would otherwise have left a dangling entry for mod_apply() to walk on every packet.
- test: add fragroute test cases (fragroute_valid, fragroute_negsize, fragroute_badrules) covering the modules above; previously the fragroute tree had no test coverage at all.
Full Changelog: v4.5.4...v4.5.5
Download the release by clicking the tcpreplay* assets below ...
v4.5.4
This update contains only security advisory updates.
What's Changed
- SECURITY: fix heap buffer overflow in the tcprewrite fragroute tcp_opt module (GHSA-pfqg-243f-8q25, CWE-787, CVSS 8.4). tcp_opt_apply() (src/fragroute/mod_tcp_opt.c) passed sizeof(pkt->pkt_data) - ETH_HDR_LEN as the buffer-capacity bound to inet_add_option(); pkt_data is a pointer, so sizeof() was 8 on 64-bit builds and the subtraction underflowed size_t to near SIZE_MAX, disabling the bounds check and letting libdnet's option-insertion memmove() write past the packet's heap allocation. The bound is now computed from the real buffer extent (pkt->pkt_buf + pkt->pkt_buf_size - pkt->pkt_ip). Reported by tinyb0y.
- SECURITY: fix heap buffer overflow in the tcprewrite fragroute ip6_opt module (GHSA-2wmf-4p77-784q, CWE-787). ip6_opt_apply() (src/fragroute/mod_ip6_opt.c) inserted IPv6 routing/extension headers with memmove()/memcpy() without checking that the inserted bytes fit the packet's remaining buffer headroom. Both the route and raw paths now verify pkt_end + offset stays within the allocation and skip the packet otherwise. Reported by tinyb0y.
- SECURITY: fix the same class of unchecked option-insertion overflow in the fragroute ip_opt module (src/fragroute/mod_ip_opt.c), which used a fixed PKT_BUF_LEN - ETH_HDR_LEN bound that overcounts capacity by the buffer's lead-alignment pad on small packets; it now uses the same real buffer-extent bound as tcp_opt.
- SECURITY: fix SEGV in the tcpedit DLT_JUNIPER_ETHER plugin when used as a decoder (GHSA-ww62-mxv7-pg55, CWE-704, CWE-843). dlt_jnpr_ether_post_init()/_cleanup()/_decode()/_proto()/_get_mac()/_l2len() (src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c) read ctx->encoder->config to get the plugin's own config, but this plugin only ever operates as a decoder (packet encoding is unsupported) - ctx->encoder pointed at an unrelated plugin's config struct, a type confusion that crashed tcpedit_dlt_getplugin() when processing DLT_JUNIPER_ETHER input, e.g. via tcprewrite --dlt=enet. Fixed by reading ctx->decoder->config in all six sites. Reported and originally patched by jiezhuzzz.
- SECURITY: fix crash in remove_settings() on failed text_mmap (GHSA-5cjx-62pj-pmgq, CWE-476). remove_settings() (libopts/save.c), reachable from the AutoOpts option-file save/rewrite path (e.g. via --load-opts), dereferenced the pointer returned by text_mmap() without checking for MAP_FAILED, unlike the other two callers in configfile.c. A missing/inaccessible/wrong-type/empty settings file crashed the program. Fixed with the same TEXT_MMAP_FAILED_ADDR() guard already used elsewhere in the codebase. Reported by jiezhuzzz.
- SECURITY: fix buffer overflow in the KHIAL sendpacket() path (GHSA-pjh6-6hrw-vcwr, CWE-787, CVSS 6.6). buffer_payload_size (src/common/sendpacket.c) was computed as sizeof(buffer) + sizeof(struct pcap_pkthdr) instead of sizeof(buffer) - sizeof(struct pcap_pkthdr), overstating the KHIAL send buffer's available space by 48 bytes (64-bit) and letting memcpy() overflow the static buffer for packets >= 10024 bytes sent via tcpreplay --intf1=; the following write() had the same problem in reverse, reading past the buffer into the character device. Fixed the calculation and added an explicit length check that rejects oversized packets before either memcpy or write() touch the buffer. Reported by 360AlphaLab.
Full Changelog: v4.5.3...v4.5.4
Download the release by clicking the tcpreplay* assets below ...
v4.5.3
Announcing Tcpreplay v4.5.3 This update contains mostly bug fixes.
What's Changed
- v4.5.3 by @fklassen in #980
- Refs #984: sendpacket bounded EAGAIN/ENOBUFS retry (merges #986 + fixups) by @fklassen in #996
- Bug #984 endless retries on enobufs eagain by @fklassen in #997
- gitignore: share .claude/skills/, document code-review-graph MCP setup by @fklassen in #998
- send_packets: fix miscalibrated overflow guards in calc_sleep_time (#974) by @fklassen in #999
- migrate autoopts -> getopt + asciidoctor by @GabrielGanne in #991
- Revert "migrate autoopts -> getopt + asciidoctor" by @fklassen in #1000
- fix fragroute overflow on packets with too many MPLS labels by @GabrielGanne in #993
- tcprewrite - fix adding vlan tag with missing options by @GabrielGanne in #994
- tcpprep - fix buffer overflow on truncated packets by @GabrielGanne in #995
- fragroute: fix TAILQ_FOREACH_REVERSE portability across queue.h variants by @fklassen in #1001
- sendpacket: stop inhibiting libbpf's default XDP program load for AF_XDP TX by @fklassen in #1002
- sendpacket: warn when sending on a down/no-carrier interface (Linux) by @fklassen in #1003
- tcpreplay: fix --multiplier timing drift (takes over #915) by @fklassen in #1004
- send_packets: bound and make abortable the netmap TX-ring drain wait by @fklassen in #1005
- netmap: remove stray switch defaults in nm_do_ioctl() breaking --netmap by @fklassen in #1006
- sendpacket: route "zc:" PF_RING ZC device names through libpcap by @fklassen in #1007
- tcprewrite: rewrite addresses embedded in ICMPv6 error messages by @fklassen in #1008
- docs: document autogen.sh requirement for git checkouts by @fklassen in #1009
- tcpliveplay: call pcap_breakloop() from the SIGALRM handler by @fklassen in #1010
- tcpedit: fix wrong end-of-buffer pointer in TCP-sequence/portmap rewrite by @fklassen in #1013
- tcpr_random: fix signed left-shift undefined behavior (does not fix #1011 - see comments) by @fklassen in #1012
- configure: stop --with-netmap from leaking -I/sys into CFLAGS by @fklassen in #1014
- update Copyright to 2026 by @fklassen in #1017
Full Changelog: v4.5.2...v4.5.3
Download the release by clicking the tcpreplay* assets below ...
v4.5.2
Announcing Tcpreplay v4.5.1. This update contains mostly bug fixes.
What's Changed
- feature: fix/recalculate header checksum for ipv6-frag(mentation) pac… by @ChuckCottrill in #900
- Feature #897 IPv6 frag checksum support by @fklassen in #932
- Bug #933 ci: hexdump failing pcap on test failure by @fklassen in #934
- Bug #935 AF_SCP memory leaks by @fklassen in #936
- Bug #896 AF_XDP: default --xdp-batch-size=1 by @fklassen in #937
- Bug #731 #904 various TX_RING link errors by @fklassen in #938
- configure.ac: Fixup the libpcap search loops. by @infrastation in #905
- Bug #905 configure libpcap search fix by @fklassen in #939
- Fix building on OpenBSD by @brad0 in #907
- Bug #907 fix build open bsd by @fklassen in #940
- do not exit in get_l2len_protocol() if l2len==0 by @GabrielGanne in #911
- Bug #911 continue on get l2len protcol is zero by @fklassen in #941
- fix compile, linker error for txring by @markyang92 in #924
- Bug #924 TX_RING: Compile issue by @fklassen in #942
- Bug #899 flows: correct IPv6 header version check by @fklassen in #943
- Bug #794 #901 mac addr altered on seed option by @fklassen in #944
- Bug #809 workaround for gcc 4.4.4 failure on FreeBSD by @fklassen in #945
- Bug #894 stop on --portmap syntax error by @fklassen in #946
- Bug #902 tcprewrite: SEGV on crafted capture by @fklassen in #949
- Bug #914 tcprewrite: ensure --infile & --outfile are not the same file by @fklassen in #950
- Bug #928 tcprep: heap addess leak in xX.c by @fklassen in #951
- Bug #947 - ensure defines.h is always regenerated by @fklassen in #964
- Bug #956 XDP - fail with a more informative error message by @fklassen in #965
- Bug #970 tcprewrite: --fixlen: do not use realloc by @fklassen in #975
- Bug #967 gcc15: fix out-of-tree build by @fklassen in #976
- Bug #977 C23 standard support by @fklassen in #978
- 4.5.2 beta3 -> 4.5.2 by @fklassen in #979
New Contributors
- @brad0 made their first contribution in #907
- @markyang92 made their first contribution in #924
Full Changelog: v4.5.1...v4.5.2
Download the release by clicking the tcpreplay* assets below ...
v4.5.1
Announcing v4.5.1
This release contains contributions from a record number of new contributors. This is greatly appreciated since I am a team of one, and do Tcpreplay maintenance in my spare time.
There are many bug fixes and new features. Most notable features:
- AF_XDP socket support - if you have a newer Linux kernel, you will be able to transmit at line rates without having to install 3rd party kernel modules (e.g. netmap, PF_RING)
-wtcpreplay option - this overrides the-ioption, and allows you to write to a PCAP file rather than an interface--includeand--excludetcpreplay options - allows replay of a list of specific packet numbers to replay. This may slow things down, so consider using in combination with-w.--fixhdrlentcpreplay option - added to control action on packet length changes-Wtcpreplay option - suppress warnings when replaying- SLL2( Linux "cooked" capture encapsulation v2)
- Haiku support
What's Changed
- Add support for LINUX_SLL2 by @btriller in #728
- Feature #727 - Linux SLL v2 by @fklassen in #820
- Bug #779 - honour overflow for all PPS values by @fklassen in #821
- AF_XDP socket extension using libxdp api by @plangarbalint in #797
- Feature #822 - AF_XDP socket extension by @fklassen in #823
- Nanosec accurate packet processing by @plangarbalint in #796
- Handle IPv6 fragment extension header by @ChuckCottrill in #832
- Bug #837 - handle IPv6 fragment extension header by @fklassen in #838
- Feature #796 - nanosecond packet processing by @fklassen in #836
- configure.ac: unify search dirs for pcap and add lib32 by @shr-project in #819
- Feature #839 - add pull request template by @fklassen in #840
- ipv6 - add check for extension header length by @GabrielGanne in #842
- Bug #827 PR #842 IPv6 extension header - staging by @fklassen in #859
- add check for empty cidr by @GabrielGanne in #843
- Bug #824 and PR #843: check for empty CIDR by @fklassen in #860
- Add option to turn on/off fix packet header length by @ChuckCottrill in #846
- Bug #703 #844 PR #846: optionally fix packet header length --fixhdrlen by @fklassen in #861
- Bug 863: fix nansecond timestamp regression by @fklassen in #865
- autotools - AC_HELP_STRING is obsolete in 2.70 by @GabrielGanne in #856
- some Haiku support by @infrastation in #847
- configure.ac: do not run conftest in case of cross compilation by @ChenQi1989 in #849
- dlt_jnpr_ether_cleanup: check config before cleanup by @Marsman1996 in #851
- Fix recursive tcpedit cleanup by @GabrielGanne in #855
- Bug #813: back out PR #855 by @fklassen in #866
- Bug #867 - run regfree() on close by @fklassen in #868
- Bug #869 tcpprep memory leak include exclude by @fklassen in #870
- Bug #811 - add check for invalid jnpr header length by @fklassen in #872
- Bug #792 avoid assertion and other fixes by @fklassen in #873
- Bug #844 tap: ignore TUNSETIFF EBUSY errors by @fklassen in #874
- Bug #876 - add missing free_umem_and_xsk function by @fklassen in #877
- Feature #878 - add -w / --suppress-warning option by @fklassen in #879
- Bug #835 false unsupported dlt warnings on 802.3 (Ethernet I) and LLC by @fklassen in #880
- Feature #884 include exclude options by @fklassen in #885
- Feature #853 direct traffic to pcap by @fklassen in #871
- Feature #853 restore missing -P command by @fklassen in #887
- Bug #888: check for map == NULL in cidr.c by @fklassen in #889
New Contributors
- @btriller made their first contribution in #728
- @plangarbalint made their first contribution in #797
- @ChuckCottrill made their first contribution in #832
- @shr-project made their first contribution in #819
- @infrastation made their first contribution in #847
- @ChenQi1989 made their first contribution in #849
Full Changelog: v4.4.4...v4.5.1
v4.4.4
This is Tcpreplay suite 4.4.4 This release contains bug fixes only.
What's Changed
- dlt_jnpr_ether_cleanup: check subctx before cleanup by @Marsman1996 in #781
- Bug #780 assert tcpedit dlt cleanup by @fklassen in #800
- Fix bugs caused by strtok_r by @Marsman1996 in #783
- Bug #782 #784 #785 #786 #787 #788 strtok r isuses by @fklassen in #801
- Update en10mb.c by @david-guti in #793
- PR #793 ip6 unicast flood by @fklassen in #802
- Bug #719 fix overflow check for parse_mpls() by @fklassen in #804
- PR #793 - update tests for corrected IPv6 MAC by @fklassen in #805
- PR #793 - update tests for vlandel by @fklassen in #806
- Feature #773 gh actions ci by @fklassen in #807
New Contributors
- @Marsman1996 made their first contribution in #781
- @david-guti made their first contribution in #793
Full Changelog: v4.4.3...v4.4.4
Download the release by clicking the tcpreplay* assets below ...
v4.4.3
This is Tcpreplay suite 4.4.3 This release contains bug fixes only.
What's Changed
- Feature #759: Upgrade autogen/libopts to 5.18.16 by @fklassen in #760
- Bug #751 don't exit after send error by @fklassen in #761
- Bug #750: configure: libpcap version robustness by @fklassen in #764
- Bug #749 flow stats: avoid overstating flow packet count by @fklassen in #765
- Bug #750 more libpcap version updates by @fklassen in #766
- Bug #767 tests: support for out-of-tree tests by @fklassen in #768
- Bug #750 - fix macOS test failure by @fklassen in #770
- 4.4.3 by @fklassen in #769 and #771
Full Changelog: v4.4.2...v4.4.3
Download the release by clicking the tcpreplay* assets below ...
v4.4.2
This is Tcpreplay suite 4.4.2
This release contains bug fixes only.
What's Changed
- Bug #716 heap-buffer-overflow in get_l2len_protocol() by @fklassen in #738
- Bug #721 fixed typo in tcpliveplay.c by @jonathan-dev in #721
- Bug #717 avoid assertion in get_layer4_v6 by @fklassen in #739
- Bug #718 improved heap-overflow protection by @fklassen in #740
- Bug #719 better overflow protection in parse_mpls by @fklassen in #741
- Bug #725 FORCE_ALIGN on arm by @fklassen in #742
- Bug #729 tcpreplay_edit: disallow both -K and -l options by @fklassen in #743
- Bug #735 heap-overflow in get_l2len_protocol by @fklassen in #744
- Bug #745 remove autogen.sh from distribution tarballs by @fklassen in #747
New Contributors
- @jonathan-dev made their first contribution in #721
Full Changelog: v4.4.1...v4.4.2