use bpaf::{Parser, construct, long};
use std::path::PathBuf;
use crate::cli::{Command, input_arg};
#[derive(Debug, Clone)]
pub struct ReplayArgs {
pub input: PathBuf,
pub interfaces: Vec<String>,
pub speed: Option<String>,
pub pps: Option<u64>,
pub proto: Option<String>,
pub src_ip: Vec<String>,
pub dst_ip: Vec<String>,
pub ip: Vec<String>,
pub src_port: Vec<String>,
pub dst_port: Vec<String>,
pub port: Vec<String>,
pub flow_id: Option<String>,
pub from: Option<String>,
pub to: Option<String>,
pub tcp_flags: Option<String>,
pub min_len: Option<u32>,
pub max_len: Option<u32>,
pub unidirectional: bool,
pub negate: bool,
pub filter_expr: Option<String>,
pub min_flow_packets: Option<u64>,
}
pub fn replay_cmd() -> impl Parser<Command> {
let input = input_arg();
let interfaces = long("interface")
.short('i')
.help(
"Network interface to transmit on (repeatable: each packet is sent to all interfaces)",
)
.argument::<String>("IFACE")
.many();
let speed = long("speed")
.help("Replay speed: positive multiplier (1.0 = real-time, 2.0 = 2× faster) or 'max'")
.argument::<String>("SPEED")
.optional();
let pps = long("pps")
.help("Fixed replay rate in packets per second (mutually exclusive with --speed)")
.argument::<u64>("N")
.optional();
let proto = long("proto")
.help("Comma-separated protocols to keep: tcp,udp,icmp,icmp6 or numbers")
.argument::<String>("PROTOS")
.optional();
let src_ip = long("src-ip")
.help("Source IP or CIDR to keep (repeatable, OR-ed)")
.argument::<String>("CIDR")
.many();
let dst_ip = long("dst-ip")
.help("Destination IP or CIDR to keep (repeatable, OR-ed)")
.argument::<String>("CIDR")
.many();
let ip = long("ip")
.help("Either-endpoint IP or CIDR (repeatable, OR-ed)")
.argument::<String>("CIDR")
.many();
let src_port = long("src-port")
.help("Source port or range to keep (repeatable)")
.argument::<String>("PORT")
.many();
let dst_port = long("dst-port")
.help("Destination port or range to keep (repeatable)")
.argument::<String>("PORT")
.many();
let port = long("port")
.help("Either-endpoint port or range (repeatable)")
.argument::<String>("PORT")
.many();
let flow_id = long("flow-id")
.help("Comma-separated hex flow IDs to retain")
.argument::<String>("IDS")
.optional();
let from = long("from")
.help("Keep packets at or after this datetime (RFC 3339 or Unix epoch seconds)")
.argument::<String>("DATETIME")
.optional();
let to = long("to")
.help("Keep packets at or before this datetime (RFC 3339 or Unix epoch seconds)")
.argument::<String>("DATETIME")
.optional();
let tcp_flags = long("tcp-flags")
.help("TCP flags filter, e.g. SYN, SYN+ACK, RST:exact")
.argument::<String>("FLAGS")
.optional();
let min_len = long("min-len")
.help("Minimum captured packet length in bytes")
.argument::<u32>("BYTES")
.optional();
let max_len = long("max-len")
.help("Maximum captured packet length in bytes")
.argument::<u32>("BYTES")
.optional();
let unidirectional = long("unidirectional")
.short('u')
.help("Compute flow IDs unidirectionally (default: bidirectional)")
.switch();
let negate = long("not")
.help("Invert the filter: keep packets that do NOT match")
.switch();
let filter_expr = long("filter")
.short('f')
.help("tcpdump/libpcap BPF expression, e.g. \"tcp and dst port 443\"")
.argument::<String>("EXPR")
.optional();
let min_flow_packets = long("min-flow-packets")
.help("Only include flows with at least N packets (pre-scan pass; non-IP packets excluded)")
.argument::<u64>("N")
.optional();
construct!(ReplayArgs {
interfaces,
speed,
pps,
proto,
src_ip,
dst_ip,
ip,
src_port,
dst_port,
port,
flow_id,
from,
to,
tcp_flags,
min_len,
max_len,
unidirectional,
negate,
filter_expr,
min_flow_packets,
input,
})
.to_options()
.descr("Replay a PCAP capture onto a live network interface (Linux; requires CAP_NET_RAW)")
.command("replay")
.map(|a| Command::Replay(Box::new(a)))
}