dwd/
cmd.rs

1use core::{error::Error, net::SocketAddr, num::NonZero};
2use std::path::PathBuf;
3
4use clap::{ArgAction, Parser};
5use pnet::ipnetwork::IpNetwork;
6
7use crate::engine::{
8    http::{payload::jsonline::JsonLineRecord, Config as HttpConfig},
9    udp::Config as UdpConfig,
10};
11
12/// The traffic generator we deserve.
13#[derive(Debug, Clone, Parser)]
14#[command(version, about)]
15#[command(flatten_help = true)]
16pub struct Cmd {
17    #[clap(subcommand)]
18    pub mode: ModeCmd,
19    /// Path to the generator file.
20    /// See /etc/dwd/generator.yaml for details.
21    #[clap(long, global = true)]
22    pub generator: Option<PathBuf>,
23    /// Be verbose in terms of logging.
24    #[clap(short, action = ArgAction::Count, global = true)]
25    pub verbose: u8,
26}
27
28#[derive(Debug, Clone, Parser)]
29pub enum ModeCmd {
30    /// HTTP mode.
31    Http(HttpCmd),
32    /// Fast, but restricted HTTP mode.
33    #[command(name = "http/raw")]
34    HttpRaw(HttpRawCmd),
35    /// UDP mode.
36    ///
37    /// Response packets (if any) will be ignored.
38    Udp(UdpCmd),
39    #[cfg(feature = "dpdk")]
40    /// DPDK mode.
41    ///
42    /// This mode is capable of generating very intensive workload by utilizing
43    /// the DPDK library.
44    /// The core idea is kernel bypass and working with NIC entirely in
45    /// userspace.
46    ///
47    /// Note, that this mode requires the application to be run with CAP_ADMIN
48    /// capabilities.
49    Dpdk(DpdkCmd),
50}
51
52#[derive(Debug, Clone, Parser)]
53pub struct HttpRawCmd {
54    #[clap(flatten)]
55    pub cmd: HttpCmd,
56}
57
58#[derive(Debug, Clone, Parser)]
59pub struct HttpCmd {
60    /// Target endpoint.
61    #[clap(required = true)]
62    pub addr: SocketAddr,
63    /// Native workload settings.
64    #[clap(flatten)]
65    pub native: NativeLoadCmd,
66    /// Number of parallel jobs.
67    ///
68    /// This also limits the maximum concurrent requests in flight. To achieve
69    /// better runtime characteristics this value should be the multiple of
70    /// the number of threads.    
71    #[clap(short, long, default_value_t = std::thread::available_parallelism().unwrap_or(NonZero::<usize>::MIN))]
72    pub concurrency: NonZero<usize>,
73    /// Path to the JSON payload file.        
74    #[clap(long, value_name = "PATH", required = true)]
75    pub payload_json: Option<PathBuf>,
76    /// Set linger TCP option with specified value.
77    #[clap(long)]
78    pub tcp_linger: Option<u64>,
79    /// Enable SOCK_NODELAY socket option.
80    #[clap(long)]
81    pub tcp_no_delay: bool,
82}
83
84impl<T> TryFrom<HttpCmd> for HttpConfig<T>
85where
86    T: TryFrom<JsonLineRecord, Error = Box<dyn Error>>,
87{
88    type Error = Box<dyn Error>;
89
90    fn try_from(cmd: HttpCmd) -> Result<Self, Self::Error> {
91        let HttpCmd {
92            addr,
93            native,
94            concurrency,
95            payload_json,
96            tcp_linger,
97            tcp_no_delay,
98        } = cmd;
99
100        let requests = {
101            if let Some(path) = payload_json {
102                JsonLineRecord::from_fs(path)?
103            } else {
104                todo!();
105            }
106        };
107
108        let m = Self {
109            addr,
110            native: native.try_into()?,
111            concurrency,
112            tcp_linger,
113            tcp_no_delay,
114            requests,
115        };
116
117        Ok(m)
118    }
119}
120
121#[derive(Debug, Clone, Parser)]
122pub struct UdpCmd {
123    /// Target endpoint.
124    #[clap(required = true)]
125    pub addr: SocketAddr,
126    /// Native workload settings.
127    #[clap(flatten)]
128    pub native: NativeLoadCmd,
129}
130
131impl TryFrom<UdpCmd> for UdpConfig {
132    type Error = Box<dyn Error>;
133
134    fn try_from(v: UdpCmd) -> Result<Self, Self::Error> {
135        let UdpCmd { addr, native } = v;
136
137        let native = native.try_into()?;
138
139        let m = Self { addr, native };
140
141        Ok(m)
142    }
143}
144
145/// Native workload config.
146#[derive(Debug, Clone, Parser)]
147pub struct NativeLoadCmd {
148    /// Number of threads.
149    #[clap(short, long, default_value_t = std::thread::available_parallelism().unwrap_or(NonZero::<usize>::MIN))]
150    pub threads: NonZero<usize>,
151    /// Maximum number of requests executed per socket before reconnection.
152    ///
153    /// If none given (the default) sockets renew is disabled.
154    #[clap(long)]
155    pub requests_per_socket: Option<u64>,
156    /// IP prefix used to collect this machine's global unicast IP addresses and
157    /// use them as bind addresses.
158    ///
159    /// For example, specifying "2a02:6b8:0:320:1111:1111:1111::/112" will scan
160    /// all interfaces, collect all global unicast IP addresses and filter
161    /// them by the given prefix. This option conflicts with the "bind-ips"
162    /// argument.
163    #[clap(long)]
164    pub bind_network: Option<IpNetwork>,
165}
166
167#[derive(Debug, Clone, Parser)]
168pub struct DpdkCmd {
169    /// Path to the DPDK configuration file in YAML format.
170    #[clap(long, required = true)]
171    pub dpdk_path: PathBuf,
172    /// Path to the PCAP file (not pcapng!).
173    ///
174    /// Packets containing in this file will be used as a workload, looping
175    /// infinitely until profile exhaustion.
176    #[clap(long, required = true)]
177    pub pcap_path: PathBuf,
178}