// 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. //! Netsim daemon cxx libraries. use std::pin::Pin; use crate::bluetooth::chip::{ create_add_rust_device_result, AddRustDeviceResult, RustBluetoothChipCallbacks, }; use crate::http_server::server_response::ServerResponseWritable; use crate::http_server::server_response::StrHeaders; use cxx::let_cxx_string; use crate::captures::captures_handler::handle_capture_cxx; use crate::devices::devices_handler::{ add_chip_cxx, get_distance_cxx, handle_device_cxx, remove_chip_cxx, AddChipResultCxx, }; use crate::ranging::*; use crate::transport::grpc::{register_grpc_transport, unregister_grpc_transport}; use crate::version::*; use crate::wireless::wifi::handle_wifi_response; use crate::wireless::{ bluetooth::report_invalid_packet_cxx, handle_request_cxx, handle_response_cxx, }; #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim::wireless")] pub mod ffi_wireless { extern "Rust" { #[cxx_name = HandleRequestCxx] fn handle_request_cxx(chip_id: u32, packet: &CxxVector, packet_type: u8); #[cxx_name = HandleResponse] fn handle_response_cxx(chip_id: u32, packet: &CxxVector, packet_type: u8); } } #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim::transport")] pub mod ffi_transport { extern "Rust" { #[cxx_name = RegisterGrpcTransport] fn register_grpc_transport(chip_id: u32); #[cxx_name = UnregisterGrpcTransport] fn unregister_grpc_transport(chip_id: u32); } unsafe extern "C++" { // Grpc server. include!("backend/backend_packet_hub.h"); #[rust_name = handle_grpc_response] #[namespace = "netsim::backend"] fn HandleResponseCxx(chip_id: u32, packet: &Vec, packet_type: u8); include!("core/server.h"); #[namespace = "netsim::server"] type GrpcServer; #[rust_name = shut_down] #[namespace = "netsim::server"] fn Shutdown(self: &GrpcServer); #[rust_name = get_grpc_port] #[namespace = "netsim::server"] fn GetGrpcPort(self: &GrpcServer) -> u32; #[rust_name = run_grpc_server_cxx] #[namespace = "netsim::server"] pub fn RunGrpcServerCxx( netsim_grpc_port: u32, no_cli_ui: bool, vsock: u16, ) -> UniquePtr; // Grpc client. // Expose functions in Cuttlefish only, because it's only used by CVDs and it's // unable to pass function pointers on Windows. #[cfg(feature = "cuttlefish")] include!("backend/grpc_client.h"); #[allow(dead_code)] #[rust_name = stream_packets] #[namespace = "netsim::backend::client"] #[cfg(feature = "cuttlefish")] fn StreamPackets(server: &String) -> u32; #[allow(dead_code)] #[rust_name = read_packet_response_loop] #[namespace = "netsim::backend::client"] #[cfg(feature = "cuttlefish")] fn ReadPacketResponseLoop( stream_id: u32, read_fn: fn(stream_id: u32, proto_bytes: &[u8]), ) -> bool; #[allow(dead_code)] #[rust_name = write_packet_request] #[cfg(feature = "cuttlefish")] #[namespace = "netsim::backend::client"] fn WritePacketRequest(stream_id: u32, proto_bytes: &[u8]) -> bool; } } #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim")] pub mod ffi_bluetooth { extern "Rust" { // Rust Bluetooth device. #[namespace = "netsim::hci::facade"] type DynRustBluetoothChipCallbacks; #[cxx_name = Tick] #[namespace = "netsim::hci::facade"] fn tick(dyn_callbacks: &mut DynRustBluetoothChipCallbacks); #[cxx_name = ReceiveLinkLayerPacket] #[namespace = "netsim::hci::facade"] fn receive_link_layer_packet( dyn_callbacks: &mut DynRustBluetoothChipCallbacks, source_address: String, destination_address: String, packet_type: u8, packet: &[u8], ); // Bluetooth facade. #[namespace = "netsim::hci::facade"] type AddRustDeviceResult; #[cxx_name = "CreateAddRustDeviceResult"] #[namespace = "netsim::hci"] fn create_add_rust_device_result( facade_id: u32, rust_chip: UniquePtr, ) -> Box; // Rust Invalid Packet Report #[cxx_name = "ReportInvalidPacket"] #[namespace = "netsim::hci::facade"] fn report_invalid_packet_cxx( rootcanal_id: u32, reason: i32, description: &CxxString, packet: &CxxVector, ); } #[allow(dead_code)] unsafe extern "C++" { // Bluetooth facade. include!("hci/hci_packet_hub.h"); #[rust_name = handle_bt_request] #[namespace = "netsim::hci"] fn HandleBtRequestCxx(rootcanal_id: u32, packet_type: u8, packet: &Vec); // Rust Bluetooth device. include!("hci/rust_device.h"); #[namespace = "netsim::hci::facade"] type RustBluetoothChip; #[rust_name = send_link_layer_le_packet] #[namespace = "netsim::hci::facade"] fn SendLinkLayerLePacket(self: &RustBluetoothChip, packet: &[u8], tx_power: i8); include!("hci/bluetooth_facade.h"); #[rust_name = bluetooth_get_cxx] #[namespace = "netsim::hci::facade"] pub fn GetCxx(rootcanal_id: u32) -> Vec; #[rust_name = bluetooth_reset] #[namespace = "netsim::hci::facade"] pub fn Reset(rootcanal_id: u32); #[rust_name = bluetooth_remove] #[namespace = "netsim::hci::facade"] pub fn Remove(rootcanal_id: u32); #[rust_name = bluetooth_add] #[namespace = "netsim::hci::facade"] pub fn Add(chip_id: u32, address: &CxxString, controller_proto_bytes: &[u8]) -> u32; /* From https://cxx.rs/binding/box.html#restrictions, ``` If T is an opaque Rust type, the Rust type is required to be Sized i.e. size known at compile time. In the future we may introduce support for dynamically sized opaque Rust types. ``` The workaround is using Box (fat pointer) as the opaque type. Reference: - Passing trait objects to C++. https://github.com/dtolnay/cxx/issues/665. - Exposing trait methods to C++. https://github.com/dtolnay/cxx/issues/667 */ #[rust_name = bluetooth_add_rust_device] #[namespace = "netsim::hci::facade"] pub fn AddRustDevice( chip_id: u32, callbacks: Box, string_type: &CxxString, address: &CxxString, ) -> Box; /// The provided address must be 6 bytes in length #[rust_name = bluetooth_set_rust_device_address] #[namespace = "netsim::hci::facade"] pub fn SetRustDeviceAddress(rootcanal_id: u32, address: [u8; 6]); #[rust_name = bluetooth_remove_rust_device] #[namespace = "netsim::hci::facade"] pub fn RemoveRustDevice(rootcanal_id: u32); #[rust_name = bluetooth_start] #[namespace = "netsim::hci::facade"] pub fn Start(proto_bytes: &[u8], instance_num: u16); #[rust_name = bluetooth_stop] #[namespace = "netsim::hci::facade"] pub fn Stop(); #[rust_name = add_device_to_phy] #[namespace = "netsim::hci::facade"] pub fn AddDeviceToPhy(rootcanal_id: u32, is_low_energy: bool); #[rust_name = remove_device_from_phy] #[namespace = "netsim::hci::facade"] pub fn RemoveDeviceFromPhy(rootcanal_id: u32, is_low_energy: bool); } } #[cxx::bridge(namespace = "netsim::wifi::facade")] pub mod ffi_wifi { #[allow(dead_code)] unsafe extern "C++" { // WiFi facade. include!("wifi/wifi_packet_hub.h"); #[rust_name = handle_wifi_request] #[namespace = "netsim::wifi"] fn HandleWifiRequestCxx(chip_id: u32, packet: &Vec); #[rust_name = hostapd_send] #[namespace = "netsim::wifi"] fn HostapdSendCxx(chip_id: u32, packet: &Vec); #[rust_name = libslirp_send] #[namespace = "netsim::wifi"] fn LibslirpSendCxx(chip_id: u32, packet: &Vec); #[namespace = "netsim::wifi"] pub fn libslirp_main_loop_wait(); include!("wifi/wifi_facade.h"); #[rust_name = wifi_start] pub fn Start(proto_bytes: &[u8]); #[rust_name = wifi_stop] pub fn Stop(); } #[allow(unsafe_op_in_unsafe_fn)] extern "Rust" { #[cxx_name = HandleWiFiResponse] fn handle_wifi_response(packet: &[u8]); } } #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim::device")] pub mod ffi_devices { extern "Rust" { // Device Resource type AddChipResultCxx; #[cxx_name = "GetDeviceId"] fn get_device_id(self: &AddChipResultCxx) -> u32; #[cxx_name = "GetChipId"] fn get_chip_id(self: &AddChipResultCxx) -> u32; #[cxx_name = "IsError"] fn is_error(self: &AddChipResultCxx) -> bool; #[allow(clippy::too_many_arguments)] #[cxx_name = AddChipCxx] fn add_chip_cxx( device_guid: &str, device_name: &str, chip_kind: &CxxString, chip_address: &str, chip_name: &str, chip_manufacturer: &str, chip_product_name: &str, bt_properties: &CxxVector, ) -> Box; #[cxx_name = RemoveChipCxx] fn remove_chip_cxx(device_id: u32, chip_id: u32); #[cxx_name = GetDistanceCxx] fn get_distance_cxx(a: u32, b: u32) -> f32; } } #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim")] pub mod ffi_response_writable { extern "Rust" { // handlers for gRPC server's invocation of API calls #[cxx_name = "HandleCaptureCxx"] fn handle_capture_cxx( responder: Pin<&mut CxxServerResponseWriter>, method: String, param: String, body: String, ); #[cxx_name = "HandleDeviceCxx"] fn handle_device_cxx( responder: Pin<&mut CxxServerResponseWriter>, method: String, param: String, body: String, ); } unsafe extern "C++" { /// A C++ class which can be used to respond to a request. include!("frontend/server_response_writable.h"); #[namespace = "netsim::frontend"] type CxxServerResponseWriter; #[namespace = "netsim::frontend"] fn put_ok_with_length(self: &CxxServerResponseWriter, mime_type: &CxxString, length: usize); #[namespace = "netsim::frontend"] fn put_chunk(self: &CxxServerResponseWriter, chunk: &[u8]); #[namespace = "netsim::frontend"] fn put_ok(self: &CxxServerResponseWriter, mime_type: &CxxString, body: &CxxString); #[namespace = "netsim::frontend"] fn put_error(self: &CxxServerResponseWriter, error_code: u32, error_message: &CxxString); } } #[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "netsim")] pub mod ffi_util { extern "Rust" { // Ranging #[cxx_name = "DistanceToRssi"] fn distance_to_rssi(tx_power: i8, distance: f32) -> i8; // Version #[cxx_name = "GetVersion"] fn get_version() -> String; } #[allow(dead_code)] unsafe extern "C++" { // Crash report. include!("util/crash_report.h"); #[rust_name = set_up_crash_report] #[namespace = "netsim"] pub fn SetUpCrashReport(); // Frontend client. include!("frontend/frontend_client_stub.h"); #[rust_name = is_netsimd_alive] #[namespace = "netsim::frontend"] pub fn IsNetsimdAlive(instance_num: u16) -> bool; } } // It's required so `RustBluetoothChip` can be sent between threads safely. // Ref: How to use opaque types in threads? https://github.com/dtolnay/cxx/issues/1175 // SAFETY: Nothing in `RustBluetoothChip` depends on being run on a particular thread. unsafe impl Send for ffi_bluetooth::RustBluetoothChip {} type DynRustBluetoothChipCallbacks = Box; fn tick(dyn_callbacks: &mut DynRustBluetoothChipCallbacks) { (**dyn_callbacks).tick(); } fn receive_link_layer_packet( dyn_callbacks: &mut DynRustBluetoothChipCallbacks, source_address: String, destination_address: String, packet_type: u8, packet: &[u8], ) { (**dyn_callbacks).receive_link_layer_packet( source_address, destination_address, packet_type, packet, ); } /// CxxServerResponseWriter is defined in server_response_writable.h /// Wrapper struct allows the impl to discover the respective C++ methods pub struct CxxServerResponseWriterWrapper<'a> { pub writer: Pin<&'a mut ffi_response_writable::CxxServerResponseWriter>, } impl ServerResponseWritable for CxxServerResponseWriterWrapper<'_> { fn put_ok_with_length(&mut self, mime_type: &str, length: usize, _headers: StrHeaders) { let_cxx_string!(mime_type = mime_type); self.writer.put_ok_with_length(&mime_type, length); } fn put_chunk(&mut self, chunk: &[u8]) { self.writer.put_chunk(chunk); } fn put_ok(&mut self, mime_type: &str, body: &str, _headers: StrHeaders) { let_cxx_string!(mime_type = mime_type); let_cxx_string!(body = body); self.writer.put_ok(&mime_type, &body); } fn put_error(&mut self, error_code: u16, error_message: &str) { let_cxx_string!(error_message = error_message); self.writer.put_error(error_code.into(), &error_message); } fn put_ok_with_vec(&mut self, _mime_type: &str, _body: Vec, _headers: StrHeaders) { todo!() } fn put_ok_switch_protocol(&mut self, _connection: &str, _headers: StrHeaders) { todo!() } }