# PCAP File Support & Deterministic Replay Engine

NimPacket includes a pure Nim PCAP module - no libpcap, no external C dependencies. Read and write standard `.pcap` files that open in Wireshark, get capture statistics, filter by protocol, and replay traffic with precise timing control.

## Features

- **Pure Nim** - Zero external dependencies, works everywhere Nim compiles
- **Wireshark compatible** - Standard PCAP format (magic `0xA1B2C3D4`, version 2.4)
- **Byte order detection** - Automatically handles both little-endian and big-endian PCAP files
- **Protocol detection** - Identifies TCP, UDP, ICMP, ICMPv6, ARP, DNS, DHCP inside packets
- **Statistics** - Packet count, capture duration, size distribution, protocol breakdown
- **Filtering** - Filter captures by protocol and write to new files
- **Deterministic replay** - Exact inter-packet timing with adjustable speed and flow-level filtering
- **Auto link-type detection** - Knows when you're writing raw IP vs Ethernet frames

## Quick Start

### Python

```python
import nimpacket as np

# Craft packets and save to PCAP
syn = np.build_tcp_syn_packet("192.168.1.1", "10.0.0.1", 54321, 80)
udp = np.build_udp_packet("192.168.1.1", "10.0.0.2", 12345, 5000)
np.write_pcap("crafted.pcap", [syn, udp])

# Read a capture file
data = np.read_pcap("crafted.pcap")
print(f"Packets: {data['packet_count']}")

# Get stats
stats = np.pcap_stats("crafted.pcap")
print(f"TCP: {stats['protocols']['tcp']}, UDP: {stats['protocols']['udp']}")

# Filter by protocol
np.filter_pcap("crafted.pcap", "tcp_only.pcap", "tcp")

# Replay at 2x speed, only TCP traffic to port 80
np.replay("crafted.pcap", speed=2.0, protocol="tcp", dst_port=80, verbose=True)
```

### CLI

```bash
# Read packets
nimpacket pcap read capture.pcap
nimpacket pcap read capture.pcap --summary
nimpacket pcap read capture.pcap --packet 5
nimpacket pcap read capture.pcap --json

# Statistics
nimpacket pcap stats capture.pcap
nimpacket pcap stats capture.pcap --json

# Write a packet to PCAP
nimpacket pcap write output.pcap --hex "450000280001..."

# Filter by protocol
nimpacket pcap filter capture.pcap --protocol tcp --output tcp_only.pcap

# Replay with timing control
nimpacket pcap replay capture.pcap --speed 2.0
nimpacket pcap replay capture.pcap --protocol tcp --dst-port 443 --schedule
nimpacket pcap replay capture.pcap --speed 0.5 --dry-run
nimpacket pcap replay capture.pcap --loop --count 3
```

### Nim

```nim
import nimpacket

# Read a PCAP file
let capture = readPcap("traffic.pcap")
echo pcapStats(capture)

# Filter by protocol
let tcpOnly = filterPcapByProtocol(capture, "tcp")
writePcap("tcp_only.pcap", tcpOnly)

# Write raw packets to PCAP
let pkt = (myIp / myTcp).toBytes()
writePcap("crafted.pcap", @[pkt])

# Build a replay schedule
var config = newReplayConfig(speed = 2.0)
config.filterProtocol = "tcp"
config.filterDstPort = 443
let schedule = replaySchedule(capture, config)
for item in schedule:
  echo "delay: ", item.delayUs, "us, size: ", item.data.len, "B"
```

---

## PCAP File Format

NimPacket implements the standard PCAP file format:

| Section | Size | Description |
|---------|------|-------------|
| Global Header | 24 bytes | Magic number, version, snaplen, link type |
| Packet Header | 16 bytes | Timestamp (sec + usec), captured length, original length |
| Packet Data | Variable | Raw packet bytes |

- **Magic number**: `0xA1B2C3D4` (little-endian) or `0xD4C3B2A1` (big-endian)
- **Version**: 2.4
- **Link types**: Ethernet (1), Raw IP (101)

---

## Python API Reference

### `read_pcap(filename) -> dict`

Read a PCAP file and return all packets.

```python
data = np.read_pcap("capture.pcap")
# Returns:
# {
#   "global_header": {"magic": "0xA1B2C3D4", "version_major": 2, ...},
#   "packets": [{"ts_sec": 1234, "ts_usec": 0, "incl_len": 40, "data_hex": "4500..."}, ...],
#   "packet_count": 42
# }
```

### `write_pcap(filename, packets, timestamps=None)`

Write packets to a PCAP file. Auto-detects link type (raw IP vs Ethernet).

