wlan_hw_sim/
netdevice_helper.rs

1// Copyright 2019 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 fidl::endpoints::{create_proxy, Proxy};
6use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
7use futures::{FutureExt as _, StreamExt as _, TryStreamExt as _};
8use std::pin::pin;
9use wlan_common::append::Append;
10use wlan_common::big_endian::BigEndianU16;
11use wlan_common::mac;
12
13/// Returns a Netdevice client with the specified MAC address, or None if none is found.
14pub async fn create_client(
15    devfs: &fidl_fuchsia_io::DirectoryProxy,
16    mac: fidl_fuchsia_net::MacAddress,
17) -> Option<(netdevice_client::Client, netdevice_client::Port)> {
18    let (directory, directory_server) = create_proxy::<fidl_fuchsia_io::DirectoryMarker>();
19
20    fdio::open_at(
21        devfs.as_channel().as_ref(),
22        "class/network",
23        fidl_fuchsia_io::PERM_READABLE,
24        directory_server.into_channel(),
25    )
26    .expect("connect to /dev/class/network");
27
28    let dirents = fuchsia_fs::directory::readdir(&directory).await.expect("readdir failed");
29    let devices = dirents.into_iter().map(|file| {
30        log::info!("Found file name {:?}", file.name);
31        connect_to_named_protocol_at_dir_root::<
32                fidl_fuchsia_hardware_network::DeviceInstanceMarker,
33            >(&directory, &file.name)
34            .expect("creating proxy")
35    });
36
37    // TODO(https://fxbug.dev/338700293): Consider looping with a backoff delay if the device is not found
38    // on the first try.
39    let results = futures::stream::iter(devices).filter_map(|netdev_device| async move {
40        let (device_proxy, device_server) =
41            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::DeviceMarker>();
42        netdev_device.get_device(device_server).expect("get device");
43        let client = netdevice_client::Client::new(device_proxy);
44
45        let port_id = match client
46            .device_port_event_stream()
47            .expect("failed to get port event stream")
48            .try_next()
49            .await
50            .expect("error observing ports")
51            .expect("port stream ended unexpectedly")
52        {
53            fidl_fuchsia_hardware_network::DevicePortEvent::Existing(port_id) => port_id,
54            e @ fidl_fuchsia_hardware_network::DevicePortEvent::Removed(_)
55            | e @ fidl_fuchsia_hardware_network::DevicePortEvent::Idle(_)
56            | e @ fidl_fuchsia_hardware_network::DevicePortEvent::Added(_) => {
57                unreachable!("unexpected event: {:?}", e);
58            }
59        };
60        let netdev_port = port_id.try_into().expect("bad port id");
61        let port_proxy = client.connect_port(netdev_port).expect("failed to connect port");
62        let (mac_addressing, mac_addressing_server) =
63            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>();
64        port_proxy.get_mac(mac_addressing_server).expect("failed to get mac addressing");
65        let addr = mac_addressing.get_unicast_address().await.expect("failed to get address");
66
67        (addr.octets == mac.octets).then(move || (client, netdev_port))
68    });
69    let mut results = pin!(results);
70    results.next().await
71}
72
73/// Returns a Session and a Task. The Task is not used by the client, but must remain in scope
74/// to avoid being destructed, as it drives the Session.
75pub async fn start_session(
76    client: netdevice_client::Client,
77    port: netdevice_client::Port,
78) -> (netdevice_client::Session, fuchsia_async::Task<()>) {
79    let info = client.device_info().await.expect("get device info");
80    let (session, task) = client
81        .new_session_with_derivable_config(
82            "wlan-test",
83            netdevice_client::DerivableConfig {
84                default_buffer_length: info
85                    .base_info
86                    .max_buffer_length
87                    .expect("buffer length not set in DeviceInfo")
88                    .get() as usize,
89                ..Default::default()
90            },
91        )
92        .await
93        .expect("open primary session");
94    let task_handle = fuchsia_async::Task::spawn(task.map(|r| r.expect("session task failed")));
95    session
96        .attach(port, &[fidl_fuchsia_hardware_network::FrameType::Ethernet])
97        .await
98        .expect("attach port");
99
100    let (watcher_proxy, watcher_server) = fidl::endpoints::create_proxy();
101    client
102        .connect_port(port)
103        .expect("Failed to connect to port")
104        // Using `MAX_STATUS_BUFFER` is overly conservative, but, since this is a test, there is no
105        // real downside.
106        .get_status_watcher(watcher_server, fidl_fuchsia_hardware_network::MAX_STATUS_BUFFER)
107        .unwrap();
108
109    // Wait for the port to come online. Otherwise, tests may attempt to use the data path before
110    // the network device is ready to handle frames.
111    loop {
112        if let Some(flags) = watcher_proxy.watch_status().await.unwrap().flags {
113            if flags == fidl_fuchsia_hardware_network::StatusFlags::ONLINE {
114                log::info!("Network device port online!");
115                break;
116            }
117            log::info!("Waiting for network device port to come online...");
118        }
119    }
120
121    (session, task_handle)
122}
123
124pub async fn send(session: &netdevice_client::Session, port: &netdevice_client::Port, data: &[u8]) {
125    let mut buffer = session.alloc_tx_buffer(data.len()).await.expect("allocate tx buffer");
126    buffer.set_frame_type(fidl_fuchsia_hardware_network::FrameType::Ethernet);
127    buffer.set_port(*port);
128    buffer.write_at(0, &data).expect("write message");
129    session.send(buffer).expect("failed to send data");
130}
131
132pub async fn recv(session: &netdevice_client::Session) -> Vec<u8> {
133    let recv_result = session.recv().await.expect("recv failed");
134    let mut buffer = vec![0; recv_result.len()];
135    recv_result.read_at(0, &mut buffer).expect("read from buffer");
136    buffer
137}
138
139pub fn write_fake_frame(da: ieee80211::MacAddr, sa: ieee80211::MacAddr, payload: &[u8]) -> Vec<u8> {
140    let mut buf = vec![];
141    buf.append_value(&mac::EthernetIIHdr {
142        da,
143        sa,
144        ether_type: BigEndianU16::from_native(mac::ETHER_TYPE_IPV4),
145    })
146    .expect("error creating fake ethernet header");
147    buf.append_bytes(payload).expect("buffer too small for ethernet payload");
148    buf
149}