1use clap::{Parser, Subcommand};
6use eui48::MacAddress;
7use flex_fuchsia_wlan_common as wlan_common;
8use flex_fuchsia_wlan_policy as wlan_policy;
9
10#[derive(PartialEq, Copy, Clone, Debug, clap::ValueEnum)]
11pub enum RoleArg {
12 Client,
13 Ap,
14}
15
16#[derive(PartialEq, Copy, Clone, Debug, clap::ValueEnum)]
17pub enum ScanTypeArg {
18 Active,
19 Passive,
20}
21
22#[derive(PartialEq, Copy, Clone, Debug, clap::ValueEnum)]
23pub enum SecurityTypeArg {
24 None,
25 Wep,
26 Wpa,
27 Wpa2,
28 Wpa3,
29}
30
31#[derive(PartialEq, Copy, Clone, Debug, clap::ValueEnum)]
32pub enum CredentialTypeArg {
33 None,
34 Psk,
35 Password,
36}
37
38impl From<RoleArg> for wlan_common::WlanMacRole {
39 fn from(arg: RoleArg) -> Self {
40 match arg {
41 RoleArg::Client => wlan_common::WlanMacRole::Client,
42 RoleArg::Ap => wlan_common::WlanMacRole::Ap,
43 }
44 }
45}
46
47impl From<ScanTypeArg> for wlan_common::ScanType {
48 fn from(arg: ScanTypeArg) -> Self {
49 match arg {
50 ScanTypeArg::Active => wlan_common::ScanType::Active,
51 ScanTypeArg::Passive => wlan_common::ScanType::Passive,
52 }
53 }
54}
55
56impl From<SecurityTypeArg> for wlan_policy::SecurityType {
57 fn from(arg: SecurityTypeArg) -> Self {
58 match arg {
59 SecurityTypeArg::r#None => wlan_policy::SecurityType::None,
60 SecurityTypeArg::Wep => wlan_policy::SecurityType::Wep,
61 SecurityTypeArg::Wpa => wlan_policy::SecurityType::Wpa,
62 SecurityTypeArg::Wpa2 => wlan_policy::SecurityType::Wpa2,
63 SecurityTypeArg::Wpa3 => wlan_policy::SecurityType::Wpa3,
64 }
65 }
66}
67
68impl From<PolicyNetworkConfig> for wlan_policy::NetworkConfig {
69 fn from(arg: PolicyNetworkConfig) -> Self {
70 let credential = match arg.credential_type {
71 Some(CredentialTypeArg::r#None) => wlan_policy::Credential::None(wlan_policy::Empty),
72 Some(CredentialTypeArg::Psk) => {
73 wlan_policy::Credential::Psk(parse_psk_string(arg.credential.unwrap()))
74 }
75 Some(CredentialTypeArg::Password) => {
76 wlan_policy::Credential::Password(arg.credential.unwrap().as_bytes().to_vec())
77 }
78 None => {
79 credential_from_string(arg.credential.unwrap_or_else(|| "".to_string()))
81 }
82 };
83
84 let security_type = security_type_from_args(arg.security_type, &credential);
85
86 let network_id = wlan_policy::NetworkIdentifier {
87 ssid: arg.ssid.as_bytes().to_vec(),
88 type_: security_type,
89 };
90 wlan_policy::NetworkConfig {
91 id: Some(network_id),
92 credential: Some(credential),
93 ..Default::default()
94 }
95 }
96}
97
98fn parse_psk_string(credential: String) -> Vec<u8> {
101 let psk_arg = credential.as_bytes().to_vec();
102 hex::decode(psk_arg).expect(
103 "Error: PSK must be 64 hexadecimal characters.\
104 Example: \"123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234\"",
105 )
106}
107
108fn credential_from_string(credential: String) -> wlan_policy::Credential {
111 match credential.len() {
112 0 => wlan_policy::Credential::None(wlan_policy::Empty),
113 0..=63 => wlan_policy::Credential::Password(credential.into_bytes()),
114 64 => wlan_policy::Credential::Psk(parse_psk_string(credential)),
115 65..=usize::MAX => {
116 panic!(
117 "Provided credential is too long. A password must be between 0 and 63 \
118 characters and a PSK must be 64 hexadecimal characters. Provided \
119 credential is {} characters.",
120 credential.len()
121 );
122 }
123 _ => {
124 panic!("Invalid credential of length {}", credential.len())
126 }
127 }
128}
129
130fn security_type_from_args(
133 security_arg: Option<SecurityTypeArg>,
134 credential: &wlan_policy::Credential,
135) -> wlan_policy::SecurityType {
136 if let Some(arg) = security_arg {
137 match arg {
138 SecurityTypeArg::Wep => wlan_policy::SecurityType::Wep,
139 SecurityTypeArg::Wpa => wlan_policy::SecurityType::Wpa,
140 SecurityTypeArg::Wpa2 => wlan_policy::SecurityType::Wpa2,
141 SecurityTypeArg::Wpa3 => wlan_policy::SecurityType::Wpa3,
142 SecurityTypeArg::r#None => wlan_policy::SecurityType::None,
143 }
144 } else {
145 match credential {
146 wlan_policy::Credential::None(_) => wlan_policy::SecurityType::None,
147 _ => wlan_policy::SecurityType::Wpa2,
148 }
149 }
150}
151
152#[derive(clap::Args, Clone, Debug)]
153pub struct PolicyNetworkConfig {
154 #[arg(long)]
155 pub ssid: String,
156 #[arg(long = "security-type", value_enum, ignore_case = true)]
157 pub security_type: Option<SecurityTypeArg>,
158 #[arg(long = "credential-type", value_enum, ignore_case = true)]
159 pub credential_type: Option<CredentialTypeArg>,
160 #[arg(long)]
161 pub credential: Option<String>,
162}
163
164#[derive(clap::Args, Clone, Debug)]
165pub struct ForgetArgs {
166 #[arg(long)]
167 pub ssid: String,
168 #[arg(long = "security-type", value_enum, ignore_case = true)]
169 pub security_type: Option<SecurityTypeArg>,
170}
171
172impl ForgetArgs {
173 pub fn parse_security(&self) -> Option<wlan_policy::SecurityType> {
174 self.security_type.map(|s| s.into())
175 }
176}
177
178#[derive(clap::Args, Clone, Debug)]
179pub struct SaveNetworkArgs {
180 #[arg(long)]
181 pub ssid: String,
182 #[arg(long = "security-type", value_enum, ignore_case = true)]
183 pub security_type: SecurityTypeArg,
184 #[arg(long = "credential-type", value_enum, ignore_case = true)]
185 pub credential_type: Option<CredentialTypeArg>,
186 #[arg(long)]
187 pub credential: String,
188}
189
190#[derive(clap::Args, Clone, Debug)]
191pub struct ConnectArgs {
192 #[arg(long)]
193 pub ssid: String,
194 #[arg(long = "security-type", value_enum, ignore_case = true)]
195 pub security_type: Option<SecurityTypeArg>,
196}
197
198#[derive(Subcommand, Clone, Debug)]
199pub enum PolicyClientCmd {
200 #[command(name = "connect")]
201 Connect(ConnectArgs),
202 #[command(name = "list-saved-networks")]
203 GetSavedNetworks,
204 #[command(name = "listen")]
205 Listen,
206 #[command(name = "forget-network")]
207 ForgetNetwork(ForgetArgs),
208 #[command(name = "save-network")]
209 SaveNetwork(PolicyNetworkConfig),
210 #[command(name = "scan")]
211 ScanForNetworks,
212 #[command(name = "start-client-connections")]
213 StartClientConnections,
214 #[command(name = "stop-client-connections")]
215 StopClientConnections,
216 #[command(name = "dump-config")]
217 DumpConfig,
218 #[command(name = "restore-config")]
219 RestoreConfig { serialized_config: String },
220 #[command(name = "status")]
221 Status,
222}
223
224#[derive(Subcommand, Clone, Debug)]
225pub enum PolicyAccessPointCmd {
226 #[command(name = "start")]
228 Start(PolicyNetworkConfig),
229 #[command(name = "stop")]
230 Stop(PolicyNetworkConfig),
231 #[command(name = "stop-all")]
232 StopAllAccessPoints,
233 #[command(name = "listen")]
234 Listen,
235 #[command(name = "status")]
236 Status,
237}
238
239#[derive(Subcommand, Clone, Debug)]
240pub enum DeprecatedConfiguratorCmd {
241 #[command(name = "suggest-mac")]
242 SuggestAccessPointMacAddress {
243 #[arg(required = true)]
244 mac: MacAddress,
245 },
246}
247
248#[derive(Parser, Clone, Debug)]
249pub enum Opt {
250 #[command(subcommand, name = "client")]
251 Client(PolicyClientCmd),
252 #[command(subcommand, name = "ap")]
253 AccessPoint(PolicyAccessPointCmd),
254 #[command(subcommand, name = "deprecated")]
255 Deprecated(DeprecatedConfiguratorCmd),
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[fuchsia::test]
264 fn test_construct_config_wep() {
265 test_construct_config_security(wlan_policy::SecurityType::Wep, SecurityTypeArg::Wep);
266 }
267
268 #[fuchsia::test]
270 fn test_construct_config_wpa() {
271 test_construct_config_security(wlan_policy::SecurityType::Wpa, SecurityTypeArg::Wpa);
272 }
273
274 #[fuchsia::test]
276 fn test_construct_config_wpa2() {
277 test_construct_config_security(wlan_policy::SecurityType::Wpa2, SecurityTypeArg::Wpa2);
278 }
279
280 #[fuchsia::test]
282 fn test_construct_config_wpa3() {
283 test_construct_config_security(wlan_policy::SecurityType::Wpa3, SecurityTypeArg::Wpa3);
284 }
285
286 #[fuchsia::test]
289 fn test_construct_config_open() {
290 let open_config = PolicyNetworkConfig {
291 ssid: "some_ssid".to_string(),
292 security_type: None,
293 credential_type: None,
294 credential: Some("".to_string()),
295 };
296 let expected_cfg = wlan_policy::NetworkConfig {
297 id: Some(wlan_policy::NetworkIdentifier {
298 ssid: "some_ssid".as_bytes().to_vec(),
299 type_: wlan_policy::SecurityType::None,
300 }),
301 credential: Some(wlan_policy::Credential::None(wlan_policy::Empty {})),
302 ..Default::default()
303 };
304 let result_cfg = wlan_policy::NetworkConfig::from(open_config);
305 assert_eq!(expected_cfg, result_cfg);
306 }
307
308 #[fuchsia::test]
311 fn test_construct_config_open_with_omitted_args() {
312 let open_config = PolicyNetworkConfig {
313 ssid: "some_ssid".to_string(),
314 security_type: Some(SecurityTypeArg::None),
315 credential_type: Some(CredentialTypeArg::None),
316 credential: Some("".to_string()),
317 };
318 let expected_cfg = wlan_policy::NetworkConfig {
319 id: Some(wlan_policy::NetworkIdentifier {
320 ssid: "some_ssid".as_bytes().to_vec(),
321 type_: wlan_policy::SecurityType::None,
322 }),
323 credential: Some(wlan_policy::Credential::None(wlan_policy::Empty {})),
324 ..Default::default()
325 };
326 let result_cfg = wlan_policy::NetworkConfig::from(open_config);
327 assert_eq!(expected_cfg, result_cfg);
328 }
329
330 #[fuchsia::test]
333 fn test_construct_config_password_provided_no_security() {
334 let password = "mypassword";
335 let ssid = "some_ssid";
336 let arg_config = PolicyNetworkConfig {
337 ssid: ssid.to_string(),
338 security_type: None,
339 credential_type: None,
340 credential: Some(password.to_string()),
341 };
342 let expected_cfg = wlan_policy::NetworkConfig {
343 id: Some(wlan_policy::NetworkIdentifier {
344 ssid: ssid.as_bytes().to_vec(),
345 type_: wlan_policy::SecurityType::Wpa2,
346 }),
347 credential: Some(wlan_policy::Credential::Password(password.as_bytes().to_vec())),
348 ..Default::default()
349 };
350 let result_cfg = wlan_policy::NetworkConfig::from(arg_config);
351 assert_eq!(expected_cfg, result_cfg);
352 }
353
354 #[fuchsia::test]
357 fn test_construct_config_psk_provided_no_security() {
358 let psk = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234".to_string();
359 let psk_bytes = hex::decode(psk.as_bytes().to_vec()).unwrap();
360 let ssid = "some_ssid";
361 let arg_config = PolicyNetworkConfig {
362 ssid: ssid.to_string(),
363 security_type: None,
364 credential_type: None,
365 credential: Some(psk),
366 };
367 let expected_cfg = wlan_policy::NetworkConfig {
368 id: Some(wlan_policy::NetworkIdentifier {
369 ssid: ssid.as_bytes().to_vec(),
370 type_: wlan_policy::SecurityType::Wpa2,
371 }),
372 credential: Some(wlan_policy::Credential::Psk(psk_bytes)),
373 ..Default::default()
374 };
375 let result_cfg = wlan_policy::NetworkConfig::from(arg_config);
376 assert_eq!(expected_cfg, result_cfg);
377 }
378
379 #[fuchsia::test]
382 fn test_construct_config_psk() {
383 const ASCII_ZERO: u8 = 49;
385 let psk =
386 String::from_utf8([ASCII_ZERO; 64].to_vec()).expect("Failed to create PSK test value");
387 let wpa_config = PolicyNetworkConfig {
388 ssid: "some_ssid".to_string(),
389 security_type: Some(SecurityTypeArg::Wpa2),
390 credential_type: Some(CredentialTypeArg::Psk),
391 credential: Some(psk),
392 };
393 let expected_cfg = wlan_policy::NetworkConfig {
394 id: Some(wlan_policy::NetworkIdentifier {
395 ssid: "some_ssid".as_bytes().to_vec(),
396 type_: wlan_policy::SecurityType::Wpa2,
397 }),
398 credential: Some(wlan_policy::Credential::Psk([17; 32].to_vec())),
399 ..Default::default()
400 };
401 let result_cfg = wlan_policy::NetworkConfig::from(wpa_config);
402 assert_eq!(expected_cfg, result_cfg);
403 }
404
405 fn test_construct_config_security(
408 fidl_type: wlan_policy::SecurityType,
409 tool_type: SecurityTypeArg,
410 ) {
411 let args_config = PolicyNetworkConfig {
412 ssid: "some_ssid".to_string(),
413 security_type: Some(tool_type),
414 credential_type: Some(CredentialTypeArg::Password),
415 credential: Some("some_password_here".to_string()),
416 };
417 let expected_cfg = wlan_policy::NetworkConfig {
418 id: Some(wlan_policy::NetworkIdentifier {
419 ssid: "some_ssid".as_bytes().to_vec(),
420 type_: fidl_type,
421 }),
422 credential: Some(wlan_policy::Credential::Password(
423 "some_password_here".as_bytes().to_vec(),
424 )),
425 ..Default::default()
426 };
427 let result_cfg = wlan_policy::NetworkConfig::from(args_config);
428 assert_eq!(expected_cfg, result_cfg);
429 }
430}