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.query_iface(iface_id).await.context("Error querying iface")?.map_err(|e| {
95        format_err!("query_iface {} failed: {}", iface_id, zx::Status::err_from_raw(e))
96    })
97}
98
99pub async fn get_wlan_sta_addr(
100    monitor_proxy: &DeviceMonitorProxy,
101    iface_id: u16,
102) -> Result<[u8; 6], Error> {
103    let iface_info = query_iface(monitor_proxy, iface_id).await?;
104    Ok(iface_info.sta_addr)
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use assert_matches::assert_matches;
111    use fidl_fuchsia_wlan_common::WlanMacRole;
112    use fidl_fuchsia_wlan_device_service::{
113        DeviceMonitorCreateIfaceResponse, DeviceMonitorMarker, DeviceMonitorRequest,
114        DeviceMonitorRequestStream,
115    };
116    use fuchsia_async::TestExecutor;
117    use futures::StreamExt;
118    use futures::task::Poll;
119    use std::pin::pin;
120
121    pub(crate) fn setup_fake_service<M: fidl::endpoints::ProtocolMarker>()
122    -> (fuchsia_async::TestExecutor, M::Proxy, M::RequestStream) {
123        let exec = fuchsia_async::TestExecutor::new();
124        let (proxy, server) = fidl::endpoints::create_proxy::<M>();
125        (exec, proxy, server.into_stream())
126    }
127
128    fn fake_iface_query_response(
129        sta_addr: [u8; 6],
130        role: fidl_fuchsia_wlan_common::WlanMacRole,
131    ) -> QueryIfaceResponse {
132        QueryIfaceResponse {
133            role,
134            id: 0,
135            phy_id: 0,
136            phy_assigned_id: 0,
137            sta_addr,
138            factory_addr: sta_addr,
139        }
140    }
141
142    pub fn respond_to_query_iface_list_request(
143        exec: &mut TestExecutor,
144        req_stream: &mut DeviceMonitorRequestStream,
145        ifaces: Vec<u16>,
146    ) {
147        let req = exec.run_until_stalled(&mut req_stream.next());
148        let responder = assert_matches!(
149            req,
150            Poll::Ready(Some(Ok(DeviceMonitorRequest::ListIfaces { responder }))) => responder
151        );
152        responder.send(&ifaces[..]).expect("fake query iface list response: send failed")
153    }
154
155    pub fn respond_to_query_iface_request(
156        exec: &mut TestExecutor,
157        req_stream: &mut DeviceMonitorRequestStream,
158        role: fidl_fuchsia_wlan_common::WlanMacRole,
159        fake_mac_addr: Option<[u8; 6]>,
160    ) {
161        let req = exec.run_until_stalled(&mut req_stream.next());
162        let responder = assert_matches!(
163            req,
164            Poll::Ready(Some(Ok(DeviceMonitorRequest::QueryIface { iface_id: _, responder }))) => responder
165        );
166        if let Some(mac) = fake_mac_addr {
167            let response = fake_iface_query_response(mac, role);
168            responder.send(Ok(&response)).expect("sending fake response with mac address");
169        } else {
170            responder.send(Err(zx::sys::ZX_ERR_NOT_FOUND)).expect("sending fake response with none")
171        }
172    }
173
174    pub fn respond_to_create_iface_request(
175        exec: &mut TestExecutor,
176        req_stream: &mut DeviceMonitorRequestStream,
177        fake_response: Result<u16, i32>,
178    ) {
179        let req = exec.run_until_stalled(&mut req_stream.next());
180        let responder = assert_matches!(
181            req,
182            Poll::Ready(Some(Ok(DeviceMonitorRequest::CreateIface { responder, .. }))) => responder
183        );
184
185        let fake_response = fake_response
186            .map(|fake_iface_id| DeviceMonitorCreateIfaceResponse {
187                iface_id: Some(fake_iface_id),
188                ..Default::default()
189            })
190            .map_err(|_| fidl_fuchsia_wlan_device_service::DeviceMonitorError::Internal);
191        responder
192            .send(fake_response.as_ref().map_err(|e| *e))
193            .expect("sending fake response with iface id");
194    }
195
196    #[test]
197    fn test_get_wlan_sta_addr_ok() {
198        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
199        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
200        let mut mac_addr_fut = pin!(mac_addr_fut);
201
202        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
203
204        respond_to_query_iface_request(
205            &mut exec,
206            &mut req_stream,
207            WlanMacRole::Client,
208            Some([1, 2, 3, 4, 5, 6]),
209        );
210
211        let mac_addr = exec.run_singlethreaded(&mut mac_addr_fut).expect("should get a mac addr");
212        assert_eq!(mac_addr, [1, 2, 3, 4, 5, 6]);
213    }
214
215    #[test]
216    fn test_get_wlan_sta_addr_not_found() {
217        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
218        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
219        let mut mac_addr_fut = pin!(mac_addr_fut);
220
221        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
222
223        respond_to_query_iface_request(&mut exec, &mut req_stream, WlanMacRole::Client, None);
224
225        let err = exec.run_singlethreaded(&mut mac_addr_fut).expect_err("should be an error");
226        assert!(format!("{}", err).contains("NOT_FOUND"));
227    }
228
229    #[test]
230    fn test_get_wlan_sta_addr_service_interrupted() {
231        let (mut exec, proxy, req_stream) = setup_fake_service::<DeviceMonitorMarker>();
232        let mac_addr_fut = get_wlan_sta_addr(&proxy, 0);
233        let mut mac_addr_fut = pin!(mac_addr_fut);
234
235        assert_matches!(exec.run_until_stalled(&mut mac_addr_fut), Poll::Pending);
236
237        // Simulate service not being available by closing the channel
238        std::mem::drop(req_stream);
239
240        let err = exec.run_singlethreaded(&mut mac_addr_fut).expect_err("should be an error");
241        assert!(format!("{}", err).contains("Error querying iface"));
242    }
243
244    #[test]
245    fn test_create_iface_ok() {
246        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
247        let iface_id_fut = create_iface(&proxy, 0, WlanMacRole::Client, [0, 0, 0, 0, 0, 0].into());
248
249        let mut iface_id_fut = pin!(iface_id_fut);
250
251        assert_matches!(exec.run_until_stalled(&mut iface_id_fut), Poll::Pending);
252        respond_to_create_iface_request(&mut exec, &mut req_stream, Ok(15));
253
254        let iface_id = exec.run_singlethreaded(&mut iface_id_fut).expect("should get an iface id");
255
256        assert_eq!(iface_id, 15);
257    }
258
259    #[test]
260    fn test_create_iface_internal_err() {
261        let (mut exec, proxy, mut req_stream) = setup_fake_service::<DeviceMonitorMarker>();
262        let iface_id_fut = create_iface(&proxy, 0, WlanMacRole::Client, [0, 0, 0, 0, 0, 0].into());
263
264        let mut iface_id_fut = pin!(iface_id_fut);
265
266        assert_matches!(exec.run_until_stalled(&mut iface_id_fut), Poll::Pending);
267        respond_to_create_iface_request(&mut exec, &mut req_stream, Err(zx::sys::ZX_ERR_INTERNAL));
268
269        let err = exec.run_singlethreaded(&mut iface_id_fut).expect_err("Should get an error");
270        assert!(format!("{}", err).contains("Internal"));
271    }
272}