Skip to main content

wlan_service_util/
lib.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error, format_err};
6use fidl_fuchsia_wlan_common::WlanMacRole;
7use fidl_fuchsia_wlan_device_service::{
8    DestroyIfaceRequest, DeviceMonitorCreateIfaceRequest, DeviceMonitorCreateIfaceResponse,
9    DeviceMonitorProxy, QueryIfaceResponse,
10};
11use log::info;
12
13use ieee80211::{MacAddr, MacAddrBytes};
14
15pub mod ap;
16pub mod client;
17
18// Helper methods for calling wlan_service fidl methods
19pub async fn get_iface_list(monitor_proxy: &DeviceMonitorProxy) -> Result<Vec<u16>, Error> {
20    monitor_proxy.list_ifaces().await.context("Error getting iface list")
21}
22
23/// Returns the first iface id with the requested role
24///
25/// # Arguments: 2
26/// * `monitor_proxy`: a DeviceMonitorProxy
27/// * 'role' : requested WlanMacRole (client or ap)
28pub async fn get_first_iface(
29    monitor_proxy: &DeviceMonitorProxy,
30    role: WlanMacRole,
31) -> Result<u16, Error> {
32    let wlan_iface_ids =
33        get_iface_list(monitor_proxy).await.context("Connect: failed to get wlan iface list")?;
34
35    if wlan_iface_ids.len() == 0 {
36        return Err(format_err!("No wlan interface found"));
37    }
38    info!("Found {} wlan iface entries", wlan_iface_ids.len());
39    for iface_id in wlan_iface_ids {
40        let iface_info = query_iface(monitor_proxy, iface_id).await?;
41
42        if iface_info.role == role {
43            return Ok(iface_id);
44        }
45    }
46    Err(format_err!("interface with role {:?} not found", role))
47}
48/// Returns the list of Phy IDs for this system.
49///
50/// # Arguments
51/// * `monitor_proxy`: a DeviceMonitorProxy
52pub async fn get_phy_list(monitor_proxy: &DeviceMonitorProxy) -> Result<Vec<u16>, Error> {
53    let phys = monitor_proxy.list_phys().await.context("Error getting phy list")?;
54    Ok(phys)
55}
56
57pub async fn create_iface(
58    monitor_proxy: &DeviceMonitorProxy,
59    phy_id: u16,
60    role: WlanMacRole,
61    sta_address: MacAddr,
62) -> Result<u16, Error> {
63    let req = DeviceMonitorCreateIfaceRequest {
64        phy_id: Some(phy_id),
65        role: Some(role),
66        sta_address: Some(sta_address.to_array()),
67        ..Default::default()
68    };
69
70    match monitor_proxy.create_iface(&req).await.context("FIDL error creating iface")? {
71        Err(e) => Err(format_err!("Error creating iface: {:?}", e)),
72        Ok(DeviceMonitorCreateIfaceResponse { iface_id: None, .. }) => {
73            Err(format_err!("Create iface completed without returning an iface id"))
74        }
75        Ok(DeviceMonitorCreateIfaceResponse { iface_id: Some(iface_id), .. }) => {
76            info!("Created iface {:?}", iface_id);
77            Ok(iface_id)
78        }
79    }
80}
81
82pub async fn destroy_iface(monitor_proxy: &DeviceMonitorProxy, iface_id: u16) -> Result<(), Error> {
83    let req = DestroyIfaceRequest { iface_id };
84
85    let response = monitor_proxy.destroy_iface(&req).await.context("Error destroying iface")?;
86    zx::Status::ok(response).context("Destroy iface returned non-OK status")?;
87    Ok(info!("Destroyed iface {:?}", iface_id))
88}
89
90async fn query_iface(
91    monitor_proxy: &DeviceMonitorProxy,
92    iface_id: u16,
93) -> Result<QueryIfaceResponse, Error> {
94    monitor_proxy
95        .query_iface(iface_id)
96        .await
97        .context("Error querying iface")?
98        .map_err(|e| format_err!("query_iface {} failed: {}", iface_id, zx::Status::from_raw(e)))
99}
100
101pub async fn get_wlan_sta_addr(
102    monitor_proxy: &DeviceMonitorProxy,
103    iface_id: u16,
104) -> Result<[u8; 6], Error> {
105    let iface_info = query_iface(monitor_proxy, iface_id).await?;
106    Ok(iface_info.sta_addr)
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use assert_matches::assert_matches;
113    use fidl_fuchsia_wlan_common::WlanMacRole;
114    use fidl_fuchsia_wlan_device_service::{
115        DeviceMonitorCreateIfaceResponse, DeviceMonitorMarker, DeviceMonitorRequest,
116        DeviceMonitorRequestStream,
117    };
118    use fuchsia_async::TestExecutor;
119    use futures::StreamExt;
120    use futures::task::Poll;
121    use std::pin::pin;
122
123    pub(crate) fn setup_fake_service<M: fidl::endpoints::ProtocolMarker>()
124    -> (fuchsia_async::TestExecutor, M::Proxy, M::RequestStream) {
125        let exec = fuchsia_async::TestExecutor::new();
126        let (proxy, server) = fidl::endpoints::create_proxy::<M>();
127        (exec, proxy, server.into_stream())
128    }
129
130    fn fake_iface_query_response(
131        sta_addr: [u8; 6],
132        role: fidl_fuchsia_wlan_common::WlanMacRole,
133    ) -> QueryIfaceResponse {
134        QueryIfaceResponse {
135            role,
136            id: 0,
137            phy_id: 0,
138            phy_assigned_id: 0,
139            sta_addr,
140            factory_addr: sta_addr,
141        }
142    }
143
144    pub fn respond_to_query_iface_list_request(
145        exec: &mut TestExecutor,
146        req_stream: &mut DeviceMonitorRequestStream,
147        ifaces: Vec<u16>,
148    ) {
149        let req = exec.run_until_stalled(&mut req_stream.next());
150        let responder = assert_variant !(
151            req,
152            Poll::Ready(Some(Ok(DeviceMonitorRequest::ListIfaces{responder})))
153            => responder);
154        responder.send(&ifaces[..]).expect("fake query iface list response: send failed")
155    }
156
157    pub fn respond_to_query_iface_request(
158        exec: &mut TestExecutor,
159        req_stream: &mut DeviceMonitorRequestStream,
160        role: fidl_fuchsia_wlan_common::WlanMacRole,
161        fake_mac_addr: Option<[u8; 6]>,
162    ) {
163        let req = exec.run_until_stalled(&mut req_stream.next());
164        let responder = assert_variant !(
165            req,
166            Poll::Ready(Some(Ok(DeviceMonitorRequest::QueryIface{iface_id : _, responder})))
167            => responder);
168        if let Some(mac) = fake_mac_addr {
169            let response = fake_iface_query_response(mac, role);
170            responder.send(Ok(&response)).expect("sending fake response with mac address");
171        } else {
172            responder.send(Err(zx::sys::ZX_ERR_NOT_FOUND)).expect("sending fake response with none")
173        }
174    }
175
176    pub fn respond_to_create_iface_request(
177        exec: &mut TestExecutor,
178        req_stream: &mut DeviceMonitorRequestStream,
179        fake_response: Result<u16, i32>,
180    ) {
181        let req = exec.run_until_stalled(&mut req_stream.next());
182        let responder = assert_variant !(
183            req,
184            Poll::Ready(Some(Ok(DeviceMonitorRequest::CreateIface{responder, ..})))
185            => responder);
186
187        let fake_response = fake_response.map_ok(|fake_iface_id| {
188            DeviceMonitorCreateIfaceResponse { iface_id: Some(fake_iface_id), ..Default::default() }
189        });
190        responder.send(fake_response).expect("sending fake response with iface id");
191    }
192
193    #[test]
194    fn test_get_wlan_sta_addr_ok() {
195        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
196        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
197        let mut mac_addr_fut = pin!(mac_addr_fut);
198
199        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
200
201        respond_to_query_iface_request(
202            &mut exec,
203            &mut req_stream,
204            WlanMacRole::Client,
205            Some([1, 2, 3, 4, 5, 6]),
206        );
207
208        let mac_addr = exec.run_singlethreaded(&mut mac_addr_fut).expect("should get a mac addr");
209        assert_eq!(mac_addr, [1, 2, 3, 4, 5, 6]);
210    }
211
212    #[test]
213    fn test_get_wlan_sta_addr_not_found() {
214        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
215        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
216        let mut mac_addr_fut = pin!(mac_addr_fut);
217
218        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
219
220        respond_to_query_iface_request(&mut exec, &mut req_stream, WlanMacRole::Client, None);
221
222        let err = exec.run_singlethreaded(&mut mac_addr_fut).expect_err("should be an error");
223        assert!(format!("{}", err).contains("NOT_FOUND"));
224    }
225
226    #[test]
227    fn test_get_wlan_sta_addr_service_interrupted() {
228        let (mut exec, proxy, req_stream) = setup_fake_service::<DeviceMonitorMarker>();
229        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
230        let mut mac_addr_fut = pin!(mac_addr_fut);
231
232        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
233
234        // Simulate service not being available by closing the channel
235        std::mem::drop(req_stream);
236
237        let err = exec.run_singlethreaded(&mut mac_addr_fut).expect_err("should be an error");
238        assert!(format!("{}", err).contains("Error querying iface"));
239    }
240
241    #[test]
242    fn test_create_iface_ok() {
243        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
244        let iface_id_fut = create_iface(&proxy, 0, WlanMacRole::Client, [0, 0, 0, 0, 0, 0].into());
245
246        let mut iface_id_fut = pin!(iface_id_fut);
247
248        assert_matches!(exec.run_until_stalled(&mut iface_id_fut), Poll::Pending);
249        respond_to_create_iface_request(&mut exec, &mut req_stream, Ok(15));
250
251        let iface_id = exec.run_singlethreaded(&mut iface_id_fut).expect("should get an iface id");
252
253        assert_eq!(iface_id, 15);
254    }
255
256    #[test]
257    fn test_create_iface_internal_err() {
258        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
259        let iface_id_fut = create_iface(&proxy, 0, WlanMacRole::Client, [0, 0, 0, 0, 0, 0].into());
260
261        let mut iface_id_fut = pin!(iface_id_fut);
262
263        assert_matches!(exec.run_until_stalled(&mut iface_id_fut), Poll::Pending);
264        respond_to_create_iface_request(&mut exec, &mut req_stream, Err(zx::sys::ZX_ERR_INTERNAL));
265
266        let err = exec.run_singlethreaded(&mut iface_id_fut).expect_err("Should get an error");
267        assert!(format!("{}", err).contains("INTERNAL"));
268    }
269}