netring 0.6.0

High-performance zero-copy packet I/O for Linux (AF_PACKET TPACKET_V3 + AF_XDP)
Documentation
# Troubleshooting

## Common Errors

### `Error::PermissionDenied` — "insufficient privileges (need CAP_NET_RAW)"

AF_PACKET sockets require `CAP_NET_RAW`.

**Fix (option 1): Run as root**
```bash
sudo cargo run --example capture
```

**Fix (option 2): Set capability on binary**
```bash
cargo build --example capture --release
sudo setcap cap_net_raw+ep target/release/examples/capture
./target/release/examples/capture eth0
```

**Fix (option 3): Add to group (distro-dependent)**
```bash
sudo usermod -aG netdev $USER  # then re-login
```

### `Error::InterfaceNotFound` — "interface not found: eth0"

The interface name doesn't exist on this system.

```bash
# List available interfaces
ip link show
# Common names: lo, eth0, ens33, enp0s3, wlan0
```

### `Error::Mmap` with ENOMEM

The ring buffer is too large for available memory.

- `CaptureBuilder` automatically retries with smaller rings (down to 25%)
- Reduce `block_count` or `block_size` manually
- Increase locked memory limit: `ulimit -l unlimited`

### `Error::SockOpt("PACKET_IGNORE_OUTGOING")` on older kernels

`PACKET_IGNORE_OUTGOING` was added in Linux 4.20. On older kernels:
```rust
// Don't use .ignore_outgoing(true) on kernels < 4.20
let cap = Capture::builder()
    .interface("eth0")
    // .ignore_outgoing(true)  // skip this
    .build()?;
```

### `Error::SockOpt("SO_BUSY_POLL")`

Requires `CONFIG_NET_RX_BUSY_POLL` in kernel config. Not all kernels have it.

## Performance Issues

### High drop count in stats

```rust
let stats = cap.stats()?;
if stats.drops > 0 {
    // Ring is filling faster than you process
}
```

**Solutions:**
1. Increase `block_count` (more buffer)
2. Use `fanout()` to distribute across threads
3. Attach a BPF filter to drop unwanted packets in kernel
4. Do less work in the capture loop — copy and process elsewhere
5. Use `ignore_outgoing(true)` if you don't need TX packets

### Sequence gaps in batch processing

```
WARNING: block sequence gap: expected 5, got 8 (dropped 3 blocks)
```

Means the kernel ran out of blocks to fill — your processing was too slow
and the kernel had to recycle blocks you hadn't returned yet.

Same solutions as high drop count above.

### Block timeout not working (batch never arrives on sparse traffic)

If `block_timeout_ms(0)` is set, blocks only retire when full. On low-traffic
interfaces, this means you may never see a batch.

**Fix:** Use a non-zero timeout (default is 60ms).

### `mmap with MAP_LOCKED failed, retrying without`

This warning means `MAP_LOCKED` failed (missing `CAP_IPC_LOCK`). The ring
still works but pages may be swapped to disk under memory pressure.

**Fix:**
```bash
ulimit -l unlimited
# Or: sudo setcap cap_ipc_lock+ep /path/to/binary
```

## Fanout Issues

### All packets going to one socket

- Verify all sockets use the **same** `group_id`
- Verify all sockets are bound to the **same** interface
- Try `FanoutFlags::DEFRAG` to reassemble IP fragments before hashing
- Check that traffic has varied flows (same src/dst → same socket with Hash mode)

### Fanout `setsockopt` fails

Fanout must be called **after** `bind()`. The builder handles this automatically,
but if using the low-level socket API directly, ensure ordering.

## Testing Without a Network

Use the loopback interface (`lo`) — it always exists:

```rust
let cap = Capture::open("lo")?;
```

Generate test traffic:
```bash
# In another terminal
ping 127.0.0.1
# Or
nc -u 127.0.0.1 12345 <<< "test packet"
```