Project

General

Profile

Bug #8654 » gen_pcap.py

scapy python script to gen pcap - Shane Dugan, 06/12/2026 07:57 PM

 
#!/usr/bin/env python3
"""
Generate a pcap to reproduce QUIC TX accumulation (unbounded transaction growth).

Creates 340 flows x 100 packets of QUIC v1 Handshake long-header packets.
Each packet passes the QUIC probing parser and creates a TX via the non-Initial
code path (new_tx called, parser returns true). TXs accumulate because the
framework cannot free them fast enough when detection rules pin them for inspection.

Requires: scapy (pip install scapy)

Usage:
python3 gen_pcap.py
# produces quic-cpu-repro.pcap
"""

from scapy.all import Ether, IP, UDP, Raw, wrpcap
import struct
import os


def make_quic_handshake(dcid, scid, payload_size=128):
"""Build a QUIC v1 Long Header Handshake packet.

Parser path in quic.rs:
- from_bytes: is_long=true, version=0x00000001, type bits=0x02 -> QuicType::Handshake
- parse(): ty != Initial -> new_tx() called unconditionally, returns true
- No decryption attempted for non-Initial packets
"""
version = 0x00000001
# Long header: bit7=1, fixed bit6=1, type bits 4-5=0x02 (Handshake), pn_len bits=0x00
first_byte = 0xe0

header = bytearray()
header.append(first_byte)
header += struct.pack('>I', version)
header.append(len(dcid))
header += dcid
header.append(len(scid))
header += scid

payload = os.urandom(payload_size)
length_enc = struct.pack('>H', 0x4000 | len(payload))
header += length_enc

return bytes(header) + payload


def main():
packets = []
num_flows = 340
pkts_per_flow = 100

for flow_idx in range(num_flows):
src_ip = f"10.{(flow_idx >> 8) & 0xff}.{flow_idx & 0xff}.{(flow_idx % 254) + 1}"
src_port = 40000 + (flow_idx % 25000)
dst_ip = "20.209.226.33"
dst_port = 443

# Consistent DCID/SCID per flow
dcid = struct.pack('>Q', flow_idx + 1)
scid = struct.pack('>I', flow_idx + 1)

for pkt_idx in range(pkts_per_flow):
payload = make_quic_handshake(dcid, scid)
pkt = (
Ether() /
IP(src=src_ip, dst=dst_ip) /
UDP(sport=src_port, dport=dst_port) /
Raw(payload)
)
packets.append(pkt)

wrpcap("quic-cpu-repro.pcap", packets)
total_pkts = num_flows * pkts_per_flow
print(f"Written {total_pkts} packets ({num_flows} flows x {pkts_per_flow} pkts)")
print(f"to quic-cpu-repro.pcap")


if __name__ == "__main__":
main()
(1-1/4)