```python
# Simple - auto timestamps
np.write_pcap("output.pcap", [packet_bytes_1, packet_bytes_2])

# With explicit timestamps
np.write_pcap("output.pcap", [pkt1, pkt2], timestamps=[(1000, 0), (1000, 500000)])
```

### `pcap_stats(filename) -> dict`

Get statistics for a PCAP file.

```python
stats = np.pcap_stats("capture.pcap")
# Returns:
# {
#   "packet_count": 42,
#   "duration": 12.345,
#   "total_bytes": 65432,
#   "min_packet_size": 28,
#   "max_packet_size": 1500,
#   "avg_packet_size": 1557.9,
#   "protocols": {"tcp": 30, "udp": 10, "icmp": 2, "arp": 0, "dns": 0, "other": 0}
# }
```

### `filter_pcap(input_file, output_file, protocol)`

Filter a PCAP file by protocol and write matching packets to a new file.

```python
np.filter_pcap("capture.pcap", "tcp_only.pcap", "tcp")
np.filter_pcap("capture.pcap", "dns_only.pcap", "dns")
```

Supported protocols: `"tcp"`, `"udp"`, `"icmp"`, `"arp"`, `"dns"`, `"dhcp"`

### `pcap_packet_count(filename) -> int`

Quick packet count.

```python
count = np.pcap_packet_count("capture.pcap")
```

---

## Deterministic Replay Engine

The replay engine is the core differentiator. It preserves exact inter-packet timing from the original capture, lets you speed up or slow down, and filter to specific flows. Same input always produces the same output - fully deterministic.

### Why This Matters

- **IDS/IPS testing** - Replay known-bad traffic at the firewall
- **Lab environments** - Reproduce exact network conditions
- **Research** - Repeatable experiments with real packet timing
- **Performance testing** - Replay at 10x speed to stress-test systems

### `replay_schedule(filename, speed=1.0, **filters) -> list`

Build a replay schedule without actually sending anything. Returns a list of `{"delay_us": int, "data_hex": str}` entries.

```python
# Original speed
schedule = np.replay_schedule("capture.pcap")

# 2x speed, only HTTPS traffic
schedule = np.replay_schedule("capture.pcap", speed=2.0, protocol="tcp", dst_port=443)

# Slow motion, packets 10-50 only
schedule = np.replay_schedule("capture.pcap", speed=0.5, start_packet=10, end_packet=50)
```

**Filter parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `speed` | float | Speed multiplier (1.0 = original, 2.0 = double, 0.5 = half) |
| `protocol` | str | Filter: `"tcp"`, `"udp"`, `"icmp"`, `"arp"`, `"dns"`, `"dhcp"` |
| `src_ip` | str | Filter by source IP address |
| `dst_ip` | str | Filter by destination IP address |
| `src_port` | int | Filter by source port |
| `dst_port` | int | Filter by destination port |
| `start_packet` | int | Start from this packet index (0-based) |
| `end_packet` | int | End at this packet index (-1 = all) |

### `replay(filename, speed=1.0, **kwargs) -> int`

Execute a replay with actual timing delays. Returns the number of packets replayed.

```python
# Simple replay at original speed
np.replay("capture.pcap", verbose=True)

# Fast replay of specific flow
np.replay("capture.pcap", speed=5.0, protocol="tcp", dst_port=80)

# Loop 3 times
np.replay("capture.pcap", loop=True, count=3, verbose=True)

# Custom callback for each packet
def on_packet(index, delay_us, data):
    print(f"Packet {index}: {len(data)} bytes")
    return True  # return False to stop

np.replay("capture.pcap", callback=on_packet)
```

**Additional parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `loop` | bool | Loop replay (default: False) |
| `count` | int | Number of loops when `loop=True` (0 = infinite) |
| `callback` | callable | Called for each packet: `callback(index, delay_us, data_bytes)`. Return `False` to stop. |
| `verbose` | bool | Print progress to stdout |

---

## CLI Reference

### `pcap read`

Read and display packets from a PCAP file.

```bash
nimpacket pcap read capture.pcap                  # Full hex dump of all packets
nimpacket pcap read capture.pcap --summary        # One-line-per-packet overview
nimpacket pcap read capture.pcap --packet 5       # Show only packet #5
nimpacket pcap read capture.pcap --json           # JSON output
```

### `pcap stats`

Show capture file statistics.

```bash
nimpacket pcap stats capture.pcap
nimpacket pcap stats capture.pcap --json
```

