diff --git a/floss/hcidoc/Cargo.toml b/floss/hcidoc/Cargo.toml index ce6437943b2616c096fc0868e7d592972ea49006..d7f279760d0821ae4ff4e6e5e69b4991932536e3 100644 --- a/floss/hcidoc/Cargo.toml +++ b/floss/hcidoc/Cargo.toml @@ -10,3 +10,4 @@ clap = "4.0" chrono = "0.4" num-derive = "0.3" num-traits = "0.2" +lazy_static = "1.0" diff --git a/floss/hcidoc/src/groups/connections.rs b/floss/hcidoc/src/groups/connections.rs index 509eb7f7388a13e3fe2b1af70742ddbd720e3b95..765f7eed9286c107fa6c72e132b21075a4f806a0 100644 --- a/floss/hcidoc/src/groups/connections.rs +++ b/floss/hcidoc/src/groups/connections.rs @@ -801,6 +801,9 @@ impl Rule for OddDisconnectionsRule { // We don't do anything with RX packets yet. PacketChild::AclRx(_) => (), + + // End packet.inner match + _ => (), } } diff --git a/floss/hcidoc/src/groups/controllers.rs b/floss/hcidoc/src/groups/controllers.rs index 3682814796fecbb375b30310c0530944a50c2d33..46927607658a764637689af94b15edc963c962b4 100644 --- a/floss/hcidoc/src/groups/controllers.rs +++ b/floss/hcidoc/src/groups/controllers.rs @@ -1,35 +1,60 @@ ///! Rule group for tracking controller related issues. use chrono::NaiveDateTime; +use lazy_static::lazy_static; +use std::collections::HashSet; use std::convert::Into; use std::io::Write; use crate::engine::{Rule, RuleGroup, Signal}; -use crate::parser::{Packet, PacketChild}; -use bt_packets::hci::EventChild; +use crate::parser::{NewIndex, Packet, PacketChild}; +use bt_packets::hci::{CommandCompleteChild, ErrorCode, EventChild, LocalVersionInformation}; enum ControllerSignal { - HardwareError, // Controller reports HCI event: Hardware Error + HardwareError, // Controller reports HCI event: Hardware Error + LikelyExternalController, // Controller is not in the known list. Likely to be an external controller. } impl Into<&'static str> for ControllerSignal { fn into(self) -> &'static str { match self { ControllerSignal::HardwareError => "HardwareError", + ControllerSignal::LikelyExternalController => "LikelyExternalController", } } } +lazy_static! { + static ref KNOWN_CONTROLLER_NAMES: [String; 6] = [ + String::from("Bluemoon Universal Bluetooth Host Controller"), // AC7625 + String::from("MTK MT7961 #1"), // MT7921LE/MT7921LS + String::from("MTK MT7922 #1"), // MT7922 + String::from("RTK_BT_5.0"), // RTL8822CE + String::from("RT_BT"), // RTL8852AE + String::from(""), // AC9260/AC9560/AX200/AX201/AX203/AX211/MVL8897/QCA6174A3/QCA6174A5/QC_WCN6856 + ]; +} +const KNOWN_CONTROLLER_MANUFACTURERS: [u16; 5] = [ + 2, // Intel. + 29, // Qualcomm + 70, // MediaTek + 72, // Marvell + 93, // Realtek +]; + struct ControllerRule { /// Pre-defined signals discovered in the logs. signals: Vec<Signal>, /// Interesting occurrences surfaced by this rule. reportable: Vec<(NaiveDateTime, String)>, + + /// All detected open_index. + controllers: HashSet<String>, } impl ControllerRule { pub fn new() -> Self { - ControllerRule { signals: vec![], reportable: vec![] } + ControllerRule { signals: vec![], reportable: vec![], controllers: HashSet::new() } } pub fn report_hardware_error(&mut self, packet: &Packet) { @@ -41,6 +66,48 @@ impl ControllerRule { self.reportable.push((packet.ts, format!("controller reported hardware error"))); } + + fn process_local_name(&mut self, local_name: &[u8; 248], packet: &Packet) { + let null_index = local_name.iter().position(|&b| b == 0).unwrap_or(local_name.len()); + match String::from_utf8(local_name[..null_index].to_vec()) { + Ok(name) => { + if !KNOWN_CONTROLLER_NAMES.contains(&name) { + self.signals.push(Signal { + index: packet.index, + ts: packet.ts, + tag: ControllerSignal::LikelyExternalController.into(), + }) + } + } + Err(_) => self.signals.push(Signal { + index: packet.index, + ts: packet.ts, + tag: ControllerSignal::LikelyExternalController.into(), + }), + } + } + + fn process_local_version(&mut self, version_info: &LocalVersionInformation, packet: &Packet) { + if !KNOWN_CONTROLLER_MANUFACTURERS.contains(&version_info.manufacturer_name) { + self.signals.push(Signal { + index: packet.index, + ts: packet.ts, + tag: ControllerSignal::LikelyExternalController.into(), + }) + } + } + + fn process_new_index(&mut self, new_index: &NewIndex, packet: &Packet) { + self.controllers.insert(new_index.get_addr_str()); + + if self.controllers.len() > 1 { + self.signals.push(Signal { + index: packet.index, + ts: packet.ts, + tag: ControllerSignal::LikelyExternalController.into(), + }); + } + } } impl Rule for ControllerRule { @@ -50,8 +117,28 @@ impl Rule for ControllerRule { EventChild::HardwareError(_ev) => { self.report_hardware_error(&packet); } + EventChild::CommandComplete(ev) => match ev.specialize() { + CommandCompleteChild::ReadLocalNameComplete(ev) => { + if ev.get_status() != ErrorCode::Success { + return; + } + + self.process_local_name(ev.get_local_name(), &packet); + } + CommandCompleteChild::ReadLocalVersionInformationComplete(ev) => { + if ev.get_status() != ErrorCode::Success { + return; + } + + self.process_local_version(ev.get_local_version_information(), &packet); + } + _ => {} + }, _ => {} }, + PacketChild::NewIndex(ni) => { + self.process_new_index(ni, &packet); + } _ => {} } } diff --git a/floss/hcidoc/src/groups/informational.rs b/floss/hcidoc/src/groups/informational.rs index 9ae82c9f191eac1544d9e00c1cc8b401cb4b2b5b..31181ebf1c673853221aa309eee5faf3ae9099dd 100644 --- a/floss/hcidoc/src/groups/informational.rs +++ b/floss/hcidoc/src/groups/informational.rs @@ -930,7 +930,10 @@ impl Rule for InformationalRule { // PacketChild::AclRx(rx).specialize() _ => {} } - } // packet.inner + } + + // End packet.inner match + _ => (), } } diff --git a/floss/hcidoc/src/parser.rs b/floss/hcidoc/src/parser.rs index bec8df41c17c8e7ade2fe0bbb998730d319f5a8f..7d11b983189ff3f2fdfc8d0122d38272af0f07ff 100644 --- a/floss/hcidoc/src/parser.rs +++ b/floss/hcidoc/src/parser.rs @@ -287,6 +287,7 @@ pub enum PacketChild { HciEvent(Event), AclTx(Acl), AclRx(Acl), + NewIndex(NewIndex), } impl<'a> TryFrom<&'a LinuxSnoopPacket> for PacketChild { @@ -314,6 +315,11 @@ impl<'a> TryFrom<&'a LinuxSnoopPacket> for PacketChild { Err(e) => Err(format!("Couldn't parse acl rx: {:?}", e)), }, + LinuxSnoopOpcodes::NewIndex => match NewIndex::parse(item.data.as_slice()) { + Ok(data) => Ok(PacketChild::NewIndex(data)), + Err(e) => Err(format!("Couldn't parse new index: {:?}", e)), + }, + // TODO(b/262928525) - Add packet handlers for more packet types. _ => Err(format!("Unhandled packet opcode: {:?}", item.opcode())), } @@ -400,3 +406,44 @@ pub fn get_acl_content(acl: &Acl) -> AclContent { _ => AclContent::None, } } + +#[derive(Clone, Debug)] +pub struct NewIndex { + _hci_type: u8, + _bus: u8, + bdaddr: [u8; 6], + _name: [u8; 8], +} + +impl NewIndex { + fn parse(data: &[u8]) -> Result<NewIndex, std::string::String> { + if data.len() != std::mem::size_of::<NewIndex>() { + return Err(format!("Invalid size for New Index packet: {}", data.len())); + } + + let rest = data; + let (hci_type, rest) = rest.split_at(std::mem::size_of::<u8>()); + let (bus, rest) = rest.split_at(std::mem::size_of::<u8>()); + let (bdaddr, rest) = rest.split_at(6 * std::mem::size_of::<u8>()); + let (name, _rest) = rest.split_at(8 * std::mem::size_of::<u8>()); + + Ok(NewIndex { + _hci_type: hci_type[0], + _bus: bus[0], + bdaddr: bdaddr.try_into().unwrap(), + _name: name.try_into().unwrap(), + }) + } + + pub fn get_addr_str(&self) -> String { + String::from(format!( + "[{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}]", + self.bdaddr[0], + self.bdaddr[1], + self.bdaddr[2], + self.bdaddr[3], + self.bdaddr[4], + self.bdaddr[5] + )) + } +}