|
#!/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()
|