Example output:
```
PCAP Stats: capture.pcap
  Packets: 142
  Duration: 12.345s
  Total bytes: 98234
  Avg packet size: 691.8 bytes
  Min packet size: 54 bytes
  Max packet size: 1514 bytes
  Link type: 1
  Snaplen: 65535
  Protocols:
    TCP: 120
    UDP: 15
    ICMP: 5
    DNS: 2
```

### `pcap write`

Write packet data to a PCAP file.

```bash
nimpacket pcap write output.pcap --hex "450000280001000040060000c0a80101..."
nimpacket pcap write output.pcap --file raw_packet.bin
```

### `pcap filter`

Filter a capture by protocol and save to new file.

```bash
nimpacket pcap filter capture.pcap --protocol tcp --output tcp_only.pcap
nimpacket pcap filter capture.pcap --protocol dns --output dns_traffic.pcap
```

### `pcap replay`

Replay packets with timing control.

```bash
# Show replay schedule
nimpacket pcap replay capture.pcap --schedule
nimpacket pcap replay capture.pcap --schedule --speed 2.0 --json

# Dry run (show what would happen)
nimpacket pcap replay capture.pcap --dry-run --speed 5.0

# Actual replay
nimpacket pcap replay capture.pcap --speed 2.0
nimpacket pcap replay capture.pcap --protocol tcp --dst-port 443

# Loop
nimpacket pcap replay capture.pcap --loop --count 5 --speed 10.0

# Filter combinations
nimpacket pcap replay capture.pcap --protocol udp --src-ip 192.168.1.1 --speed 0.5
```

---

## Nim API Reference

### Types

```nim
type
  PcapGlobalHeader* = object
    magicNumber*: uint32     # 0xA1B2C3D4
    versionMajor*: uint16    # 2
    versionMinor*: uint16    # 4
    thiszone*: int32         # GMT offset (usually 0)
    sigfigs*: uint32         # Timestamp accuracy (usually 0)
    snaplen*: uint32         # Max captured packet length
    network*: uint32         # Link type (1=Ethernet, 101=Raw IP)

  PcapPacketHeader* = object
    tsSec*: uint32           # Timestamp seconds (Unix epoch)
    tsUsec*: uint32          # Timestamp microseconds
    inclLen*: uint32         # Captured length
    origLen*: uint32         # Original length on wire

  PcapPacket* = object
    header*: PcapPacketHeader
    data*: seq[byte]

  PcapFile* = object
    globalHeader*: PcapGlobalHeader
    packets*: seq[PcapPacket]

  ReplayConfig* = object
    speed*: float            # 1.0 = original, 2.0 = 2x fast
    loop*: bool
    count*: int
    startPacket*: int
    endPacket*: int          # -1 = all
    filterProtocol*: string
    filterSrcIP*: string
    filterDstIP*: string
    filterDstPort*: int
    filterSrcPort*: int
```

### Functions

| Function | Description |
|----------|-------------|
| `readPcap(filename): PcapFile` | Read a complete PCAP file |
| `writePcap(filename, pcapFile)` | Write a PcapFile to disk |
| `writePcap(filename, packets)` | Write raw byte arrays with auto timestamps |
| `pcapStats(pcapFile): string` | Human-readable statistics |
| `pcapStatsJson(pcapFile): string` | Stats as JSON |
| `filterPcapByProtocol(pcapFile, protocol): PcapFile` | Filter by protocol |
| `filterPcapByFlow(pcapFile, config): PcapFile` | Filter by flow criteria |
| `replaySchedule(pcapFile, config): seq[...]` | Build deterministic replay schedule |
| `packetSummary(pkt, index, linkType): string` | One-line packet summary |
| `pcapPacketCount(filename): int` | Quick packet count |

### Constants

```nim
const
  PCAP_MAGIC_LE* = 0xA1B2C3D4'u32
  PCAP_MAGIC_BE* = 0xD4C3B2A1'u32
  PCAP_LINKTYPE_ETHERNET* = 1'u32
  PCAP_LINKTYPE_RAW* = 101'u32
```

---

## Tests

28 unit tests covering:

- Global header serialization roundtrip
- Packet header serialization roundtrip
- File I/O (empty, single packet, multi-packet)
- TCP SYN write-read-parse integration
- Statistics (empty, multi-protocol)
- Protocol filtering (TCP/UDP/ICMP)
- Big-endian byte order detection
- Replay schedule (1x, 2x, 0.5x speed)
- Replay flow filtering (protocol, port)
- Replay packet range selection
- Replay determinism verification
- Empty replay (no matching packets)

```bash
nim c -r --path:src tests/test_pcap.nim
```
