diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-08 21:48:48 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-08 21:48:48 +0000 |
commit | a6ed50e171e39d7d145dce75866f51d63b4aa41d (patch) | |
tree | f350f8d408ab6b05bbf285f1469c79d27d444080 | |
parent | f33f179ba09b0c057d9e9b63c0060edc7aa46acb (diff) | |
parent | 2fc6f103439e3d1a29512a094f3dfc1686749279 (diff) | |
download | netsim-a6ed50e171e39d7d145dce75866f51d63b4aa41d.tar.gz |
Snap for 11286485 from 2fc6f103439e3d1a29512a094f3dfc1686749279 to emu-34-release
Change-Id: I75a063c29dd571770f4de1147ce4dd7c7e384d90
30 files changed, 1936 insertions, 291 deletions
@@ -302,10 +302,12 @@ cc_test_host { rust_library_host { name: "libnetsim_proto", + features: ["cuttlefish"], crate_name: "netsim_proto", srcs: ["rust/proto/src/lib.rs"], rustlibs: [ "libprotobuf", + "libgrpcio", ], } @@ -404,3 +406,26 @@ rust_binary_host { "libnetsim_cli", ], } + +rust_binary_host { + name: "netsim_test_client", + srcs: ["rust/frontend/src/netsim_test_client.rs"], + rustlibs: [ + "libgrpcio", + "libnetsim_proto", + "libprotobuf", + "libnetsim_common", + ], +} + +rust_binary_host { + name: "netsim_test_server", + srcs: ["rust/frontend/src/netsim_test_server.rs"], + rustlibs: [ + "libgrpcio", + "libnetsim_proto", + "libprotobuf", + "libnetsim_common", + "libfutures", + ], +} diff --git a/cmake/netsim_dependencies.cmake b/cmake/netsim_dependencies.cmake index 658ece9..8f063f7 100644 --- a/cmake/netsim_dependencies.cmake +++ b/cmake/netsim_dependencies.cmake @@ -1,5 +1,5 @@ set(BLUETOOTH_EMULATION True) -set(AOSP ${CMAKE_CURRENT_LIST_DIR}/../../..) +get_filename_component(AOSP "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) set(EXTERNAL ${AOSP}/external) set(EXTERNAL_QEMU ${EXTERNAL}/qemu) set(ANDROID_QEMU2_TOP_DIR ${EXTERNAL_QEMU}) @@ -34,7 +34,6 @@ set(_gRPC_RE2_INCLUDE_DIR "${EXTERNAL_QEMU}/android/third_party/re2") set(_gRPC_RE2_LIBRARIES re2) set(NETSIM_EXT TRUE) - # Let's bin place everything in the root, with the shared libs in the right # place set(DBG_INFO ${CMAKE_BINARY_DIR}/build/debug_info) @@ -50,7 +49,6 @@ else() set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/distribution/emulator) endif() - # First make the protobuf and dependencies available to gRPC add_subdirectory(${EXTERNAL}/qemu/android/third_party/protobuf protobuf) diff --git a/pdl/mac80211_hwsim.pdl b/pdl/mac80211_hwsim.pdl index dce9518..5ca3c70 100644 --- a/pdl/mac80211_hwsim.pdl +++ b/pdl/mac80211_hwsim.pdl @@ -242,7 +242,7 @@ struct HwsimMsgHdr { reserved: 16, } -struct HwsimMsg { +packet HwsimMsg { nl_hdr: NlMsgHdr, hwsim_hdr: HwsimMsgHdr, // user header diff --git a/rust/cli/Cargo.toml b/rust/cli/Cargo.toml index 3df2f83..7f25581 100644 --- a/rust/cli/Cargo.toml +++ b/rust/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "netsim-cli" -version = "0.2.3" +version = "0.2.6" edition = "2021" build = "build.rs" diff --git a/rust/daemon/Cargo.toml b/rust/daemon/Cargo.toml index 6b970dd..a309677 100644 --- a/rust/daemon/Cargo.toml +++ b/rust/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "netsim-daemon" -version = "0.2.3" +version = "0.2.6" edition = "2021" build = "build.rs" diff --git a/rust/daemon/src/echip/mod.rs b/rust/daemon/src/echip/mod.rs index 010c9e4..5e8712e 100644 --- a/rust/daemon/src/echip/mod.rs +++ b/rust/daemon/src/echip/mod.rs @@ -23,4 +23,6 @@ pub use crate::echip::emulated_chip::CreateParam; pub use crate::echip::emulated_chip::EmulatedChip; pub use crate::echip::emulated_chip::SharedEmulatedChip; pub use crate::echip::emulated_chip::{get, new, remove}; -pub use crate::echip::packet::{handle_request, handle_request_cxx, handle_response}; +pub use crate::echip::packet::{ + handle_request, handle_request_cxx, handle_response, hwsim_cmd_response, +}; diff --git a/rust/daemon/src/echip/packet.rs b/rust/daemon/src/echip/packet.rs index 8140594..d92888a 100644 --- a/rust/daemon/src/echip/packet.rs +++ b/rust/daemon/src/echip/packet.rs @@ -116,6 +116,20 @@ pub fn handle_response(chip_id: ChipIdentifier, packet: &cxx::CxxVector<u8>, pac }; } +// Send HwsimCmd packets to guest OS. +pub fn hwsim_cmd_response(client_id: u32, packet: &[u8]) { + let mut senders = SENDERS.lock(); + if let Some(responder) = senders.get(&client_id) { + let packet_type = PacketType::HCI_PACKET_UNSPECIFIED.value() as u8; + if responder.send(ResponsePacket { packet: packet.to_owned(), packet_type }).is_err() { + warn!("send failed for client: {client_id}"); + senders.remove(&client_id); // Remove from the map using the value itself + } + } else { + warn!("unknown client: {client_id}"); + } +} + /// Handle requests from transports. pub fn handle_request(chip_id: ChipIdentifier, packet: &mut Vec<u8>, packet_type: u8) { captures_handler::handle_packet_request(chip_id, packet, packet_type.into()); diff --git a/rust/daemon/src/echip/wifi.rs b/rust/daemon/src/echip/wifi.rs index 2dba81a..101f259 100644 --- a/rust/daemon/src/echip/wifi.rs +++ b/rust/daemon/src/echip/wifi.rs @@ -13,9 +13,10 @@ // limitations under the License. use crate::devices::chip::ChipIdentifier; -use crate::echip::{EmulatedChip, SharedEmulatedChip}; +use crate::echip::{packet::hwsim_cmd_response, EmulatedChip, SharedEmulatedChip}; use crate::ffi::ffi_wifi; -use crate::wifi::medium; +use crate::wifi::medium::Medium; +use lazy_static::lazy_static; use log::info; use netsim_proto::common::ChipKind as ProtoChipKind; use netsim_proto::config::WiFi as WiFiConfig; @@ -34,12 +35,16 @@ pub struct Wifi { chip_id: ChipIdentifier, } +// Allocator for chip identifiers. +lazy_static! { + static ref MEDIUM: Mutex<Medium> = Mutex::new(Medium::new(hwsim_cmd_response)); +} + impl EmulatedChip for Wifi { fn handle_request(&self, packet: &[u8]) { - if crate::config::get_dev() { - let _ = medium::parse_hwsim_cmd(packet); + if !MEDIUM.lock().expect("Lock failed").process(self.chip_id, packet) { + ffi_wifi::handle_wifi_request(self.chip_id, &packet.to_vec()); } - ffi_wifi::handle_wifi_request(self.chip_id, &packet.to_vec()); } fn reset(&mut self) { @@ -90,9 +95,6 @@ pub fn new(_params: &CreateParams, chip_id: ChipIdentifier) -> SharedEmulatedChi /// Starts the WiFi service. pub fn wifi_start(config: &MessageField<WiFiConfig>) { - if crate::config::get_dev() { - medium::test_parse_hwsim_cmd(); - } let proto_bytes = config.as_ref().unwrap_or_default().write_to_bytes().unwrap(); ffi_wifi::wifi_start(&proto_bytes); } diff --git a/rust/daemon/src/rust_main.rs b/rust/daemon/src/rust_main.rs index a0a8aa1..ce66d1b 100644 --- a/rust/daemon/src/rust_main.rs +++ b/rust/daemon/src/rust_main.rs @@ -34,7 +34,7 @@ use crate::ffi::ffi_util; use crate::service::{new_test_beacon, Service, ServiceParams}; #[cfg(feature = "cuttlefish")] use netsim_common::util::os_utils::get_server_address; -use netsim_proto::config::{Bluetooth as BluetoothConfig, Config}; +use netsim_proto::config::{Bluetooth as BluetoothConfig, Capture, Config}; use std::env; use std::ffi::{c_char, c_int}; use std::sync::mpsc::Receiver; @@ -155,18 +155,63 @@ fn main_loop(events_rx: Receiver<Event>) { } } -fn run_netsimd_primary(args: NetsimdArgs) { +// Disambiguate config and command line args and store merged setting in config +fn disambiguate_args(args: &mut NetsimdArgs, config: &mut Config) { + // Command line override config file arguments + + // Currently capture cannot be specified off explicitly with command line. + // Enable capture if enabled by command line arg + if args.pcap { + match config.capture.as_mut() { + Some(capture) => { + capture.enabled = Some(true); + } + None => { + let mut capture = Capture::new(); + capture.enabled = Some(true); + config.capture = Some(capture).into(); + } + } + } + + // Ensure Bluetooth config is initialized + let bt_config = match config.bluetooth.as_mut() { + Some(existing_bt_config) => existing_bt_config, + None => { + config.bluetooth = Some(BluetoothConfig::new()).into(); + config.bluetooth.as_mut().unwrap() + } + }; + + // Set disable_address_reuse as needed + if args.disable_address_reuse { + bt_config.disable_address_reuse = Some(true); + } + + // Determine test beacons configuration, default true for cuttlefish + // TODO: remove default for cuttlefish by adding flag to tests + bt_config.test_beacons = match (args.test_beacons, args.no_test_beacons) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => match bt_config.test_beacons { + Some(test_beacons) => Some(test_beacons), + None => Some(cfg!(feature = "cuttlefish")), + }, + (true, true) => panic!("unexpected flag combination"), + }; +} + +fn run_netsimd_primary(mut args: NetsimdArgs) { info!("Netsim Version: {}", get_version()); - let fd_startup_str = args.fd_startup_str.unwrap_or_default(); + let fd_startup_str = args.fd_startup_str.clone().unwrap_or_default(); let instance_num = get_instance(args.instance); let hci_port: u16 = get_hci_port(args.hci_port.unwrap_or_default(), instance_num - 1).try_into().unwrap(); #[cfg(feature = "cuttlefish")] if fd_startup_str.is_empty() { - warn!("Failed to start netsim daemon because fd startup flag `-s` is empty"); - return; + warn!("Warning: netsimd startup flag -s is empty, waiting for gRPC connections."); } if ffi_util::is_netsimd_alive(instance_num) { @@ -174,11 +219,11 @@ fn run_netsimd_primary(args: NetsimdArgs) { return; } + // Load config file let mut config = Config::new(); - if let Some(filename) = args.config { - match config_file::new_from_file(&filename) { + if let Some(ref filename) = args.config { + match config_file::new_from_file(filename) { Ok(config_from_file) => { - info!("Using config in {}", config); config = config_from_file; } Err(e) => { @@ -186,12 +231,17 @@ fn run_netsimd_primary(args: NetsimdArgs) { } } } + // Disambiguate conflicts between cmdline args and config file + disambiguate_args(&mut args, &mut config); + + // Print config file settings + info!("{:#?}", config); let service_params = ServiceParams::new( fd_startup_str, args.no_cli_ui, args.no_web_ui, - config.capture.enabled == Some(true) || args.pcap, + config.capture.enabled.unwrap_or_default(), hci_port, instance_num, args.dev, @@ -220,34 +270,12 @@ fn run_netsimd_primary(args: NetsimdArgs) { spawn_shutdown_publisher(device_events_rx); } - // Command line over-rides config file - if args.disable_address_reuse { - match config.bluetooth.as_mut() { - Some(bt_config) => { - bt_config.disable_address_reuse = Some(true); - } - None => { - let mut bt_config = BluetoothConfig::new(); - bt_config.disable_address_reuse = Some(true); - config.bluetooth = Some(bt_config).into(); - } - } - } - // Start radio facades echip::bluetooth::bluetooth_start(&config.bluetooth, instance_num); echip::wifi::wifi_start(&config.wifi); - // Maybe create test beacons, default true for cuttlefish - // TODO: remove default for cuttlefish by adding flag to tests - if config.bluetooth.test_beacons == Some(true) - || match (args.test_beacons, args.no_test_beacons) { - (true, false) => true, - (false, true) => false, - (false, false) => cfg!(feature = "cuttlefish"), - (true, true) => panic!("unexpected flag combination"), - } - { + // Create test beacons if required + if config.bluetooth.test_beacons == Some(true) { new_test_beacon(1, 1000); new_test_beacon(2, 1000); } diff --git a/rust/daemon/src/session.rs b/rust/daemon/src/session.rs index ac87adc..fbb87b7 100644 --- a/rust/daemon/src/session.rs +++ b/rust/daemon/src/session.rs @@ -46,16 +46,21 @@ struct SessionInfo { stats_proto: NetsimStats, current_device_count: i32, session_start: Instant, + write_json: bool, } impl Session { pub fn new() -> Self { + Self::new_internal(true) + } + fn new_internal(write_json: bool) -> Self { Session { handle: None, info: Arc::new(RwLock::new(SessionInfo { stats_proto: NetsimStats { version: Some(get_version()), ..Default::default() }, current_device_count: 0, session_start: Instant::now(), + write_json, })), } } @@ -65,7 +70,8 @@ impl Session { // Starts the session monitor thread to handle events and // write session stats to json file on event and periodically. pub fn start(&mut self, events_rx: Receiver<Event>) -> &mut Self { - let info = self.info.clone(); + let info = Arc::clone(&self.info); + // Start up session monitor thread self.handle = Some( Builder::new() @@ -73,8 +79,6 @@ impl Session { .spawn(move || { let mut next_instant = Instant::now() + WRITE_INTERVAL; loop { - // Hold the write lock for the duration of this loop iteration - let mut lock = info.write().expect("Could not acquire session lock"); let mut write_stats = true; let this_instant = Instant::now(); let timeout = if next_instant > this_instant { @@ -82,7 +86,11 @@ impl Session { } else { Duration::ZERO }; - match events_rx.recv_timeout(timeout) { + let next_event = events_rx.recv_timeout(timeout); + + // Hold the write lock for the duration of this loop iteration + let mut lock = info.write().expect("Could not acquire session lock"); + match next_event { Ok(Event::ShutDown(ShutDown { reason })) => { // Shutting down, save the session duration and exit update_session_duration(&mut lock); @@ -134,9 +142,11 @@ impl Session { // End of event match - write current stats to json if write_stats { update_session_duration(&mut lock); - let current_stats = get_current_stats(lock.stats_proto.clone()); - if let Err(err) = write_stats_to_json(current_stats) { - error!("Failed to write stats to json: {err:?}"); + if lock.write_json { + let current_stats = get_current_stats(lock.stats_proto.clone()); + if let Err(err) = write_stats_to_json(current_stats) { + error!("Failed to write stats to json: {err:?}"); + } } next_instant = Instant::now() + WRITE_INTERVAL; } @@ -155,10 +165,14 @@ impl Session { if !self.handle.as_ref().expect("no session monitor").is_finished() { info!("session monitor active, waiting..."); } + // Synchronize on session monitor thread self.handle.take().map(JoinHandle::join); + let lock = self.info.read().expect("Could not acquire session lock"); - write_stats_to_json(lock.stats_proto.clone())?; + if lock.write_json { + write_stats_to_json(lock.stats_proto.clone())?; + } Ok(()) } } @@ -184,3 +198,220 @@ fn write_stats_to_json(stats_proto: NetsimStats) -> anyhow::Result<()> { file.flush()?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::events; + use crate::events::{ + ChipAdded, ChipRemoved, DeviceAdded, DeviceRemoved, Event, Events, ShutDown, + }; + use netsim_proto::stats::NetsimRadioStats; + use std::sync::Mutex; + + #[test] + fn test_new() { + let session: Session = Session::new(); + assert!(session.handle.is_none()); + let lock = session.info.read().expect("Could not acquire session lock"); + assert_eq!(lock.current_device_count, 0); + assert!(matches!( + lock.stats_proto, + NetsimStats { version: Some(_), duration_secs: None, .. } + )); + assert_eq!(lock.stats_proto.version.clone().unwrap(), get_version().clone()); + assert_eq!(lock.stats_proto.radio_stats.len(), 0); + } + + fn setup_session_start_test() -> (Session, Arc<Mutex<Events>>) { + let mut session = Session::new_internal(false); + let mut events = events::test::new(); + let events_rx = events::test::subscribe(&mut events); + session.start(events_rx); + (session, events) + } + + fn get_stats_proto(session: &Session) -> NetsimStats { + session.info.read().expect("Could not acquire session lock").stats_proto.clone() + } + + #[test] + fn test_start_and_shutdown() { + let (mut session, mut events) = setup_session_start_test(); + + // we want to be able to check the session time gets incremented + std::thread::sleep(std::time::Duration::from_secs(1)); + + // publish the shutdown afterwards to cause the separate thread to stop + events::test::publish( + &mut events, + Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }), + ); + + // join the handle + session.handle.take().map(JoinHandle::join); + + let stats_proto = get_stats_proto(&session); + + // check device counts are missing if no device add/remove events occurred + assert!(stats_proto.device_count.is_none()); + + assert!(stats_proto.peak_concurrent_devices.is_none()); + + // check the session time is > 0 + assert!(stats_proto.duration_secs.unwrap() > 0u64); + } + + #[test] + fn test_start_and_stop() { + let (session, mut events) = setup_session_start_test(); + + // we want to be able to check the session time gets incremented + std::thread::sleep(std::time::Duration::from_secs(1)); + + // publish the shutdown which is required when using `session.stop()` + events::test::publish( + &mut events, + Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }), + ); + + // should not panic or deadlock + session.stop().unwrap(); + } + + #[test] + fn test_start_and_device_add() { + let (mut session, mut events) = setup_session_start_test(); + + // we want to be able to check the session time gets incremented + std::thread::sleep(std::time::Duration::from_secs(1)); + + events::test::publish( + &mut events, + Event::DeviceAdded(DeviceAdded { builtin: false, ..Default::default() }), + ); + + // publish the shutdown afterwards to cause the separate thread to stop + events::test::publish( + &mut events, + Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }), + ); + + // join the handle + session.handle.take().map(JoinHandle::join); + + // check device counts were incremented + assert_eq!( + session.info.read().expect("Could not acquire session lock").current_device_count, + 1i32 + ); + let stats_proto = get_stats_proto(&session); + + assert_eq!(stats_proto.device_count.unwrap(), 1i32); + assert_eq!(stats_proto.peak_concurrent_devices.unwrap(), 1i32); + // check the session time is > 0 + assert!(stats_proto.duration_secs.unwrap() > 0u64); + } + + #[test] + fn test_start_and_device_add_and_remove() { + let (mut session, mut events) = setup_session_start_test(); + + // we want to be able to check the session time gets incremented + std::thread::sleep(std::time::Duration::from_secs(1)); + + events::test::publish( + &mut events, + Event::DeviceAdded(DeviceAdded { builtin: false, id: 1, ..Default::default() }), + ); + + events::test::publish( + &mut events, + Event::DeviceRemoved(DeviceRemoved { builtin: false, id: 1, ..Default::default() }), + ); + + events::test::publish( + &mut events, + Event::DeviceAdded(DeviceAdded { builtin: false, id: 2, ..Default::default() }), + ); + + events::test::publish( + &mut events, + Event::DeviceRemoved(DeviceRemoved { builtin: false, id: 2, ..Default::default() }), + ); + + // publish the shutdown afterwards to cause the separate thread to stop + events::test::publish( + &mut events, + Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }), + ); + + // join the handle + session.handle.take().map(JoinHandle::join); + + // check the different device counts were incremented as expected + assert_eq!( + session.info.read().expect("Could not acquire session lock").current_device_count, + 0i32 + ); + let stats_proto = get_stats_proto(&session); + assert_eq!(stats_proto.device_count.unwrap(), 2i32); + assert_eq!(stats_proto.peak_concurrent_devices.unwrap(), 1i32); + + // check the session time is > 0 + assert!(stats_proto.duration_secs.unwrap() > 0u64); + } + + #[test] + fn test_start_and_chip_add_and_remove() { + let (mut session, mut events) = setup_session_start_test(); + + // we want to be able to check the session time gets incremented + std::thread::sleep(std::time::Duration::from_secs(1)); + + events::test::publish( + &mut events, + Event::ChipAdded(ChipAdded { builtin: false, chip_id: 0, ..Default::default() }), + ); + + std::thread::sleep(std::time::Duration::from_secs(1)); + + // no radio stats until after we remove the chip + assert_eq!(get_stats_proto(&session).radio_stats.len(), 0usize); + + events::test::publish( + &mut events, + Event::ChipRemoved(ChipRemoved { + chip_id: 0, + radio_stats: vec![NetsimRadioStats { ..Default::default() }], + ..Default::default() + }), + ); + + // publish the shutdown afterwards to cause the separate thread to stop + events::test::publish( + &mut events, + Event::ShutDown(ShutDown { reason: "Stop the session".to_string() }), + ); + + // join the handle + session.handle.take().map(JoinHandle::join); + + // check devices were not incremented (here we only added and removed the chip) + assert_eq!( + session.info.read().expect("Could not acquire session lock").current_device_count, + 0i32 + ); + + let stats_proto = get_stats_proto(&session); + assert_eq!(stats_proto.radio_stats.len(), 1usize); + + // these will still be none since no device level events were processed + assert!(stats_proto.device_count.is_none()); + + assert!(stats_proto.peak_concurrent_devices.is_none()); + + // check the session time is > 0 + assert!(stats_proto.duration_secs.unwrap() > 0u64); + } +} diff --git a/rust/daemon/src/version.rs b/rust/daemon/src/version.rs index 2a0cbc9..d296da1 100644 --- a/rust/daemon/src/version.rs +++ b/rust/daemon/src/version.rs @@ -14,7 +14,7 @@ /// Version library. -pub const VERSION: &str = "0.2.3"; +pub const VERSION: &str = "0.2.6"; pub fn get_version() -> String { VERSION.to_owned() diff --git a/rust/daemon/src/wifi/frame.rs b/rust/daemon/src/wifi/frame.rs index 4b8f462..e2e6bca 100644 --- a/rust/daemon/src/wifi/frame.rs +++ b/rust/daemon/src/wifi/frame.rs @@ -12,175 +12,59 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::ieee80211::Ieee80211; -use super::packets::mac80211_hwsim::HwsimAttrChild::*; -use super::packets::mac80211_hwsim::{HwsimAttr, HwsimMsg, HwsimMsgHdr, TxRate, TxRateFlag}; -use super::packets::netlink::{NlAttrHdr, NlMsgHdr}; +use super::ieee80211::{Ieee80211, MacAddress}; +use super::packets::mac80211_hwsim::{ + HwsimAttr, HwsimCmd, HwsimMsg, HwsimMsgHdr, TxRate, TxRateFlag, +}; +use crate::wifi::hwsim_attr_set::HwsimAttrSet; use anyhow::{anyhow, Context}; use log::{info, warn}; -use std::mem; -// Decode the hwsim Frame. -// -// HWSIM_CMD_FRAME is used to send/receive a broadcasted frame from/to -// kernel/user space, uses these attributes: -// -// HWSIM_ATTR_ADDR_TRANSMITTER, -// HWSIM_ATTR_ADDR_RECEIVER, -// HWSIM_ATTR_FRAME, -// HWSIM_ATTR_FLAGS, -// HWSIM_ATTR_RX_RATE, -// HWSIM_ATTR_SIGNAL, -// HWSIM_ATTR_COOKIE, -// HWSIM_ATTR_FREQ (optional) -// HWSIM_ATTR_TX_INFO (new use) -// HWSIM_ATTR_TX_INFO_FLAGS (new use) - -const NLA_ALIGNTO: usize = 4; - -fn nla_align(len: usize) -> usize { - len.wrapping_add(NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1) -} - -#[derive(Default)] -struct FrameBuilder { - transmitter: Option<[u8; 6]>, - receiver: Option<[u8; 6]>, - data: Option<Vec<u8>>, - flags: Option<u32>, - rx_rate_idx: Option<u32>, - signal: Option<u32>, - cookie: Option<u64>, - freq: Option<u32>, - tx_rates: Option<Vec<TxRate>>, - tx_rate_flags: Option<Vec<TxRateFlag>>, -} +/// Parser for the hwsim Frame command (HWSIM_CMD_FRAME). +/// +/// The Frame command is sent by the kernel's mac80211_hwsim subsystem +/// and contains the IEEE 802.11 frame along with hwsim attributes. +/// +/// This module parses the required and optional hwsim attributes and +/// returns errors if any required attributes are missing. +// The Frame struct contains parsed attributes along with the raw and +// parsed 802.11 frame in `data` and `ieee80211.` #[derive(Debug)] pub struct Frame { - transmitter: Option<[u8; 6]>, - receiver: Option<[u8; 6]>, - pub data: Vec<u8>, - pub ieee80211_hdr: Option<Ieee80211>, - pub flags: Option<u32>, - rx_rate_idx: Option<u32>, + pub transmitter: MacAddress, + pub flags: u32, + pub tx_info: Vec<TxRate>, + pub cookie: u64, pub signal: Option<u32>, - cookie: Option<u64>, pub freq: Option<u32>, - tx_rates: Option<Vec<TxRate>>, - tx_rate_flags: Option<Vec<TxRateFlag>>, -} - -fn anymsg(attr: &str) -> anyhow::Error { - anyhow!("hwsim Frame missing {} attribute", attr) -} - -impl FrameBuilder { - fn transmitter(&mut self, transmitter: &[u8; 6]) -> &mut Self { - self.transmitter = Some(*transmitter); - self - } - - fn receiver(&mut self, receiver: &[u8; 6]) -> &mut Self { - self.receiver = Some(*receiver); - self - } - - fn frame(&mut self, data: &[u8]) -> &mut Self { - self.data = Some(data.to_vec()); - self - } - - fn flags(&mut self, flags: u32) -> &mut Self { - self.flags = Some(flags); - self - } - - fn rx_rate(&mut self, rx_rate_idx: u32) -> &mut Self { - self.rx_rate_idx = Some(rx_rate_idx); - self - } - - fn signal(&mut self, signal: u32) -> &mut Self { - self.signal = Some(signal); - self - } - - fn cookie(&mut self, cookie: u64) -> &mut Self { - self.cookie = Some(cookie); - self - } - - fn freq(&mut self, freq: u32) -> &mut Self { - self.freq = Some(freq); - self - } - - fn tx_rates(&mut self, tx_rates: &[TxRate]) -> &mut Self { - self.tx_rates = Some(tx_rates.to_vec()); - self - } - - fn tx_rate_flags(&mut self, tx_rate_flags: &[TxRateFlag]) -> &mut Self { - self.tx_rate_flags = Some(tx_rate_flags.to_vec()); - self - } - - fn build(mut self) -> anyhow::Result<Frame> { - let data = self.data.ok_or(anymsg("frame"))?; - let ieee80211_hdr = Ieee80211::parse(&data).ok(); - Ok(Frame { - transmitter: self.transmitter, - receiver: self.receiver, - cookie: self.cookie, - flags: self.flags, - rx_rate_idx: self.rx_rate_idx, - signal: self.signal, - data, - ieee80211_hdr, - freq: self.freq, - tx_rates: self.tx_rates, - tx_rate_flags: self.tx_rate_flags, - }) - } + pub data: Vec<u8>, + pub ieee80211: Ieee80211, } impl Frame { - fn builder() -> FrameBuilder { - FrameBuilder::default() - } - // Builds and validates the Frame from the attributes in the // packet. Called when a hwsim packet with HwsimCmd::Frame is // found. - pub fn new(attributes: &[u8]) -> anyhow::Result<Frame> { - let mut index: usize = 0; - let mut builder = Frame::builder(); - while (index < attributes.len()) { - // Parse a generic netlink attribute to get the size - let nla = NlAttrHdr::parse(&attributes[index..index + 4]).unwrap(); - let nla_len = nla.nla_len as usize; - let hwsim_attr = HwsimAttr::parse(&attributes[index..index + nla_len])?; - match hwsim_attr.specialize() { - HwsimAttrAddrTransmitter(child) => builder.transmitter(child.get_address()), - HwsimAttrAddrReceiver(child) => builder.receiver(child.get_address()), - HwsimAttrFrame(child) => builder.frame(child.get_data()), - HwsimAttrFlags(child) => builder.flags(child.get_flags()), - HwsimAttrRxRate(child) => builder.rx_rate(child.get_rx_rate_idx()), - HwsimAttrSignal(child) => builder.signal(child.get_signal()), - HwsimAttrCookie(child) => builder.cookie(child.get_cookie()), - HwsimAttrFreq(child) => builder.freq(child.get_freq()), - HwsimAttrTxInfo(child) => builder.tx_rates(child.get_tx_rates()), - HwsimAttrTxInfoFlags(child) => builder.tx_rate_flags(child.get_tx_rate_flags()), - _ => { - return Err(anyhow!( - "Invalid attribute in frame: {:?}", - hwsim_attr.get_nla_type() as u32 - )) - } - }; - index += nla_align(nla_len); + pub fn parse(msg: &HwsimMsg) -> anyhow::Result<Frame> { + // Only expected to be called with HwsimCmd::Frame + if (msg.get_hwsim_hdr().hwsim_cmd != HwsimCmd::Frame) { + panic!("Invalid hwsim_cmd"); } - builder.build() + let attrs = HwsimAttrSet::parse(msg.get_attributes()).context("HwsimAttrSet")?; + let frame = attrs.frame.clone().context("Frame")?; + let ieee80211 = Ieee80211::parse(&frame).context("Ieee80211")?; + // Required attributes are unwrapped and return an error if + // they are not present. + Ok(Frame { + transmitter: attrs.transmitter.context("transmitter")?, + flags: attrs.flags.context("flags")?, + tx_info: attrs.tx_info.clone().context("tx_info")?, + cookie: attrs.cookie.context("cookie")?, + signal: attrs.signal, + freq: attrs.freq, + data: frame, + ieee80211, + }) } } diff --git a/rust/daemon/src/wifi/hwsim_attr_set.rs b/rust/daemon/src/wifi/hwsim_attr_set.rs new file mode 100644 index 0000000..e5bff25 --- /dev/null +++ b/rust/daemon/src/wifi/hwsim_attr_set.rs @@ -0,0 +1,309 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::ieee80211::MacAddress; +use super::packets::mac80211_hwsim::{ + self, HwsimAttr, HwsimAttrChild::*, HwsimCmd, HwsimMsg, HwsimMsgHdr, TxRate, TxRateFlag, +}; +use super::packets::netlink::{NlAttrHdr, NlMsgHdr}; +use anyhow::{anyhow, Context}; +use log::{info, warn}; +use pdl_runtime::Packet; + +/// Parse or Build the Hwsim attributes into a set. +/// +/// Hwsim attributes are used to exchange data between kernel's +/// mac80211_hwsim subsystem and a user space process and include: +/// +/// HWSIM_ATTR_ADDR_TRANSMITTER, +/// HWSIM_ATTR_ADDR_RECEIVER, +/// HWSIM_ATTR_FRAME, +/// HWSIM_ATTR_FLAGS, +/// HWSIM_ATTR_RX_RATE, +/// HWSIM_ATTR_SIGNAL, +/// HWSIM_ATTR_COOKIE, +/// HWSIM_ATTR_FREQ (optional) +/// HWSIM_ATTR_TX_INFO (new use) +/// HWSIM_ATTR_TX_INFO_FLAGS (new use) + +/// Aligns a length to the specified alignment boundary (`NLA_ALIGNTO`). +/// +/// # Arguments +/// +/// * `array_length`: The length in bytes to be aligned. +/// +/// # Returns +/// +/// * The aligned length, which is a multiple of `NLA_ALIGNTO`. +/// +fn nla_align(array_length: usize) -> usize { + const NLA_ALIGNTO: usize = 4; + array_length.wrapping_add(NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1) +} + +#[derive(Default)] +pub struct HwsimAttrSetBuilder { + transmitter: Option<MacAddress>, + receiver: Option<MacAddress>, + frame: Option<Vec<u8>>, + flags: Option<u32>, + rx_rate_idx: Option<u32>, + signal: Option<u32>, + cookie: Option<u64>, + freq: Option<u32>, + tx_info: Option<Vec<TxRate>>, + tx_info_flags: Option<Vec<TxRateFlag>>, + attributes: Vec<u8>, +} + +#[derive(Debug)] +pub struct HwsimAttrSet { + pub transmitter: Option<MacAddress>, + pub receiver: Option<MacAddress>, + pub frame: Option<Vec<u8>>, + pub flags: Option<u32>, + pub rx_rate_idx: Option<u32>, + pub signal: Option<u32>, + pub cookie: Option<u64>, + pub freq: Option<u32>, + pub tx_info: Option<Vec<TxRate>>, + pub tx_info_flags: Option<Vec<TxRateFlag>>, + pub attributes: Vec<u8>, +} + +/// Builder pattern for each of the HWSIM_ATTR used in conjunction +/// with the HwsimAttr packet formats defined in `mac80211_hwsim.pdl` +/// +/// Used during `parse` or to create new HwsimCmd packets containing +/// an attributes vector. +/// +impl HwsimAttrSetBuilder { + // Add packet to the attributes vec and pad for proper NLA + // alignment. This provides for to_bytes for a HwsimMsg for + // packets constructed by the Builder. + + fn extend_attributes<P: Packet>(&mut self, packet: P) { + let mut vec: Vec<u8> = packet.to_vec(); + let nla_padding = nla_align(vec.len()) - vec.len(); + vec.extend(vec![0; nla_padding]); + self.attributes.extend(vec); + } + + pub fn transmitter(&mut self, transmitter: &[u8; 6]) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrAddrTransmitterBuilder { + address: *transmitter, + nla_m: 0, + nla_o: 0, + } + .build(), + ); + self.transmitter = Some(MacAddress::from(transmitter)); + self + } + + pub fn receiver(&mut self, receiver: &[u8; 6]) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrAddrReceiverBuilder { address: *receiver, nla_m: 0, nla_o: 0 } + .build(), + ); + self.receiver = Some(MacAddress::from(receiver)); + self + } + + pub fn frame(&mut self, frame: &[u8]) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrFrameBuilder { data: (*frame).to_vec(), nla_m: 0, nla_o: 0 } + .build(), + ); + self.frame = Some(frame.to_vec()); + self + } + + pub fn flags(&mut self, flags: u32) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrFlagsBuilder { flags, nla_m: 0, nla_o: 0 }.build(), + ); + self.flags = Some(flags); + self + } + + pub fn rx_rate(&mut self, rx_rate_idx: u32) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrRxRateBuilder { rx_rate_idx, nla_m: 0, nla_o: 0 }.build(), + ); + self.rx_rate_idx = Some(rx_rate_idx); + self + } + + pub fn signal(&mut self, signal: u32) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrSignalBuilder { signal, nla_m: 0, nla_o: 0 }.build(), + ); + self.signal = Some(signal); + self + } + + pub fn cookie(&mut self, cookie: u64) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrCookieBuilder { cookie, nla_m: 0, nla_o: 0 }.build(), + ); + self.cookie = Some(cookie); + self + } + + pub fn freq(&mut self, freq: u32) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrFreqBuilder { freq, nla_m: 0, nla_o: 0 }.build(), + ); + self.freq = Some(freq); + self + } + + pub fn tx_info(&mut self, tx_info: &[TxRate]) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrTxInfoBuilder { + tx_rates: (*tx_info).to_vec(), + nla_m: 0, + nla_o: 0, + } + .build(), + ); + self.tx_info = Some(tx_info.to_vec()); + self + } + + pub fn tx_info_flags(&mut self, tx_rate_flags: &[TxRateFlag]) -> &mut Self { + self.extend_attributes( + mac80211_hwsim::HwsimAttrTxInfoFlagsBuilder { + tx_rate_flags: (*tx_rate_flags).to_vec(), + nla_m: 0, + nla_o: 0, + } + .build(), + ); + self.tx_info_flags = Some(tx_rate_flags.to_vec()); + self + } + + pub fn build(mut self) -> anyhow::Result<HwsimAttrSet> { + Ok(HwsimAttrSet { + transmitter: self.transmitter, + receiver: self.receiver, + cookie: self.cookie, + flags: self.flags, + rx_rate_idx: self.rx_rate_idx, + signal: self.signal, + frame: self.frame, + freq: self.freq, + tx_info: self.tx_info, + tx_info_flags: self.tx_info_flags, + attributes: self.attributes, + }) + } +} + +impl HwsimAttrSet { + /// Creates a new `HwsimAttrSetBuilder` with default settings, ready for configuring attributes. + /// + /// # Returns + /// + /// * A new `HwsimAttrSetBuilder` instance, initialized with default values. + /// + /// # Examples + /// + /// ```rust + /// let mut builder = HwsimAttrSetBuilder::builder(); + /// builder.signal(42).cookie(32); // Example attribute configuration + /// let attr_set = builder.build(); + /// ``` + pub fn builder() -> HwsimAttrSetBuilder { + HwsimAttrSetBuilder::default() + } + + /// Parse and validates the attributes from a HwsimMsg command. + pub fn parse(attributes: &[u8]) -> anyhow::Result<HwsimAttrSet> { + let mut index: usize = 0; + let mut builder = HwsimAttrSet::builder(); + while (index < attributes.len()) { + // Parse a generic netlink attribute to get the size + let nla_hdr = NlAttrHdr::parse(&attributes[index..index + 4]).unwrap(); + let nla_len = nla_hdr.nla_len as usize; + // Now parse a single attribute at a time from the + // attributes to allow padding per attribute. + let hwsim_attr = HwsimAttr::parse(&attributes[index..index + nla_len])?; + match hwsim_attr.specialize() { + HwsimAttrAddrTransmitter(child) => builder.transmitter(child.get_address()), + HwsimAttrAddrReceiver(child) => builder.receiver(child.get_address()), + HwsimAttrFrame(child) => builder.frame(child.get_data()), + HwsimAttrFlags(child) => builder.flags(child.get_flags()), + HwsimAttrRxRate(child) => builder.rx_rate(child.get_rx_rate_idx()), + HwsimAttrSignal(child) => builder.signal(child.get_signal()), + HwsimAttrCookie(child) => builder.cookie(child.get_cookie()), + HwsimAttrFreq(child) => builder.freq(child.get_freq()), + HwsimAttrTxInfo(child) => builder.tx_info(child.get_tx_rates()), + HwsimAttrTxInfoFlags(child) => builder.tx_info_flags(child.get_tx_rate_flags()), + _ => { + return Err(anyhow!( + "Invalid attribute message: {:?}", + hwsim_attr.get_nla_type() as u32 + )) + } + }; + // Manually step through the attribute bytes aligning as + // we go because netlink aligns each attribute which isn't + // a feature of PDL parser. + index += nla_align(nla_len); + } + builder.build() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Validate `HwsimAttrSet` attribute parsing from byte vector. + #[test] + fn test_attr_set_parse() { + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + assert_eq!(hwsim_msg.get_hwsim_hdr().hwsim_cmd, HwsimCmd::Frame); + let attrs = HwsimAttrSet::parse(hwsim_msg.get_attributes()).unwrap(); + + // Validate each attribute parsed + assert_eq!(attrs.transmitter, MacAddress::try_from(11670786u64).ok()); + assert!(attrs.receiver.is_none()); + assert!(attrs.frame.is_some()); + assert_eq!(attrs.flags, Some(2)); + assert!(attrs.rx_rate_idx.is_none()); + assert!(attrs.signal.is_none()); + assert_eq!(attrs.cookie, Some(201)); + assert_eq!(attrs.freq, Some(2422)); + assert!(attrs.tx_info.is_some()); + } + + // Validate the contents of the `attributes` bytes constructed by + // the Builder by matching with the bytes containing the input + // attributes. Confirms attribute order, packet format and + // padding. + #[test] + fn test_attr_set_attributes() { + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + assert_eq!(hwsim_msg.get_hwsim_hdr().hwsim_cmd, HwsimCmd::Frame); + let attrs = HwsimAttrSet::parse(hwsim_msg.get_attributes()).unwrap(); + assert_eq!(&attrs.attributes, hwsim_msg.get_attributes()); + } +} diff --git a/rust/daemon/src/wifi/ieee80211.rs b/rust/daemon/src/wifi/ieee80211.rs index 7e501b8..64bc61b 100644 --- a/rust/daemon/src/wifi/ieee80211.rs +++ b/rust/daemon/src/wifi/ieee80211.rs @@ -20,13 +20,26 @@ include!(concat!(env!("OUT_DIR"), "/ieee80211_packets.rs")); /// A Ieee80211 MAC address +// TODO: Add unit tests. impl fmt::Display for MacAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bytes = u64::to_le_bytes(self.0); write!( f, - "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", - bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0], + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], + ) + } +} + +impl fmt::Display for Ieee80211 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{{ds: {}, src: {}, dst: {}}}", + self.get_ds(), + self.get_source(), + self.get_destination() ) } } @@ -44,6 +57,13 @@ impl From<MacAddress> for [u8; 6] { } } +impl MacAddress { + pub fn is_multicast(&self) -> bool { + let addr = u64::to_le_bytes(self.0); + addr[0] == 0x1 + } +} + impl Ieee80211 { // Frame has addr4 field pub fn has_a4(&self) -> bool { @@ -66,6 +86,17 @@ impl Ieee80211 { && self.ieee80211.stype == (ManagementSubType::ProbeReq as u8) } + pub fn get_ds(&self) -> String { + match self.specialize() { + Ieee80211Child::Ieee80211ToAp(hdr) => "ToAp", + Ieee80211Child::Ieee80211FromAp(hdr) => "FromAp", + Ieee80211Child::Ieee80211Ibss(hdr) => "Ibss", + Ieee80211Child::Ieee80211Wds(hdr) => "Wds", + _ => panic!("unexpected specialized header"), + } + .to_string() + } + pub fn get_source(&self) -> MacAddress { match self.specialize() { Ieee80211Child::Ieee80211ToAp(hdr) => hdr.get_source(), @@ -75,6 +106,19 @@ impl Ieee80211 { _ => panic!("unexpected specialized header"), } } + + /// Ieee80211 packets have 3-4 addresses in different positions based + /// on the FromDS and ToDS flags. This function gets the destination + /// address depending on the FromDS+ToDS packet subtypes. + pub fn get_destination(&self) -> MacAddress { + match self.specialize() { + Ieee80211Child::Ieee80211ToAp(hdr) => hdr.get_destination(), + Ieee80211Child::Ieee80211FromAp(hdr) => hdr.get_destination(), + Ieee80211Child::Ieee80211Ibss(hdr) => hdr.get_destination(), + Ieee80211Child::Ieee80211Wds(hdr) => hdr.get_destination(), + _ => panic!("unexpected specialized header"), + } + } } fn parse_mac_address(s: &str) -> Option<MacAddress> { @@ -116,4 +160,14 @@ mod tests { let b = format!("{}", parse_mac_address("00:0b:85:71:20:ce").unwrap()); assert_eq!(a, b); } + + #[test] + fn test_is_multicast() { + // Multicast MAC address: 01:00:5E:00:00:FB + let mdns_mac_address = parse_mac_address("01:00:5e:00:00:fb").unwrap(); + assert!(mdns_mac_address.is_multicast()); + // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce) + let non_mdns_mac_address = parse_mac_address("00:0b:85:71:20:ce").unwrap(); + assert!(!non_mdns_mac_address.is_multicast()); + } } diff --git a/rust/daemon/src/wifi/medium.rs b/rust/daemon/src/wifi/medium.rs index 11454df..c5c4739 100644 --- a/rust/daemon/src/wifi/medium.rs +++ b/rust/daemon/src/wifi/medium.rs @@ -12,17 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::packets::mac80211_hwsim::{HwsimAttr, HwsimCmd, HwsimMsg, HwsimMsgHdr}; -use super::packets::netlink::{NlAttrHdr, NlMsgHdr}; +use super::ieee80211::MacAddress; +use super::packets::mac80211_hwsim::{HwsimAttr, HwsimCmd, HwsimMsg, HwsimMsgHdr, NlMsgHdr}; +use super::packets::netlink::NlAttrHdr; +use crate::devices::chip::ChipIdentifier; use crate::wifi::frame::Frame; +use crate::wifi::hwsim_attr_set::HwsimAttrSet; +use crate::wifi::packets::mac80211_hwsim::HwsimMsgBuilder; use anyhow::{anyhow, Context}; -use log::{info, warn}; - -const NLA_ALIGNTO: usize = 4; - -fn nla_align(len: usize) -> usize { - len.wrapping_add(NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1) -} +use log::{debug, info, warn}; +use pdl_runtime::Packet; +use std::collections::{HashMap, HashSet}; #[derive(Debug)] pub enum HwsimCmdEnum { @@ -37,71 +37,161 @@ pub enum HwsimCmdEnum { DelMacAddr, } -pub fn parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum> { - match HwsimMsg::parse(packet) { - Ok(hwsim_msg) => match (hwsim_msg.hwsim_hdr.hwsim_cmd) { +struct Station { + client_id: u32, + addr: MacAddress, // virtual interface mac address +} + +pub struct Medium { + callback: HwsimCmdCallback, + stations: HashMap<MacAddress, Station>, +} + +type HwsimCmdCallback = fn(u32, &[u8]); + +impl Medium { + pub fn new(callback: HwsimCmdCallback) -> Medium { + Self { callback, stations: HashMap::new() } + } + fn get_station_by_addr(&self, addr: MacAddress) -> Option<&Station> { + self.stations.get(&addr) + } +} + +impl Station { + fn new(client_id: u32, addr: MacAddress) -> Self { + Self { client_id, addr } + } +} + +/// Process commands from the kernel's mac80211_hwsim subsystem. +/// +/// This is the processing that will be implemented: +/// +/// * The source MacAddress in 802.11 frames is re-mapped to a globally +/// unique MacAddress because resumed Emulator AVDs appear with the +/// same address. +/// +/// * 802.11 frames sent between stations +/// +/// * 802.11 multicast frames are re-broadcast to connected stations. +/// + +impl Medium { + pub fn process(&mut self, client_id: u32, packet: &[u8]) -> bool { + match self.process_internal(client_id, packet) { + Ok(b) => b, + Err(e) => { + warn!("error processing wifi {e}"); + // continue processing hwsim cmd on error + false + } + } + } + + fn process_internal(&mut self, client_id: u32, packet: &[u8]) -> anyhow::Result<bool> { + let hwsim_msg = HwsimMsg::parse(packet)?; + match (hwsim_msg.get_hwsim_hdr().hwsim_cmd) { HwsimCmd::Frame => { - let frame = Frame::new(&hwsim_msg.attributes)?; - Ok(HwsimCmdEnum::Frame(Box::new(frame))) + let frame = Frame::parse(&hwsim_msg)?; + info!( + "Frame chip {}, addr {}, flags {}, cookie {:?}, ieee80211 {}", + client_id, frame.transmitter, frame.flags, frame.cookie, frame.ieee80211 + ); + let addr = frame.transmitter; + // Creates Stations on the fly when there is no config file + let _ = self.stations.entry(addr).or_insert_with(|| Station::new(client_id, addr)); + let sender = self.stations.get(&addr).unwrap(); + self.queue_frame(sender, frame) + } + HwsimCmd::AddMacAddr => { + let attr_set = HwsimAttrSet::parse(hwsim_msg.get_attributes())?; + if let (Some(addr), Some(hwaddr)) = (attr_set.transmitter, attr_set.receiver) { + info!("ADD_MAC_ADDR transmitter {:?} receiver {:?}", hwaddr, addr); + } else { + warn!("ADD_MAC_ADDR missing transmitter or receiver"); + } + Ok(false) } - _ => Err(anyhow!("Unknown HwsimkMsg cmd={:?}", hwsim_msg.hwsim_hdr.hwsim_cmd)), + HwsimCmd::DelMacAddr => { + let attr_set = HwsimAttrSet::parse(hwsim_msg.get_attributes())?; + if let (Some(addr), Some(hwaddr)) = (attr_set.transmitter, attr_set.receiver) { + info!("DEL_MAC_ADDR transmitter {:?} receiver {:?}", hwaddr, addr); + } else { + warn!("DEL_MAC_ADDR missing transmitter or receiver"); + } + Ok(false) + } + _ => { + info!("Another command found {:?}", hwsim_msg); + Ok(false) + } + } + } + + fn queue_frame(&self, station: &Station, frame: Frame) -> anyhow::Result<bool> { + let destination = frame.ieee80211.get_destination(); + if let Some(station) = self.stations.get(&destination) { + info!("Frame deliver from {} to {}", station.addr, destination); + // rewrite packet to destination client: ToAP -> FromAP + Ok(true) + } else if destination.is_multicast() { + info!("Frame multicast {}", frame.ieee80211); + Ok(true) + } else { + // pass to libslirp + Ok(false) + } + } +} + +fn build_tx_info(hwsim_msg: &HwsimMsg) -> anyhow::Result<HwsimMsg> { + let attrs = HwsimAttrSet::parse(hwsim_msg.get_attributes()).context("HwsimAttrSet").unwrap(); + + let hwsim_hdr = hwsim_msg.get_hwsim_hdr(); + let nl_hdr = hwsim_msg.get_nl_hdr(); + let mut new_attr_builder = HwsimAttrSet::builder(); + const SIGNAL: u32 = 4294967246; + + new_attr_builder + .transmitter(&attrs.transmitter.context("transmitter")?.into()) + .flags(attrs.flags.context("flags")?) + .cookie(attrs.cookie.context("cookie")?) + .signal(attrs.signal.unwrap_or(SIGNAL)) + .tx_info(attrs.tx_info.context("tx_info")?.as_slice()); + + let new_attr = new_attr_builder.build().unwrap(); + let nlmsg_len = + nl_hdr.nlmsg_len + new_attr.attributes.len() as u32 - attrs.attributes.len() as u32; + let new_hwsim_msg = HwsimMsgBuilder { + attributes: new_attr.attributes, + hwsim_hdr: HwsimMsgHdr { + hwsim_cmd: HwsimCmd::TxInfoFrame, + hwsim_version: hwsim_hdr.hwsim_version, + reserved: hwsim_hdr.reserved, + }, + nl_hdr: NlMsgHdr { + nlmsg_len, + nlmsg_type: nl_hdr.nlmsg_type, + nlmsg_flags: nl_hdr.nlmsg_flags, + nlmsg_seq: 0, + nlmsg_pid: 0, }, - Err(e) => Err(anyhow!("Unable to parse netlink message! {:?}", e)), } + .build(); + Ok(new_hwsim_msg) } -pub fn test_parse_hwsim_cmd() { - let packet: Vec<u8> = vec![ - 188, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 10, 0, 2, 0, 2, 21, 178, 0, - 0, 0, 0, 0, 98, 0, 3, 0, 64, 0, 0, 0, 255, 255, 255, 255, 255, 255, 74, 129, 38, 251, 211, - 154, 255, 255, 255, 255, 255, 255, 128, 12, 0, 0, 1, 8, 2, 4, 11, 22, 12, 18, 24, 36, 50, - 4, 48, 72, 96, 108, 45, 26, 126, 16, 27, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 22, 35, 1, 120, 200, 26, 64, 0, 0, 191, 206, 0, 0, 0, 0, 0, 0, - 0, 0, 250, 255, 250, 255, 0, 0, 8, 0, 4, 0, 2, 0, 0, 0, 8, 0, 19, 0, 118, 9, 0, 0, 12, 0, - 7, 0, 0, 1, 255, 0, 255, 0, 255, 0, 16, 0, 21, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, - 12, 0, 8, 0, 201, 0, 0, 0, 0, 0, 0, 0, - ]; - assert!(parse_hwsim_cmd(&packet).is_ok()); - - // missing transmitter attribute - let packet2: Vec<u8> = vec![ - 132, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 0, 2, 21, 178, 0, - 0, 0, 0, 0, 76, 0, 3, 0, 8, 2, 0, 0, 2, 21, 178, 0, 0, 0, 0, 19, 16, 133, 254, 1, 82, 85, - 10, 0, 2, 2, 0, 0, 170, 170, 3, 0, 0, 0, 8, 0, 69, 0, 0, 40, 0, 14, 0, 0, 64, 6, 177, 19, - 142, 251, 46, 164, 10, 0, 2, 16, 1, 187, 198, 28, 0, 0, 250, 220, 35, 200, 197, 208, 80, - 16, 255, 255, 57, 216, 0, 0, 8, 0, 5, 0, 1, 0, 0, 0, 8, 0, 6, 0, 206, 255, 255, 255, 8, 0, - 19, 0, 143, 9, 0, 0, - ]; - assert!(parse_hwsim_cmd(&packet2).is_ok()); - - // missing cookie attribute - let packet3: Vec<u8> = vec![ - 144, 1, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 0, 2, 21, 178, 0, - 0, 0, 0, 0, 85, 1, 3, 0, 128, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 19, 16, 133, 254, - 1, 0, 19, 16, 133, 254, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 3, 1, 4, 0, 11, 65, 110, 100, - 114, 111, 105, 100, 87, 105, 102, 105, 1, 4, 130, 132, 139, 150, 3, 1, 8, 42, 1, 7, 45, 26, - 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 22, 8, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, 0, 0, 2, 128, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 0, 19, 16, 133, 254, 1, 0, 19, 16, 133, 254, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 232, 3, 1, 4, 0, 11, 65, 110, 100, 114, 111, 105, 100, 87, 105, - 102, 105, 1, 4, 130, 132, 139, 150, 3, 1, 8, 42, 1, 7, 45, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 22, 8, 0, 19, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, 0, 0, 2, 16, 0, 0, 0, 2, 21, 178, 0, 0, 0, - 0, 19, 16, 133, 254, 1, 0, 19, 16, 133, 254, 1, 0, 0, 1, 4, 0, 0, 1, 192, 1, 4, 130, 132, - 139, 150, 45, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 61, 22, 8, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, - 0, 0, 2, 90, 3, 36, 1, 0, 0, 0, 0, 8, 0, 5, 0, 1, 0, 0, 0, 8, 0, 6, 0, 206, 255, 255, 255, - 8, 0, 19, 0, 143, 9, 0, 0, - ]; - assert!(parse_hwsim_cmd(&packet3).is_ok()); - - // HwsimkMsg cmd=TxInfoFrame packet - let packet3: Vec<u8> = vec![ - 72, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 10, 0, 2, 0, 2, 21, 178, 0, - 0, 0, 0, 0, 8, 0, 4, 0, 4, 0, 0, 0, 12, 0, 8, 0, 60, 0, 0, 0, 0, 0, 0, 0, 8, 0, 6, 0, 206, - 255, 255, 255, 12, 0, 7, 0, 3, 0, 0, 0, 0, 0, 255, 0, - ]; - assert!(parse_hwsim_cmd(&packet3).is_err()); +// It's usd by radiotap.rs for packet capture. +pub fn parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum> { + let hwsim_msg = HwsimMsg::parse(packet)?; + match (hwsim_msg.get_hwsim_hdr().hwsim_cmd) { + HwsimCmd::Frame => { + let frame = Frame::parse(&hwsim_msg)?; + Ok(HwsimCmdEnum::Frame(Box::new(frame))) + } + _ => Err(anyhow!("Unknown HwsimMsg cmd={:?}", hwsim_msg.get_hwsim_hdr().hwsim_cmd)), + } } #[cfg(test)] @@ -110,6 +200,50 @@ mod tests { #[test] fn test_netlink_attr() { - test_parse_hwsim_cmd(); + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv"); + assert!(parse_hwsim_cmd(&packet).is_ok()); + + // missing transmitter attribute + let packet2: Vec<u8> = include!("test_packets/hwsim_cmd_frame2.csv"); + assert!(parse_hwsim_cmd(&packet2).is_err()); + + // missing cookie attribute + let packet3: Vec<u8> = include!("test_packets/hwsim_cmd_frame_no_cookie.csv"); + assert!(parse_hwsim_cmd(&packet3).is_err()); + + // HwsimkMsg cmd=TxInfoFrame packet + let packet3: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv"); + assert!(parse_hwsim_cmd(&packet3).is_err()); + } + + #[test] + fn test_is_mdns_packet() { + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame_mdns.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + let mdns_frame = Frame::parse(&hwsim_msg).unwrap(); + assert!(mdns_frame.ieee80211.get_destination().is_multicast()); + + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + let non_mdns_frame = Frame::parse(&hwsim_msg).unwrap(); + assert!(!non_mdns_frame.ieee80211.get_destination().is_multicast()); + } + + #[test] + fn test_build_tx_info_reconstruct() { + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + assert_eq!(hwsim_msg.get_hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame); + + let new_hwsim_msg = build_tx_info(&hwsim_msg).unwrap(); + assert_eq!(hwsim_msg, new_hwsim_msg); + } + + #[test] + fn test_build_tx_info() { + let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv"); + let hwsim_msg = HwsimMsg::parse(&packet).unwrap(); + let hwsim_msg_tx_info = build_tx_info(&hwsim_msg).unwrap(); + assert_eq!(hwsim_msg_tx_info.get_hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame); } } diff --git a/rust/daemon/src/wifi/mod.rs b/rust/daemon/src/wifi/mod.rs index 8891cce..80842a8 100644 --- a/rust/daemon/src/wifi/mod.rs +++ b/rust/daemon/src/wifi/mod.rs @@ -18,6 +18,7 @@ #![allow(unused)] pub(crate) mod frame; +pub(crate) mod hwsim_attr_set; pub(crate) mod ieee80211; pub(crate) mod medium; pub(crate) mod packets; diff --git a/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame.csv b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame.csv new file mode 100644 index 0000000..34420fe --- /dev/null +++ b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame.csv @@ -0,0 +1 @@ +vec![188, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 10, 0, 2, 0, 2, 21, 178, 0, 0, 0, 0, 0, 98, 0, 3, 0, 64, 0, 0, 0, 255, 255, 255, 255, 255, 255, 74, 129, 38, 251, 211, 154, 255, 255, 255, 255, 255, 255, 128, 12, 0, 0, 1, 8, 2, 4, 11, 22, 12, 18, 24, 36, 50, 4, 48, 72, 96, 108, 45, 26, 126, 16, 27, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 22, 35, 1, 120, 200, 26, 64, 0, 0, 191, 206, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 250, 255, 0, 0, 8, 0, 4, 0, 2, 0, 0, 0, 8, 0, 19, 0, 118, 9, 0, 0, 12, 0, 7, 0, 0, 1, 255, 0, 255, 0, 255, 0, 16, 0, 21, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 12, 0, 8, 0, 201, 0, 0, 0, 0, 0, 0, 0] diff --git a/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame2.csv b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame2.csv new file mode 100644 index 0000000..378bf2c --- /dev/null +++ b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame2.csv @@ -0,0 +1 @@ +vec![132, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 0, 2, 21, 178, 0, 0, 0, 0, 0, 76, 0, 3, 0, 8, 2, 0, 0, 2, 21, 178, 0, 0, 0, 0, 19, 16, 133, 254, 1, 82, 85, 10, 0, 2, 2, 0, 0, 170, 170, 3, 0, 0, 0, 8, 0, 69, 0, 0, 40, 0, 14, 0, 0, 64, 6, 177, 19, 142, 251, 46, 164, 10, 0, 2, 16, 1, 187, 198, 28, 0, 0, 250, 220, 35, 200, 197, 208, 80, 16, 255, 255, 57, 216, 0, 0, 8, 0, 5, 0, 1, 0, 0, 0, 8, 0, 6, 0, 206, 255, 255, 255, 8, 0, 19, 0, 143, 9, 0, 0]
\ No newline at end of file diff --git a/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_mdns.csv b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_mdns.csv new file mode 100644 index 0000000..76b8f4c --- /dev/null +++ b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_mdns.csv @@ -0,0 +1 @@ +vec![160, 2, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 10, 0, 2, 0, 2, 21, 178, 0, 0, 0, 0, 0, 71, 2, 3, 0, 8, 1, 58, 1, 0, 19, 16, 133, 254, 1, 2, 21, 178, 0, 0, 0, 1, 0, 94, 0, 0, 251, 160, 8, 170, 170, 3, 0, 0, 0, 8, 0, 69, 0, 2, 35, 114, 146, 64, 0, 255, 17, 26, 44, 10, 0, 2, 16, 224, 0, 0, 251, 20, 233, 20, 233, 2, 15, 158, 47, 0, 0, 132, 0, 0, 0, 0, 12, 0, 0, 0, 5, 21, 97, 100, 98, 45, 69, 77, 85, 76, 65, 84, 79, 82, 51, 52, 88, 49, 88, 49, 51, 88, 48, 4, 95, 97, 100, 98, 4, 95, 116, 99, 112, 5, 108, 111, 99, 97, 108, 0, 0, 16, 128, 1, 0, 0, 17, 148, 0, 1, 0, 9, 95, 115, 101, 114, 118, 105, 99, 101, 115, 7, 95, 100, 110, 115, 45, 115, 100, 4, 95, 117, 100, 112, 192, 44, 0, 12, 0, 1, 0, 0, 17, 148, 0, 2, 192, 34, 192, 34, 0, 12, 0, 1, 0, 0, 17, 148, 0, 2, 192, 12, 192, 12, 0, 33, 128, 1, 0, 0, 0, 120, 0, 16, 0, 0, 0, 0, 21, 179, 7, 65, 110, 100, 114, 111, 105, 100, 192, 44, 45, 87, 105, 70, 105, 84, 101, 115, 116, 45, 55, 49, 52, 102, 50, 54, 56, 52, 45, 101, 102, 101, 52, 45, 52, 55, 97, 100, 45, 97, 55, 53, 99, 45, 100, 55, 97, 101, 100, 101, 54, 55, 55, 56, 99, 102, 10, 95, 119, 105, 102, 105, 95, 116, 101, 115, 116, 192, 39, 0, 16, 128, 1, 0, 0, 17, 148, 0, 1, 0, 192, 62, 0, 12, 0, 1, 0, 0, 17, 148, 0, 2, 192, 187, 192, 187, 0, 12, 0, 1, 0, 0, 17, 148, 0, 2, 192, 141, 192, 141, 0, 33, 128, 1, 0, 0, 0, 120, 0, 8, 0, 0, 0, 0, 129, 135, 192, 131, 2, 49, 54, 1, 50, 1, 48, 2, 49, 48, 7, 105, 110, 45, 97, 100, 100, 114, 4, 97, 114, 112, 97, 0, 0, 12, 128, 1, 0, 0, 0, 120, 0, 2, 192, 131, 1, 55, 1, 50, 1, 53, 1, 49, 1, 57, 1, 52, 1, 51, 1, 65, 1, 56, 1, 52, 1, 56, 1, 55, 1, 55, 1, 53, 1, 55, 1, 53, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 56, 1, 69, 1, 70, 3, 105, 112, 54, 193, 21, 0, 12, 128, 1, 0, 0, 0, 120, 0, 2, 192, 131, 192, 131, 0, 1, 128, 1, 0, 0, 0, 120, 0, 4, 10, 0, 2, 16, 192, 131, 0, 28, 128, 1, 0, 0, 0, 120, 0, 16, 254, 128, 0, 0, 0, 0, 0, 0, 87, 87, 120, 72, 163, 73, 21, 39, 192, 12, 0, 47, 128, 1, 0, 0, 17, 148, 0, 9, 192, 12, 0, 5, 0, 0, 128, 0, 64, 192, 141, 0, 47, 128, 1, 0, 0, 17, 148, 0, 9, 192, 141, 0, 5, 0, 0, 128, 0, 64, 193, 3, 0, 47, 128, 1, 0, 0, 0, 120, 0, 6, 193, 3, 0, 2, 0, 8, 193, 39, 0, 47, 128, 1, 0, 0, 0, 120, 0, 6, 193, 39, 0, 2, 0, 8, 192, 131, 0, 47, 128, 1, 0, 0, 0, 120, 0, 8, 192, 131, 0, 4, 64, 0, 0, 8, 0, 8, 0, 4, 0, 0, 0, 0, 0, 8, 0, 19, 0, 143, 9, 0, 0, 12, 0, 7, 0, 0, 2, 0, 2, 0, 2, 255, 0, 16, 0, 21, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 255, 0, 0, 12, 0, 8, 0, 139, 0, 0, 0, 0, 0, 0, 0]
\ No newline at end of file diff --git a/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_no_cookie.csv b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_no_cookie.csv new file mode 100644 index 0000000..24177df --- /dev/null +++ b/rust/daemon/src/wifi/test_packets/hwsim_cmd_frame_no_cookie.csv @@ -0,0 +1 @@ +vec![144, 1, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 0, 2, 21, 178, 0, 0, 0, 0, 0, 85, 1, 3, 0, 128, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 19, 16, 133, 254, 1, 0, 19, 16, 133, 254, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 3, 1, 4, 0, 11, 65, 110, 100, 114, 111, 105, 100, 87, 105, 102, 105, 1, 4, 130, 132, 139, 150, 3, 1, 8, 42, 1, 7, 45, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 22, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, 0, 0, 2, 128, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 19, 16, 133, 254, 1, 0, 19, 16, 133, 254, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 3, 1, 4, 0, 11, 65, 110, 100, 114, 111, 105, 100, 87, 105, 102, 105, 1, 4, 130, 132, 139, 150, 3, 1, 8, 42, 1, 7, 45, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 22, 8, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, 0, 0, 2, 16, 0, 0, 0, 2, 21, 178, 0, 0, 0, 0, 19, 16, 133, 254, 1, 0, 19, 16, 133, 254, 1, 0, 0, 1, 4, 0, 0, 1, 192, 1, 4, 130, 132, 139, 150, 45, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 22, 8, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 4, 0, 0, 0, 2, 90, 3, 36, 1, 0, 0, 0, 0, 8, 0, 5, 0, 1, 0, 0, 0, 8, 0, 6, 0, 206, 255, 255, 255, 8, 0, 19, 0, 143, 9, 0, 0]
\ No newline at end of file diff --git a/rust/daemon/src/wifi/test_packets/hwsim_cmd_tx_info.csv b/rust/daemon/src/wifi/test_packets/hwsim_cmd_tx_info.csv new file mode 100644 index 0000000..fd9a9cd --- /dev/null +++ b/rust/daemon/src/wifi/test_packets/hwsim_cmd_tx_info.csv @@ -0,0 +1 @@ +vec![72, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 10, 0, 2, 0, 2, 21, 178, 0, 0, 0, 0, 0, 8, 0, 4, 0, 4, 0, 0, 0, 12, 0, 8, 0, 60, 0, 0, 0, 0, 0, 0, 0, 8, 0, 6, 0, 206, 255, 255, 255, 12, 0, 7, 0, 3, 0, 0, 0, 0, 0, 255, 0]
\ No newline at end of file diff --git a/rust/frontend/Cargo.toml b/rust/frontend/Cargo.toml new file mode 100644 index 0000000..9d5f308 --- /dev/null +++ b/rust/frontend/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "frontend" +version = "0.1.0" +edition = "2021" + +[dependencies] +protobuf = "3.2.0" +protoc-grpcio = "3.0.0" +protoc-rust = "2.27" +grpcio = "0.13.0" +grpcio-sys = "0.12.1" +futures = "0.3.26" + +##[build-dependencies] +##protoc-grpcio = "3.0.0" + +##[[bin]] +##name = "server" +##path = "src/server.rs" + +##[[bin]] +##name = "netsim-grpc" +##path = "src/client.rs"
\ No newline at end of file diff --git a/rust/frontend/src/netsim_test_client.rs b/rust/frontend/src/netsim_test_client.rs new file mode 100644 index 0000000..b65d256 --- /dev/null +++ b/rust/frontend/src/netsim_test_client.rs @@ -0,0 +1,30 @@ +//! netsim Rust grpc test client + +use std::env; +use std::sync::Arc; + +use grpcio::{ChannelBuilder, EnvBuilder}; +use netsim_common::util::os_utils::get_server_address; +use netsim_proto::frontend_grpc::FrontendServiceClient; + +fn main() { + let args: Vec<String> = env::args().collect(); + let server_addr: String = if args.len() > 1 { + args[1].to_owned() + } else { + match get_server_address(1) { + Some(addr) => addr, + None => { + println!("Unable to get server address."); + return; + } + } + }; + let env = Arc::new(EnvBuilder::new().build()); + + let ch = ChannelBuilder::new(env).connect(&server_addr); + let client = FrontendServiceClient::new(ch); + + let reply = client.get_version(&::protobuf::well_known_types::empty::Empty::new()).unwrap(); + println!("Version: {}", reply.version); +} diff --git a/rust/frontend/src/netsim_test_server.rs b/rust/frontend/src/netsim_test_server.rs new file mode 100644 index 0000000..6cbf4f9 --- /dev/null +++ b/rust/frontend/src/netsim_test_server.rs @@ -0,0 +1,117 @@ +//! netsim Rust grpc test server +use std::io::Read; +use std::sync::Arc; +use std::{io, thread}; + +use futures::channel::oneshot; +use futures::executor::block_on; +use futures::prelude::*; +use grpcio::{ + ChannelBuilder, Environment, ResourceQuota, RpcContext, ServerBuilder, ServerCredentials, + UnarySink, +}; + +use netsim_proto::frontend::VersionResponse; +use netsim_proto::frontend_grpc::{create_frontend_service, FrontendService}; + +#[derive(Clone)] +struct FrontendClient; + +impl FrontendService for FrontendClient { + fn get_version( + &mut self, + ctx: RpcContext<'_>, + req: protobuf::well_known_types::empty::Empty, + sink: UnarySink<VersionResponse>, + ) { + let response = VersionResponse { + version: "netsim test server version 0.0.1".to_string(), + ..Default::default() + }; + let f = sink + .success(response) + .map_err(move |e| eprintln!("failed to reply {:?}: {:?}", req, e)) + .map(|_| ()); + ctx.spawn(f) + } + + fn list_device( + &mut self, + _ctx: grpcio::RpcContext, + _req: protobuf::well_known_types::empty::Empty, + _sink: grpcio::UnarySink<netsim_proto::frontend::ListDeviceResponse>, + ) { + todo!() + } + + fn patch_device( + &mut self, + _ctx: grpcio::RpcContext, + _req: netsim_proto::frontend::PatchDeviceRequest, + _sink: grpcio::UnarySink<protobuf::well_known_types::empty::Empty>, + ) { + todo!() + } + + fn reset( + &mut self, + _ctx: grpcio::RpcContext, + _req: protobuf::well_known_types::empty::Empty, + _sink: grpcio::UnarySink<protobuf::well_known_types::empty::Empty>, + ) { + todo!() + } + + fn patch_capture( + &mut self, + _ctx: grpcio::RpcContext, + _req: netsim_proto::frontend::PatchCaptureRequest, + _sink: grpcio::UnarySink<protobuf::well_known_types::empty::Empty>, + ) { + todo!() + } + + fn list_capture( + &mut self, + _ctx: grpcio::RpcContext, + _req: protobuf::well_known_types::empty::Empty, + _sink: grpcio::UnarySink<netsim_proto::frontend::ListCaptureResponse>, + ) { + todo!() + } + + fn get_capture( + &mut self, + _ctx: grpcio::RpcContext, + _req: netsim_proto::frontend::GetCaptureRequest, + _sink: grpcio::ServerStreamingSink<netsim_proto::frontend::GetCaptureResponse>, + ) { + todo!() + } +} + +fn main() { + let env = Arc::new(Environment::new(1)); + let service = create_frontend_service(FrontendClient); + + let quota = ResourceQuota::new(Some("HelloServerQuota")).resize_memory(1024 * 1024); + let ch_builder = ChannelBuilder::new(env.clone()).set_resource_quota(quota); + + let mut server = ServerBuilder::new(env) + .register_service(service) + .channel_args(ch_builder.build_args()) + .build() + .unwrap(); + let port = server.add_listening_port("127.0.0.1:50051", ServerCredentials::insecure()).unwrap(); + server.start(); + println!("listening on port {}", port); + + let (tx, rx) = oneshot::channel(); + thread::spawn(move || { + println!("Press ENTER to exit..."); + let _ = io::stdin().read(&mut [0]).unwrap(); + tx.send(()) + }); + let _ = block_on(rx); + let _ = block_on(server.shutdown()); +} diff --git a/rust/proto/src/frontend.rs b/rust/proto/src/frontend.rs index 053533b..a0227b3 100644 --- a/rust/proto/src/frontend.rs +++ b/rust/proto/src/frontend.rs @@ -1,5 +1,5 @@ // This file is generated by rust-protobuf 3.2.0. Do not edit -// .proto file is parsed by protoc 3.21.12 +// .proto file is parsed by protoc --rust-out=... // @generated // https://github.com/rust-lang/rust-clippy/issues/702 @@ -25,10 +25,14 @@ /// of protobuf runtime. const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0; +/// Response of GetVersion. +/// +/// Returns the version of the netsim service #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.VersionResponse) pub struct VersionResponse { // message fields + /// Version of netsim service // @@protoc_insertion_point(field:netsim.frontend.VersionResponse.version) pub version: ::std::string::String, // special fields @@ -147,10 +151,15 @@ impl ::protobuf::reflect::ProtobufValue for VersionResponse { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Request of CreateDevice. +/// +/// CreateDevice is only used for built-in devices. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.CreateDeviceRequest) pub struct CreateDeviceRequest { // message fields + /// DeviceCreate proto for creation. Check DeviceCreate in model.proto for more + /// detail. // @@protoc_insertion_point(field:netsim.frontend.CreateDeviceRequest.device) pub device: ::protobuf::MessageField<super::model::DeviceCreate>, // special fields @@ -270,10 +279,14 @@ impl ::protobuf::reflect::ProtobufValue for CreateDeviceRequest { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Response of CreateDevice. +/// +/// Returns the device created in netsim #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.CreateDeviceResponse) pub struct CreateDeviceResponse { // message fields + /// Device proto // @@protoc_insertion_point(field:netsim.frontend.CreateDeviceResponse.device) pub device: ::protobuf::MessageField<super::model::Device>, // special fields @@ -393,10 +406,14 @@ impl ::protobuf::reflect::ProtobufValue for CreateDeviceResponse { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Request of DeleteDevice. +/// +/// DeleteDevice is only used for built-in device. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.DeleteChipRequest) pub struct DeleteChipRequest { // message fields + /// Device Identifier // @@protoc_insertion_point(field:netsim.frontend.DeleteChipRequest.id) pub id: u32, // special fields @@ -515,10 +532,16 @@ impl ::protobuf::reflect::ProtobufValue for DeleteChipRequest { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Request of PatchDevice. +/// +/// You may patch the device position, orientation, and the radio states. +/// For built-in devices, you may patch the specific configurations. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.PatchDeviceRequest) pub struct PatchDeviceRequest { // message fields + /// Device proto. You must include either the id or name field to have + /// a successful patch. // @@protoc_insertion_point(field:netsim.frontend.PatchDeviceRequest.device) pub device: ::protobuf::MessageField<super::model::Device>, // special fields @@ -638,12 +661,20 @@ impl ::protobuf::reflect::ProtobufValue for PatchDeviceRequest { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Response for ListDevice request. +/// +/// Returns the emulators and accessory devices that are connected to +/// the network simulator. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.ListDeviceResponse) pub struct ListDeviceResponse { // message fields + /// List of Device protos // @@protoc_insertion_point(field:netsim.frontend.ListDeviceResponse.devices) pub devices: ::std::vec::Vec<super::model::Device>, + /// Last modified timestamp for device resource. + /// The timestamp will be updated if devices state has changed (except for + /// packet counts) // @@protoc_insertion_point(field:netsim.frontend.ListDeviceResponse.last_modified) pub last_modified: ::protobuf::MessageField<::protobuf::well_known_types::timestamp::Timestamp>, // special fields @@ -780,10 +811,14 @@ impl ::protobuf::reflect::ProtobufValue for ListDeviceResponse { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Request for SubscribeDevice. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.SubscribeDeviceRequest) pub struct SubscribeDeviceRequest { // message fields + /// The SubscribeDevice will immediately return if the + /// provided last_modified timestamp is prior to the current last_modified + /// timestamp in device resource. // @@protoc_insertion_point(field:netsim.frontend.SubscribeDeviceRequest.last_modified) pub last_modified: ::protobuf::MessageField<::protobuf::well_known_types::timestamp::Timestamp>, // special fields @@ -903,6 +938,7 @@ impl ::protobuf::reflect::ProtobufValue for SubscribeDeviceRequest { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Response for SubscribeDevice request. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.SubscribeDeviceResponse) pub struct SubscribeDeviceResponse { @@ -1182,12 +1218,15 @@ pub mod subscribe_device_response { } } +/// Request of PatchCapture. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.PatchCaptureRequest) pub struct PatchCaptureRequest { // message fields + /// Capture Identifier // @@protoc_insertion_point(field:netsim.frontend.PatchCaptureRequest.id) pub id: u32, + /// PatchCapture proto // @@protoc_insertion_point(field:netsim.frontend.PatchCaptureRequest.patch) pub patch: ::protobuf::MessageField<patch_capture_request::PatchCapture>, // special fields @@ -1325,10 +1364,13 @@ impl ::protobuf::reflect::ProtobufValue for PatchCaptureRequest { /// Nested message and enums of message `PatchCaptureRequest` pub mod patch_capture_request { + /// Body of PatchCapture that will be channeled into + /// body for HandleCaptureCxx #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.PatchCaptureRequest.PatchCapture) pub struct PatchCapture { // message fields + /// Capture state // @@protoc_insertion_point(field:netsim.frontend.PatchCaptureRequest.PatchCapture.state) pub state: ::protobuf::EnumOrUnknown<super::super::model::State>, // special fields @@ -1448,10 +1490,14 @@ pub mod patch_capture_request { } } +/// Response of ListCapture +/// +/// Returns all capture information of devices connected to netsim. #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.ListCaptureResponse) pub struct ListCaptureResponse { // message fields + /// List of Capture protos // @@protoc_insertion_point(field:netsim.frontend.ListCaptureResponse.captures) pub captures: ::std::vec::Vec<super::model::Capture>, // special fields @@ -1571,10 +1617,12 @@ impl ::protobuf::reflect::ProtobufValue for ListCaptureResponse { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Request of GetCapture #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.GetCaptureRequest) pub struct GetCaptureRequest { // message fields + /// Capture Identifier // @@protoc_insertion_point(field:netsim.frontend.GetCaptureRequest.id) pub id: u32, // special fields @@ -1693,10 +1741,15 @@ impl ::protobuf::reflect::ProtobufValue for GetCaptureRequest { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; } +/// Response of GetCapture +/// +/// Returns a max of 1024 bytes of capture file. +/// GetCapture will be returning a stream of GetCaptureResponse #[derive(PartialEq,Clone,Default,Debug)] // @@protoc_insertion_point(message:netsim.frontend.GetCaptureResponse) pub struct GetCaptureResponse { // message fields + /// Max of 1024 bytes of capture file // @@protoc_insertion_point(field:netsim.frontend.GetCaptureResponse.capture_stream) pub capture_stream: ::std::vec::Vec<u8>, // special fields @@ -1854,7 +1907,169 @@ static file_descriptor_proto_data: &'static [u8] = b"\ reRequest\x1a\x16.google.protobuf.Empty\x12K\n\x0bListCapture\x12\x16.go\ ogle.protobuf.Empty\x1a$.netsim.frontend.ListCaptureResponse\x12W\n\nGet\ Capture\x12\".netsim.frontend.GetCaptureRequest\x1a#.netsim.frontend.Get\ - CaptureResponse0\x01b\x06proto3\ + CaptureResponse0\x01J\xdc)\n\x07\x12\x05\x0e\0\xb3\x01\x01\n\xd2\x04\n\ + \x01\x0c\x12\x03\x0e\0\x122\xc7\x04\x20Copyright\x202022\x20The\x20Andro\ + id\x20Open\x20Source\x20Project\n\n\x20Licensed\x20under\x20the\x20Apach\ + e\x20License,\x20Version\x202.0\x20(the\x20\"License\");\n\x20you\x20may\ + \x20not\x20use\x20this\x20file\x20except\x20in\x20compliance\x20with\x20\ + the\x20License.\n\x20You\x20may\x20obtain\x20a\x20copy\x20of\x20the\x20L\ + icense\x20at\n\n\x20\x20\x20\x20\x20\x20http://www.apache.org/licenses/L\ + ICENSE-2.0\n\n\x20Unless\x20required\x20by\x20applicable\x20law\x20or\ + \x20agreed\x20to\x20in\x20writing,\x20software\n\x20distributed\x20under\ + \x20the\x20License\x20is\x20distributed\x20on\x20an\x20\"AS\x20IS\"\x20B\ + ASIS,\n\x20WITHOUT\x20WARRANTIES\x20OR\x20CONDITIONS\x20OF\x20ANY\x20KIN\ + D,\x20either\x20express\x20or\x20implied.\n\x20See\x20the\x20License\x20\ + for\x20the\x20specific\x20language\x20governing\x20permissions\x20and\n\ + \x20limitations\x20under\x20the\x20License.\n\n\x08\n\x01\x02\x12\x03\ + \x10\0\x18\n\t\n\x02\x03\0\x12\x03\x12\0%\n\t\n\x02\x03\x01\x12\x03\x13\ + \0)\n\t\n\x02\x03\x02\x12\x03\x14\0\x1c\n\xe2\x02\n\x02\x06\0\x12\x04!\0\ + B\x01\x1a\xd5\x02*\n\x20The\x20frontend\x20service\x20for\x20the\x20netw\ + ork\x20simulator.\n\n\x20The\x20network\x20simulator\x20interconnects\ + \x20virtual\x20radio\x20controllers\x20on\x20emulated\n\x20android\x20an\ + d\x20accessory\x20devices\x20to\x20allows\x20control\x20of\x20the\x20top\ + ology,\x20device\n\x20positions,\x20and\x20RF\x20characteristics.\n\n\ + \x20Clients\x20of\x20the\x20frontend\x20service\x20include\x20a\x20Comma\ + nd\x20Line\x20Interface\x20(cli),\x20Mobly\n\x20scripts,\x20and\x20a\x20\ + web\x20UI.\n\n\n\n\n\x03\x06\0\x01\x12\x03!\x08\x17\n5\n\x04\x06\0\x02\0\ + \x12\x03#\x02B\x1a(\x20Get\x20the\x20version\x20of\x20the\x20netsim\x20s\ + ervice.\n\n\x0c\n\x05\x06\0\x02\0\x01\x12\x03#\x06\x10\n\x0c\n\x05\x06\0\ + \x02\0\x02\x12\x03#\x11&\n\x0c\n\x05\x06\0\x02\0\x03\x12\x03#1@\n&\n\x04\ + \x06\0\x02\x01\x12\x03&\x02G\x1a\x19\x20Create\x20a\x20builtin\x20device\ + \n\n\x0c\n\x05\x06\0\x02\x01\x01\x12\x03&\x06\x12\n\x0c\n\x05\x06\0\x02\ + \x01\x02\x12\x03&\x13&\n\x0c\n\x05\x06\0\x02\x01\x03\x12\x03&1E\nX\n\x04\ + \x06\0\x02\x02\x12\x03)\x02D\x1aK\x20Delete\x20a\x20builtin\x20chip.\x20\ + Implicitly\x20deletes\x20devices\x20which\x20contain\x20no\x20chips.\n\n\ + \x0c\n\x05\x06\0\x02\x02\x01\x12\x03)\x06\x10\n\x0c\n\x05\x06\0\x02\x02\ + \x02\x12\x03)\x11\"\n\x0c\n\x05\x06\0\x02\x02\x03\x12\x03)-B\n\x1d\n\x04\ + \x06\0\x02\x03\x12\x03,\x02F\x1a\x10\x20Patch\x20a\x20device\n\n\x0c\n\ + \x05\x06\0\x02\x03\x01\x12\x03,\x06\x11\n\x0c\n\x05\x06\0\x02\x03\x02\ + \x12\x03,\x12$\n\x0c\n\x05\x06\0\x02\x03\x03\x12\x03,/D\n!\n\x04\x06\0\ + \x02\x04\x12\x03/\x02C\x1a\x14\x20Reset\x20all\x20devices.\n\n\x0c\n\x05\ + \x06\0\x02\x04\x01\x12\x03/\x06\x0b\n\x0c\n\x05\x06\0\x02\x04\x02\x12\ + \x03/\x0c!\n\x0c\n\x05\x06\0\x02\x04\x03\x12\x03/,A\n$\n\x04\x06\0\x02\ + \x05\x12\x032\x02E\x1a\x17\x20Get\x20a\x20list\x20of\x20devices\n\n\x0c\ + \n\x05\x06\0\x02\x05\x01\x12\x032\x06\x10\n\x0c\n\x05\x06\0\x02\x05\x02\ + \x12\x032\x11&\n\x0c\n\x05\x06\0\x02\x05\x03\x12\x0321C\n\xa4\x01\n\x04\ + \x06\0\x02\x06\x12\x037\x02P\x1a\x96\x01\x20Get\x20a\x20list\x20of\x20de\ + vices\x20when\x20a\x20device\x20event\x20is\x20published.\n\x20Waits\x20\ + for\x20device\x20event\x20up\x20to\x2015\x20seconds\x20and\x20returns\ + \x20Error\x20response\x20if\x20no\n\x20event\x20is\x20received\n\n\x0c\n\ + \x05\x06\0\x02\x06\x01\x12\x037\x06\x15\n\x0c\n\x05\x06\0\x02\x06\x02\ + \x12\x037\x16,\n\x0c\n\x05\x06\0\x02\x06\x03\x12\x0377N\nt\n\x04\x06\0\ + \x02\x07\x12\x03;\x02H\x1ag\x20Patch\x20a\x20Capture\x20source\x20to\x20\ + turn\x20capture\x20on/off.\n\x20When\x20turned\x20on\x20the\x20old\x20ca\ + pture\x20contents\x20are\x20replaced.\n\n\x0c\n\x05\x06\0\x02\x07\x01\ + \x12\x03;\x06\x12\n\x0c\n\x05\x06\0\x02\x07\x02\x12\x03;\x13&\n\x0c\n\ + \x05\x06\0\x02\x07\x03\x12\x03;1F\n?\n\x04\x06\0\x02\x08\x12\x03>\x02G\ + \x1a2\x20List\x20all\x20Captures\x20currently\x20connected\x20on\x20nets\ + im.\n\n\x0c\n\x05\x06\0\x02\x08\x01\x12\x03>\x06\x11\n\x0c\n\x05\x06\0\ + \x02\x08\x02\x12\x03>\x12'\n\x0c\n\x05\x06\0\x02\x08\x03\x12\x03>2E\nM\n\ + \x04\x06\0\x02\t\x12\x03A\x02H\x1a@\x20Retrieve\x20the\x20contents\x20of\ + \x20the\x20packet\x20capture\x20as\x20streaming\x20bytes\n\n\x0c\n\x05\ + \x06\0\x02\t\x01\x12\x03A\x06\x10\n\x0c\n\x05\x06\0\x02\t\x02\x12\x03A\ + \x11\"\n\x0c\n\x05\x06\0\x02\t\x06\x12\x03A-3\n\x0c\n\x05\x06\0\x02\t\ + \x03\x12\x03A4F\nQ\n\x02\x04\0\x12\x04G\0J\x01\x1aE\x20Response\x20of\ + \x20GetVersion.\n\n\x20Returns\x20the\x20version\x20of\x20the\x20netsim\ + \x20service\n\n\n\n\x03\x04\0\x01\x12\x03G\x08\x17\n(\n\x04\x04\0\x02\0\ + \x12\x03I\x02\x15\x1a\x1b\x20Version\x20of\x20netsim\x20service\n\n\x0c\ + \n\x05\x04\0\x02\0\x05\x12\x03I\x02\x08\n\x0c\n\x05\x04\0\x02\0\x01\x12\ + \x03I\t\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03I\x13\x14\nX\n\x02\x04\ + \x01\x12\x04O\0S\x01\x1aL\x20Request\x20of\x20CreateDevice.\n\n\x20Creat\ + eDevice\x20is\x20only\x20used\x20for\x20built-in\x20devices.\n\n\n\n\x03\ + \x04\x01\x01\x12\x03O\x08\x1b\nc\n\x04\x04\x01\x02\0\x12\x03R\x02'\x1aV\ + \x20DeviceCreate\x20proto\x20for\x20creation.\x20Check\x20DeviceCreate\ + \x20in\x20model.proto\x20for\x20more\n\x20detail.\n\n\x0c\n\x05\x04\x01\ + \x02\0\x06\x12\x03R\x02\x1b\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03R\x1c\"\ + \n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03R%&\nN\n\x02\x04\x02\x12\x04X\0[\ + \x01\x1aB\x20Response\x20of\x20CreateDevice.\n\n\x20Returns\x20the\x20de\ + vice\x20created\x20in\x20netsim\n\n\n\n\x03\x04\x02\x01\x12\x03X\x08\x1c\ + \n\x1b\n\x04\x04\x02\x02\0\x12\x03Z\x02!\x1a\x0e\x20Device\x20proto\n\n\ + \x0c\n\x05\x04\x02\x02\0\x06\x12\x03Z\x02\x15\n\x0c\n\x05\x04\x02\x02\0\ + \x01\x12\x03Z\x16\x1c\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03Z\x1f\x20\nW\ + \n\x02\x04\x03\x12\x04`\0c\x01\x1aK\x20Request\x20of\x20DeleteDevice.\n\ + \n\x20DeleteDevice\x20is\x20only\x20used\x20for\x20built-in\x20device.\n\ + \n\n\n\x03\x04\x03\x01\x12\x03`\x08\x19\n\x20\n\x04\x04\x03\x02\0\x12\ + \x03b\x02\x10\x1a\x13\x20Device\x20Identifier\n\n\x0c\n\x05\x04\x03\x02\ + \0\x05\x12\x03b\x02\x08\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03b\t\x0b\n\ + \x0c\n\x05\x04\x03\x02\0\x03\x12\x03b\x0e\x0f\n\xb0\x01\n\x02\x04\x04\ + \x12\x04i\0m\x01\x1a\xa3\x01\x20Request\x20of\x20PatchDevice.\n\n\x20You\ + \x20may\x20patch\x20the\x20device\x20position,\x20orientation,\x20and\ + \x20the\x20radio\x20states.\n\x20For\x20built-in\x20devices,\x20you\x20m\ + ay\x20patch\x20the\x20specific\x20configurations.\n\n\n\n\x03\x04\x04\ + \x01\x12\x03i\x08\x1a\nf\n\x04\x04\x04\x02\0\x12\x03l\x02!\x1aY\x20Devic\ + e\x20proto.\x20You\x20must\x20include\x20either\x20the\x20id\x20or\x20na\ + me\x20field\x20to\x20have\n\x20a\x20successful\x20patch.\n\n\x0c\n\x05\ + \x04\x04\x02\0\x06\x12\x03l\x02\x15\n\x0c\n\x05\x04\x04\x02\0\x01\x12\ + \x03l\x16\x1c\n\x0c\n\x05\x04\x04\x02\0\x03\x12\x03l\x1f\x20\n\x8a\x01\n\ + \x02\x04\x05\x12\x04s\0z\x01\x1a~\x20Response\x20for\x20ListDevice\x20re\ + quest.\n\n\x20Returns\x20the\x20emulators\x20and\x20accessory\x20devices\ + \x20that\x20are\x20connected\x20to\n\x20the\x20network\x20simulator.\n\n\ + \n\n\x03\x04\x05\x01\x12\x03s\x08\x1a\n$\n\x04\x04\x05\x02\0\x12\x03u\ + \x02+\x1a\x17\x20List\x20of\x20Device\x20protos\n\n\x0c\n\x05\x04\x05\ + \x02\0\x04\x12\x03u\x02\n\n\x0c\n\x05\x04\x05\x02\0\x06\x12\x03u\x0b\x1e\ + \n\x0c\n\x05\x04\x05\x02\0\x01\x12\x03u\x1f&\n\x0c\n\x05\x04\x05\x02\0\ + \x03\x12\x03u)*\n\x94\x01\n\x04\x04\x05\x02\x01\x12\x03y\x02.\x1a\x86\ + \x01\x20Last\x20modified\x20timestamp\x20for\x20device\x20resource.\n\ + \x20The\x20timestamp\x20will\x20be\x20updated\x20if\x20devices\x20state\ + \x20has\x20changed\x20(except\x20for\n\x20packet\x20counts)\n\n\x0c\n\ + \x05\x04\x05\x02\x01\x06\x12\x03y\x02\x1b\n\x0c\n\x05\x04\x05\x02\x01\ + \x01\x12\x03y\x1c)\n\x0c\n\x05\x04\x05\x02\x01\x03\x12\x03y,-\n+\n\x02\ + \x04\x06\x12\x05}\0\x82\x01\x01\x1a\x1e\x20Request\x20for\x20SubscribeDe\ + vice.\n\n\n\n\x03\x04\x06\x01\x12\x03}\x08\x1e\n\xaa\x01\n\x04\x04\x06\ + \x02\0\x12\x04\x81\x01\x027\x1a\x9b\x01\x20The\x20SubscribeDevice\x20wil\ + l\x20immediately\x20return\x20if\x20the\n\x20provided\x20last_modified\ + \x20timestamp\x20is\x20prior\x20to\x20the\x20current\x20last_modified\n\ + \x20timestamp\x20in\x20device\x20resource.\n\n\r\n\x05\x04\x06\x02\0\x04\ + \x12\x04\x81\x01\x02\n\n\r\n\x05\x04\x06\x02\0\x06\x12\x04\x81\x01\x0b$\ + \n\r\n\x05\x04\x06\x02\0\x01\x12\x04\x81\x01%2\n\r\n\x05\x04\x06\x02\0\ + \x03\x12\x04\x81\x0156\n5\n\x02\x04\x07\x12\x06\x85\x01\0\x8d\x01\x01\ + \x1a'\x20Response\x20for\x20SubscribeDevice\x20request.\n\n\x0b\n\x03\ + \x04\x07\x01\x12\x04\x85\x01\x08\x1f\nD\n\x04\x04\x07\x08\0\x12\x06\x87\ + \x01\x02\x8c\x01\x03\x1a4\x20Will\x20return\x20ListDeviceResponse\x20or\ + \x20an\x20EmptyResponse\n\n\r\n\x05\x04\x07\x08\0\x01\x12\x04\x87\x01\ + \x08\x10\n'\n\x04\x04\x07\x02\0\x12\x04\x89\x01\x040\x1a\x19\x20Response\ + \x20for\x20ListDevice\n\n\r\n\x05\x04\x07\x02\0\x06\x12\x04\x89\x01\x04\ + \x16\n\r\n\x05\x04\x07\x02\0\x01\x12\x04\x89\x01\x17+\n\r\n\x05\x04\x07\ + \x02\0\x03\x12\x04\x89\x01./\n\x1e\n\x04\x04\x07\x02\x01\x12\x04\x8b\x01\ + \x04-\x1a\x10\x20Empty\x20Response\n\n\r\n\x05\x04\x07\x02\x01\x06\x12\ + \x04\x8b\x01\x04\x19\n\r\n\x05\x04\x07\x02\x01\x01\x12\x04\x8b\x01\x1a(\ + \n\r\n\x05\x04\x07\x02\x01\x03\x12\x04\x8b\x01+,\n(\n\x02\x04\x08\x12\ + \x06\x90\x01\0\x9c\x01\x01\x1a\x1a\x20Request\x20of\x20PatchCapture.\n\n\ + \x0b\n\x03\x04\x08\x01\x12\x04\x90\x01\x08\x1b\n\"\n\x04\x04\x08\x02\0\ + \x12\x04\x92\x01\x02\x10\x1a\x14\x20Capture\x20Identifier\n\n\r\n\x05\ + \x04\x08\x02\0\x05\x12\x04\x92\x01\x02\x08\n\r\n\x05\x04\x08\x02\0\x01\ + \x12\x04\x92\x01\t\x0b\n\r\n\x05\x04\x08\x02\0\x03\x12\x04\x92\x01\x0e\ + \x0f\n]\n\x04\x04\x08\x03\0\x12\x06\x96\x01\x02\x99\x01\x03\x1aM\x20Body\ + \x20of\x20PatchCapture\x20that\x20will\x20be\x20channeled\x20into\n\x20b\ + ody\x20for\x20HandleCaptureCxx\n\n\r\n\x05\x04\x08\x03\0\x01\x12\x04\x96\ + \x01\n\x16\n\x1f\n\x06\x04\x08\x03\0\x02\0\x12\x04\x98\x01\x04!\x1a\x0f\ + \x20Capture\x20state\n\n\x0f\n\x07\x04\x08\x03\0\x02\0\x06\x12\x04\x98\ + \x01\x04\x16\n\x0f\n\x07\x04\x08\x03\0\x02\0\x01\x12\x04\x98\x01\x17\x1c\ + \n\x0f\n\x07\x04\x08\x03\0\x02\0\x03\x12\x04\x98\x01\x1f\x20\n\"\n\x04\ + \x04\x08\x02\x01\x12\x04\x9b\x01\x02\x19\x1a\x14\x20PatchCapture\x20prot\ + o\n\n\r\n\x05\x04\x08\x02\x01\x06\x12\x04\x9b\x01\x02\x0e\n\r\n\x05\x04\ + \x08\x02\x01\x01\x12\x04\x9b\x01\x0f\x14\n\r\n\x05\x04\x08\x02\x01\x03\ + \x12\x04\x9b\x01\x17\x18\ni\n\x02\x04\t\x12\x06\xa1\x01\0\xa4\x01\x01\ + \x1a[\x20Response\x20of\x20ListCapture\n\n\x20Returns\x20all\x20capture\ + \x20information\x20of\x20devices\x20connected\x20to\x20netsim.\n\n\x0b\n\ + \x03\x04\t\x01\x12\x04\xa1\x01\x08\x1b\n&\n\x04\x04\t\x02\0\x12\x04\xa3\ + \x01\x02-\x1a\x18\x20List\x20of\x20Capture\x20protos\n\n\r\n\x05\x04\t\ + \x02\0\x04\x12\x04\xa3\x01\x02\n\n\r\n\x05\x04\t\x02\0\x06\x12\x04\xa3\ + \x01\x0b\x1f\n\r\n\x05\x04\t\x02\0\x01\x12\x04\xa3\x01\x20(\n\r\n\x05\ + \x04\t\x02\0\x03\x12\x04\xa3\x01+,\n%\n\x02\x04\n\x12\x06\xa7\x01\0\xaa\ + \x01\x01\x1a\x17\x20Request\x20of\x20GetCapture\n\n\x0b\n\x03\x04\n\x01\ + \x12\x04\xa7\x01\x08\x19\n\"\n\x04\x04\n\x02\0\x12\x04\xa9\x01\x02\x10\ + \x1a\x14\x20Capture\x20Identifier\n\n\r\n\x05\x04\n\x02\0\x05\x12\x04\ + \xa9\x01\x02\x08\n\r\n\x05\x04\n\x02\0\x01\x12\x04\xa9\x01\t\x0b\n\r\n\ + \x05\x04\n\x02\0\x03\x12\x04\xa9\x01\x0e\x0f\n\x93\x01\n\x02\x04\x0b\x12\ + \x06\xb0\x01\0\xb3\x01\x01\x1a\x84\x01\x20Response\x20of\x20GetCapture\n\ + \n\x20Returns\x20a\x20max\x20of\x201024\x20bytes\x20of\x20capture\x20fil\ + e.\n\x20GetCapture\x20will\x20be\x20returning\x20a\x20stream\x20of\x20Ge\ + tCaptureResponse\n\n\x0b\n\x03\x04\x0b\x01\x12\x04\xb0\x01\x08\x1a\n1\n\ + \x04\x04\x0b\x02\0\x12\x04\xb2\x01\x02\x1b\x1a#\x20Max\x20of\x201024\x20\ + bytes\x20of\x20capture\x20file\n\n\r\n\x05\x04\x0b\x02\0\x05\x12\x04\xb2\ + \x01\x02\x07\n\r\n\x05\x04\x0b\x02\0\x01\x12\x04\xb2\x01\x08\x16\n\r\n\ + \x05\x04\x0b\x02\0\x03\x12\x04\xb2\x01\x19\x1ab\x06proto3\ "; /// `FileDescriptorProto` object which was a source for this generated file diff --git a/rust/proto/src/frontend_grpc.rs b/rust/proto/src/frontend_grpc.rs new file mode 100644 index 0000000..f1e60ed --- /dev/null +++ b/rust/proto/src/frontend_grpc.rs @@ -0,0 +1,558 @@ +// This file is generated. Do not edit +// @generated + +// https://github.com/Manishearth/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] + +const METHOD_FRONTEND_SERVICE_GET_VERSION: ::grpcio::Method< + super::empty::Empty, + super::frontend::VersionResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/GetVersion", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_CREATE_DEVICE: ::grpcio::Method< + super::frontend::CreateDeviceRequest, + super::frontend::CreateDeviceResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/CreateDevice", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_DELETE_CHIP: ::grpcio::Method< + super::frontend::DeleteChipRequest, + super::empty::Empty, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/DeleteChip", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_PATCH_DEVICE: ::grpcio::Method< + super::frontend::PatchDeviceRequest, + super::empty::Empty, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/PatchDevice", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_RESET: ::grpcio::Method<super::empty::Empty, super::empty::Empty> = + ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/Reset", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + }; + +const METHOD_FRONTEND_SERVICE_LIST_DEVICE: ::grpcio::Method< + super::empty::Empty, + super::frontend::ListDeviceResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/ListDevice", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_SUBSCRIBE_DEVICE: ::grpcio::Method< + super::frontend::SubscribeDeviceRequest, + super::frontend::SubscribeDeviceResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/SubscribeDevice", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_PATCH_CAPTURE: ::grpcio::Method< + super::frontend::PatchCaptureRequest, + super::empty::Empty, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/PatchCapture", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_LIST_CAPTURE: ::grpcio::Method< + super::empty::Empty, + super::frontend::ListCaptureResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/netsim.frontend.FrontendService/ListCapture", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +const METHOD_FRONTEND_SERVICE_GET_CAPTURE: ::grpcio::Method< + super::frontend::GetCaptureRequest, + super::frontend::GetCaptureResponse, +> = ::grpcio::Method { + ty: ::grpcio::MethodType::ServerStreaming, + name: "/netsim.frontend.FrontendService/GetCapture", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +#[derive(Clone)] +pub struct FrontendServiceClient { + pub client: ::grpcio::Client, +} + +impl FrontendServiceClient { + pub fn new(channel: ::grpcio::Channel) -> Self { + FrontendServiceClient { client: ::grpcio::Client::new(channel) } + } + + pub fn get_version_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::frontend::VersionResponse> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_GET_VERSION, req, opt) + } + + pub fn get_version( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<super::frontend::VersionResponse> { + self.get_version_opt(req, ::grpcio::CallOption::default()) + } + + pub fn get_version_async_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::VersionResponse>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_GET_VERSION, req, opt) + } + + pub fn get_version_async( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::VersionResponse>> { + self.get_version_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn create_device_opt( + &self, + req: &super::frontend::CreateDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::frontend::CreateDeviceResponse> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_CREATE_DEVICE, req, opt) + } + + pub fn create_device( + &self, + req: &super::frontend::CreateDeviceRequest, + ) -> ::grpcio::Result<super::frontend::CreateDeviceResponse> { + self.create_device_opt(req, ::grpcio::CallOption::default()) + } + + pub fn create_device_async_opt( + &self, + req: &super::frontend::CreateDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::CreateDeviceResponse>> + { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_CREATE_DEVICE, req, opt) + } + + pub fn create_device_async( + &self, + req: &super::frontend::CreateDeviceRequest, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::CreateDeviceResponse>> + { + self.create_device_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn delete_chip_opt( + &self, + req: &super::frontend::DeleteChipRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::empty::Empty> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_DELETE_CHIP, req, opt) + } + + pub fn delete_chip( + &self, + req: &super::frontend::DeleteChipRequest, + ) -> ::grpcio::Result<super::empty::Empty> { + self.delete_chip_opt(req, ::grpcio::CallOption::default()) + } + + pub fn delete_chip_async_opt( + &self, + req: &super::frontend::DeleteChipRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_DELETE_CHIP, req, opt) + } + + pub fn delete_chip_async( + &self, + req: &super::frontend::DeleteChipRequest, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.delete_chip_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn patch_device_opt( + &self, + req: &super::frontend::PatchDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::empty::Empty> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_PATCH_DEVICE, req, opt) + } + + pub fn patch_device( + &self, + req: &super::frontend::PatchDeviceRequest, + ) -> ::grpcio::Result<super::empty::Empty> { + self.patch_device_opt(req, ::grpcio::CallOption::default()) + } + + pub fn patch_device_async_opt( + &self, + req: &super::frontend::PatchDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_PATCH_DEVICE, req, opt) + } + + pub fn patch_device_async( + &self, + req: &super::frontend::PatchDeviceRequest, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.patch_device_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn reset_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::empty::Empty> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_RESET, req, opt) + } + + pub fn reset(&self, req: &super::empty::Empty) -> ::grpcio::Result<super::empty::Empty> { + self.reset_opt(req, ::grpcio::CallOption::default()) + } + + pub fn reset_async_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_RESET, req, opt) + } + + pub fn reset_async( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.reset_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn list_device_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::frontend::ListDeviceResponse> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_LIST_DEVICE, req, opt) + } + + pub fn list_device( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<super::frontend::ListDeviceResponse> { + self.list_device_opt(req, ::grpcio::CallOption::default()) + } + + pub fn list_device_async_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::ListDeviceResponse>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_LIST_DEVICE, req, opt) + } + + pub fn list_device_async( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::ListDeviceResponse>> { + self.list_device_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn subscribe_device_opt( + &self, + req: &super::frontend::SubscribeDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::frontend::SubscribeDeviceResponse> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_SUBSCRIBE_DEVICE, req, opt) + } + + pub fn subscribe_device( + &self, + req: &super::frontend::SubscribeDeviceRequest, + ) -> ::grpcio::Result<super::frontend::SubscribeDeviceResponse> { + self.subscribe_device_opt(req, ::grpcio::CallOption::default()) + } + + pub fn subscribe_device_async_opt( + &self, + req: &super::frontend::SubscribeDeviceRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::SubscribeDeviceResponse>> + { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_SUBSCRIBE_DEVICE, req, opt) + } + + pub fn subscribe_device_async( + &self, + req: &super::frontend::SubscribeDeviceRequest, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::SubscribeDeviceResponse>> + { + self.subscribe_device_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn patch_capture_opt( + &self, + req: &super::frontend::PatchCaptureRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::empty::Empty> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_PATCH_CAPTURE, req, opt) + } + + pub fn patch_capture( + &self, + req: &super::frontend::PatchCaptureRequest, + ) -> ::grpcio::Result<super::empty::Empty> { + self.patch_capture_opt(req, ::grpcio::CallOption::default()) + } + + pub fn patch_capture_async_opt( + &self, + req: &super::frontend::PatchCaptureRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_PATCH_CAPTURE, req, opt) + } + + pub fn patch_capture_async( + &self, + req: &super::frontend::PatchCaptureRequest, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> { + self.patch_capture_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn list_capture_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<super::frontend::ListCaptureResponse> { + self.client.unary_call(&METHOD_FRONTEND_SERVICE_LIST_CAPTURE, req, opt) + } + + pub fn list_capture( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<super::frontend::ListCaptureResponse> { + self.list_capture_opt(req, ::grpcio::CallOption::default()) + } + + pub fn list_capture_async_opt( + &self, + req: &super::empty::Empty, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::ListCaptureResponse>> { + self.client.unary_call_async(&METHOD_FRONTEND_SERVICE_LIST_CAPTURE, req, opt) + } + + pub fn list_capture_async( + &self, + req: &super::empty::Empty, + ) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::frontend::ListCaptureResponse>> { + self.list_capture_async_opt(req, ::grpcio::CallOption::default()) + } + + pub fn get_capture_opt( + &self, + req: &super::frontend::GetCaptureRequest, + opt: ::grpcio::CallOption, + ) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<super::frontend::GetCaptureResponse>> + { + self.client.server_streaming(&METHOD_FRONTEND_SERVICE_GET_CAPTURE, req, opt) + } + + pub fn get_capture( + &self, + req: &super::frontend::GetCaptureRequest, + ) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<super::frontend::GetCaptureResponse>> + { + self.get_capture_opt(req, ::grpcio::CallOption::default()) + } + pub fn spawn<F>(&self, f: F) + where + F: ::std::future::Future<Output = ()> + Send + 'static, + { + self.client.spawn(f) + } +} + +pub trait FrontendService { + fn get_version( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::empty::Empty, + sink: ::grpcio::UnarySink<super::frontend::VersionResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn create_device( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::CreateDeviceRequest, + sink: ::grpcio::UnarySink<super::frontend::CreateDeviceResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn delete_chip( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::DeleteChipRequest, + sink: ::grpcio::UnarySink<super::empty::Empty>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn patch_device( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::PatchDeviceRequest, + sink: ::grpcio::UnarySink<super::empty::Empty>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn reset( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::empty::Empty, + sink: ::grpcio::UnarySink<super::empty::Empty>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn list_device( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::empty::Empty, + sink: ::grpcio::UnarySink<super::frontend::ListDeviceResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn subscribe_device( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::SubscribeDeviceRequest, + sink: ::grpcio::UnarySink<super::frontend::SubscribeDeviceResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn patch_capture( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::PatchCaptureRequest, + sink: ::grpcio::UnarySink<super::empty::Empty>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn list_capture( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::empty::Empty, + sink: ::grpcio::UnarySink<super::frontend::ListCaptureResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } + fn get_capture( + &mut self, + ctx: ::grpcio::RpcContext, + _req: super::frontend::GetCaptureRequest, + sink: ::grpcio::ServerStreamingSink<super::frontend::GetCaptureResponse>, + ) { + grpcio::unimplemented_call!(ctx, sink) + } +} + +pub fn create_frontend_service<S: FrontendService + Send + Clone + 'static>( + s: S, +) -> ::grpcio::Service { + let mut builder = ::grpcio::ServiceBuilder::new(); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_GET_VERSION, move |ctx, req, resp| { + instance.get_version(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_CREATE_DEVICE, move |ctx, req, resp| { + instance.create_device(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_DELETE_CHIP, move |ctx, req, resp| { + instance.delete_chip(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_PATCH_DEVICE, move |ctx, req, resp| { + instance.patch_device(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder.add_unary_handler(&METHOD_FRONTEND_SERVICE_RESET, move |ctx, req, resp| { + instance.reset(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_LIST_DEVICE, move |ctx, req, resp| { + instance.list_device(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_SUBSCRIBE_DEVICE, move |ctx, req, resp| { + instance.subscribe_device(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_PATCH_CAPTURE, move |ctx, req, resp| { + instance.patch_capture(ctx, req, resp) + }); + let mut instance = s.clone(); + builder = builder + .add_unary_handler(&METHOD_FRONTEND_SERVICE_LIST_CAPTURE, move |ctx, req, resp| { + instance.list_capture(ctx, req, resp) + }); + let mut instance = s; + builder = builder.add_server_streaming_handler( + &METHOD_FRONTEND_SERVICE_GET_CAPTURE, + move |ctx, req, resp| instance.get_capture(ctx, req, resp), + ); + builder.build() +} diff --git a/rust/proto/src/lib.rs b/rust/proto/src/lib.rs index c9eb9ec..8397ff6 100644 --- a/rust/proto/src/lib.rs +++ b/rust/proto/src/lib.rs @@ -14,11 +14,16 @@ // limitations under the License. //! protobuf library for netsim +#[cfg(feature = "cuttlefish")] +use protobuf::well_known_types::empty; pub mod common; pub mod config; pub mod configuration; pub mod frontend; +// TODO: Remove feature check once crate dependency is resolved +#[cfg(feature = "cuttlefish")] +pub mod frontend_grpc; pub mod hci_packet; pub mod model; pub mod packet_streamer; diff --git a/scripts/build_tools.py b/scripts/build_tools.py index e8f5db0..acb00a4 100755 --- a/scripts/build_tools.py +++ b/scripts/build_tools.py @@ -85,7 +85,8 @@ def main(): args.out_dir = os.path.join(AOSP_ROOT, args.out_dir) out = Path(args.out_dir) - shutil.rmtree(out) + if out.exists(): + shutil.rmtree(out) out.mkdir(exist_ok=True, parents=True) @@ -119,8 +120,8 @@ def main(): with ServerConfig(presubmit, args) as cfg: # Turn on sccache? - if cfg.sccache: - launcher.append(f"-DOPTION_CCACHE=${cfg.sccache}") + #if cfg.sccache: + # launcher.append(f"-DOPTION_CCACHE=${cfg.sccache}") # Configure run(launcher, cfg.get_env(), "bld") diff --git a/scripts/proto_update.sh b/scripts/proto_update.sh index 1062b91..6353465 100755 --- a/scripts/proto_update.sh +++ b/scripts/proto_update.sh @@ -40,6 +40,13 @@ export CARGO_HOME=$REPO/objs/rust/.cargo cd $REPO cargo build --manifest-path $CARGO +# run protoc command to generate grpc proto rust files +# Possibly need to install compilers: +# $ cargo install protobuf-codegen +# $ cargo install grpcio-compiler +# TODO: Need to add required crate mappings to work in emu-master-dev +# protoc --rust_out=./rust/proto/src --grpc_out=./rust/proto/src --plugin=protoc-gen-grpc=`which grpc_rust_plugin` -I./proto -I../../external/protobuf/src -I../../packages/modules/Bluetooth/tools/rootcanal/proto ./proto/netsim/frontend.proto + # Undo changed to Cargo.toml git checkout $CARGO diff --git a/scripts/utils.py b/scripts/utils.py index 4bf6124..8809fc7 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -40,9 +40,11 @@ TARGET_MAP = { "windows_x64": "windows_msvc-x86_64", "linux": "linux-x86_64", "linux_x64": "linux-x86_64", + "linux_x86_64": "linux-x86_64", + "linux_aarch64": "linux-aarch64", "darwin": "darwin-x86_64", "darwin_x64": "darwin-x86_64", - "linux_aarch64": "linux-aarch64", + "darwin_x86_64" : "darwin-x86_64", "darwin_aarch64": "darwin-aarch64", } @@ -57,7 +59,7 @@ AVAILABLE = { def platform_to_cmake_target(target): """Translates platform to cmake target""" - return TARGET_MAP[target] + return TARGET_MAP[target.replace("-","_")] def cmake_toolchain(target) -> str: @@ -69,7 +71,7 @@ def cmake_toolchain(target) -> str: / "android" / "build" / "cmake" - / AVAILABLE[TARGET_MAP[target]] + / AVAILABLE[TARGET_MAP[target.replace("-", "_")]] ) |