Skip to main content

wifi_ctrl/ap/
types.rs

1use super::config::ConfigError;
2use serde::{Deserialize, Serialize};
3
4/// Status of the WiFi Access Point
5#[derive(Serialize, Deserialize, Debug)]
6pub struct Status {
7    pub state: String,
8    pub phy: String,
9    pub freq: u32,
10    // The fields below vary by driver, security mode, and hostapd version, so
11    // each is optional: a missing key leaves it `None` rather than failing the
12    // whole parse.
13    pub num_sta_non_erp: Option<u64>,
14    pub num_sta_no_short_slot_time: Option<u64>,
15    pub num_sta_no_short_preamble: Option<u64>,
16    pub olbc: Option<u64>,
17    pub num_sta_ht_no_gf: Option<u64>,
18    pub num_sta_no_ht: Option<u64>,
19    pub num_sta_ht_20_mhz: Option<u64>,
20    pub num_sta_ht40_intolerant: Option<u64>,
21    pub olbc_ht: Option<u64>,
22    pub ht_op_mode: Option<String>,
23    pub cac_time_seconds: Option<u64>,
24    pub cac_time_left_seconds: Option<u64>,
25    pub channel: Option<u64>,
26    pub secondary_channel: Option<u64>,
27    pub ieee80211n: Option<u64>,
28    pub ieee80211ac: Option<u64>,
29    pub ieee80211ax: Option<u64>,
30    pub beacon_int: Option<u64>,
31    pub dtim_period: Option<u64>,
32    pub ht_caps_info: Option<String>,
33    pub ht_mcs_bitmask: Option<String>,
34    #[serde(default)] // missing if there are no rates
35    pub supported_rates: String,
36    pub max_txpower: Option<u64>,
37    #[serde(default)]
38    pub bss: Vec<String>,
39    #[serde(default)]
40    pub bssid: Vec<String>,
41    #[serde(default)]
42    pub ssid: Vec<String>,
43    #[serde(default)]
44    pub num_sta: Vec<u32>,
45}
46
47impl Status {
48    /// Decode from the response sent from the hostapd
49    /// ```
50    /// # use wifi_ctrl::ap::Status;
51    /// let resp = r#"
52    ///state=ENABLED
53    ///phy=phy0
54    ///freq=2437
55    ///num_sta_non_erp=0
56    ///num_sta_no_short_slot_time=0
57    ///num_sta_no_short_preamble=0
58    ///olbc=0
59    ///num_sta_ht_no_gf=0
60    ///num_sta_no_ht=0
61    ///num_sta_ht_20_mhz=0
62    ///num_sta_ht40_intolerant=0
63    ///olbc_ht=0
64    ///ht_op_mode=0x0
65    ///cac_time_seconds=0
66    ///cac_time_left_seconds=N/A
67    ///channel=6
68    ///edmg_enable=0
69    ///edmg_channel=0
70    ///secondary_channel=0
71    ///ieee80211n=0
72    ///ieee80211ac=0
73    ///ieee80211ax=0
74    ///beacon_int=100
75    ///dtim_period=2
76    ///supported_rates=02 04 0b 16 0c 12 18 24 30 48 60 6c
77    ///max_txpower=20
78    ///bss[0]=wlan0
79    ///bssid[0]=cc:7b:5c:1a:d2:21
80    ///ssid[0]=WiFi-SSID
81    ///num_sta[0]=0
82    ///bss[1]=wlan1
83    ///bssid[1]=cc:7b:5c:4d:ff:5c
84    ///ssid[1]=¯\\_(\xe3\x83\x84)_/¯
85    ///num_sta[1]=1
86    ///"#;
87    /// let status = Status::from_response(resp).unwrap();
88    /// assert_eq!(status.state, "ENABLED");
89    /// assert_eq!(status.freq, 2437);
90    /// assert_eq!(status.ssid, vec![r"WiFi-SSID", r#"¯\_(ツ)_/¯"#]);
91    /// assert_eq!(status.num_sta, vec![0, 1]);
92    /// ```
93    pub fn from_response(response: &str) -> Result<Self, ConfigError> {
94        crate::config::from_str(response)
95    }
96}
97
98/// Configuration of the WiFi station
99#[derive(Serialize, Deserialize, Debug)]
100pub struct Config {
101    pub bssid: String,
102    pub ssid: String,
103    pub wps_state: String,
104    #[serde(default)] // missing if zero
105    pub wpa: i32,
106    // missing if WPA is not enabled
107    pub key_mgmt: Option<String>,
108    pub group_cipher: Option<String>,
109    pub rsn_pairwise_cipher: Option<String>,
110    pub wpa_pairwise_cipher: Option<String>,
111}
112
113impl Config {
114    /// Decode from the response sent from the hostapd
115    /// ```
116    /// # use wifi_ctrl::ap::Config;
117    /// let resp = r#"
118    ///bssid=cc:7b:5c:1a:d2:21
119    ///ssid=WiFi-SSID
120    ///wps_state=disabled
121    ///wpa=2
122    ///key_mgmt=WPA-PSK
123    ///group_cipher=CCMP
124    ///rsn_pairwise_cipher=CCMP
125    ///wpa_pairwise_cipher=CCMP
126    ///"#;
127    /// let config = Config::from_response(resp).unwrap();
128    /// assert_eq!(config.wps_state, "disabled");
129    /// assert_eq!(config.wpa, 2);
130    /// assert_eq!(config.ssid, "WiFi-SSID");
131    /// ```
132    pub fn from_response(response: &str) -> Result<Self, ConfigError> {
133        crate::config::from_str(response)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_config_wpa_psk() {
143        let resp = r#"
144bssid=cc:7b:5c:1a:d2:21
145ssid=\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf
146wps_state=disabled
147wpa=2
148key_mgmt=WPA-PSK
149group_cipher=CCMP
150rsn_pairwise_cipher=CCMP
151        "#;
152        let config = Config::from_response(resp).unwrap();
153        assert_eq!(config.wpa, 2);
154        assert_eq!(config.wps_state, "disabled");
155        assert_eq!(config.ssid, r#"¯\_(ツ)_/¯"#);
156    }
157
158    #[test]
159    fn test_config_wsp_1() {
160        let resp = r#"
161bssid=cc:7b:5c:1a:d2:21
162ssid=MY_SSID
163wps_state=not configured
164passphrase=MY_PASSPHRASE
165psk=8dbbe42cb44f21088fbb9cfbf24dc9b39787d6026d436b01b3ac7d34afb4416d
166wpa=2
167key_mgmt=WPA-PSK
168group_cipher=CCMP
169rsn_pairwise_cipher=CCMP
170        "#;
171        let config = Config::from_response(resp).unwrap();
172        assert_eq!(config.wpa, 2);
173        assert_eq!(config.wps_state, "not configured");
174        assert_eq!(config.ssid, "MY_SSID");
175    }
176
177    #[test]
178    fn test_config_wsp_2() {
179        let resp = r#"
180bssid=cc:7b:5c:1a:d2:21
181ssid=MY_SSID
182wps_state=configured
183passphrase=MY_PASSPHRASE
184psk=8dbbe42cb44f21088fbb9cfbf24dc9b39787d6026d436b01b3ac7d34afb4416d
185wpa=2
186key_mgmt=WPA-PSK
187group_cipher=CCMP
188rsn_pairwise_cipher=CCMP
189        "#;
190        let config = Config::from_response(resp).unwrap();
191        assert_eq!(config.wpa, 2);
192        assert_eq!(config.wps_state, "configured");
193        assert_eq!(config.ssid, "MY_SSID");
194    }
195
196    #[test]
197    fn test_config_open() {
198        let resp = r#"
199bssid=cc:7b:5c:1a:d2:21
200ssid=Wi-Fi
201wps_state=disabled
202        "#;
203        let config = Config::from_response(resp).unwrap();
204        assert_eq!(config.wpa, 0);
205        assert_eq!(config.ssid, "Wi-Fi");
206    }
207}