1 //! Parsing of various Bluetooth packets.
2 use chrono::NaiveDateTime;
3 use num_derive::{FromPrimitive, ToPrimitive};
4 use num_traits::cast::FromPrimitive;
5 use std::convert::TryFrom;
6 use std::fs::File;
7 use std::io::{BufRead, BufReader, Error, ErrorKind, Read};
8 
9 use hcidoc_packets::hci::{Acl, AclChild, Command, Event};
10 use hcidoc_packets::l2cap::{
11     BasicFrame, BasicFrameChild, Control, ControlFrameChild, GroupFrameChild, LeControl,
12     LeControlFrameChild,
13 };
14 
15 /// Linux snoop file header format. This format is used by `btmon` on Linux systems that have bluez
16 /// installed.
17 #[derive(Clone, Copy, Debug)]
18 pub struct LinuxSnoopHeader {
19     id: [u8; 8],
20     version: u32,
21     data_type: u32,
22 }
23 
24 /// Identifier for a Linux snoop file. In ASCII, this is 'btsnoop\0'.
25 const LINUX_SNOOP_MAGIC: [u8; 8] = [0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00];
26 
27 /// Snoop files in monitor format will have this value in link type.
28 const LINUX_SNOOP_MONITOR_TYPE: u32 = 2001;
29 
30 /// Size of snoop header. 8 bytes for magic and another 8 for additional info.
31 const LINUX_SNOOP_HEADER_SIZE: usize = 16;
32 
33 impl TryFrom<&[u8]> for LinuxSnoopHeader {
34     type Error = String;
35 
try_from(item: &[u8]) -> Result<Self, Self::Error>36     fn try_from(item: &[u8]) -> Result<Self, Self::Error> {
37         if item.len() != LINUX_SNOOP_HEADER_SIZE {
38             return Err(format!("Invalid size for snoop header: {}", item.len()));
39         }
40 
41         let rest = item;
42         let (id_bytes, rest) = rest.split_at(8);
43         let (version_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
44         let (data_type_bytes, _rest) = rest.split_at(std::mem::size_of::<u32>());
45 
46         let header = LinuxSnoopHeader {
47             id: id_bytes.try_into().unwrap(),
48             version: u32::from_be_bytes(version_bytes.try_into().unwrap()),
49             data_type: u32::from_be_bytes(data_type_bytes.try_into().unwrap()),
50         };
51 
52         if header.id != LINUX_SNOOP_MAGIC {
53             return Err(format!("Id is not 'btsnoop'."));
54         }
55 
56         if header.version != 1 {
57             return Err(format!("Version is not supported. Got {}.", header.version));
58         }
59 
60         if header.data_type != LINUX_SNOOP_MONITOR_TYPE {
61             return Err(format!(
62                 "Invalid data type in snoop file. We want monitor type ({}) but got {}",
63                 LINUX_SNOOP_MONITOR_TYPE, header.data_type
64             ));
65         }
66 
67         Ok(header)
68     }
69 }
70 
71 /// Opcodes for Linux snoop packets.
72 #[derive(Debug, FromPrimitive, ToPrimitive)]
73 #[repr(u16)]
74 pub enum LinuxSnoopOpcodes {
75     NewIndex = 0,
76     DeleteIndex,
77     Command,
78     Event,
79     AclTxPacket,
80     AclRxPacket,
81     ScoTxPacket,
82     ScoRxPacket,
83     OpenIndex,
84     CloseIndex,
85     IndexInfo,
86     VendorDiag,
87     SystemNote,
88     UserLogging,
89     CtrlOpen,
90     CtrlClose,
91     CtrlCommand,
92     CtrlEvent,
93     IsoTx,
94     IsoRx,
95 
96     Invalid = 0xffff,
97 }
98 
99 /// Linux snoop file packet format.
100 #[derive(Debug, Clone)]
101 pub struct LinuxSnoopPacket {
102     /// The original length of the captured packet as received via a network.
103     pub original_length: u32,
104 
105     /// The length of the included data (can be smaller than original_length if
106     /// the received packet was truncated).
107     pub included_length: u32,
108     pub flags: u32,
109     pub drops: u32,
110     pub timestamp_magic_us: u64,
111     pub data: Vec<u8>,
112 }
113 
114 impl LinuxSnoopPacket {
adapter_index(&self) -> u16115     pub fn adapter_index(&self) -> u16 {
116         (self.flags >> 16).try_into().unwrap_or(0u16)
117     }
118 
opcode(&self) -> LinuxSnoopOpcodes119     pub fn opcode(&self) -> LinuxSnoopOpcodes {
120         LinuxSnoopOpcodes::from_u32(self.flags & 0xffff).unwrap_or(LinuxSnoopOpcodes::Invalid)
121     }
122 }
123 
124 /// Size of packet preamble (everything except the data).
125 const LINUX_SNOOP_PACKET_PREAMBLE_SIZE: usize = 24;
126 
127 /// Maximum packet size for snoop is the max ACL size + 4 bytes.
128 const LINUX_SNOOP_MAX_PACKET_SIZE: usize = 1486 + 4;
129 
130 /// Number of seconds from the year 1970 to the year 2000.
131 const LINUX_SNOOP_Y2K_OFFSET_IN_SECS: i64 = 946684800i64;
132 
133 /// Snoop timestamps start at year 0 instead of 1970 like unix timestamps. This
134 /// offset is used to represent Jan 1, 2000 AD and can be used to convert back
135 /// to unixtime.
136 const LINUX_SNOOP_Y2K_EPOCH_USECS: i64 = 0x00E03AB44A676000i64;
137 
138 /// Microseconds to seconds.
139 const USECS_TO_SECS: i64 = 1_000_000i64;
140 
141 /// Offset from the snoop timestamp to unixtimestamp in seconds. This is a negative number.
142 const LINUX_SNOOP_OFFSET_TO_UNIXTIME_SECS: i64 =
143     LINUX_SNOOP_Y2K_OFFSET_IN_SECS - (LINUX_SNOOP_Y2K_EPOCH_USECS / USECS_TO_SECS);
144 
145 // Expect specifically the pre-amble to be read here (and no data).
146 impl TryFrom<&[u8]> for LinuxSnoopPacket {
147     type Error = String;
148 
try_from(item: &[u8]) -> Result<Self, Self::Error>149     fn try_from(item: &[u8]) -> Result<Self, Self::Error> {
150         if item.len() != LINUX_SNOOP_PACKET_PREAMBLE_SIZE {
151             return Err(format!("Wrong size for snoop packet preamble: {}", item.len()));
152         }
153 
154         let rest = item;
155         let (orig_len_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
156         let (included_len_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
157         let (flags_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
158         let (drops_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
159         let (ts_bytes, _rest) = rest.split_at(std::mem::size_of::<u64>());
160 
161         // Note that all bytes are in big-endian because they're network order.
162         let packet = LinuxSnoopPacket {
163             original_length: u32::from_be_bytes(orig_len_bytes.try_into().unwrap()),
164             included_length: u32::from_be_bytes(included_len_bytes.try_into().unwrap()),
165             flags: u32::from_be_bytes(flags_bytes.try_into().unwrap()),
166             drops: u32::from_be_bytes(drops_bytes.try_into().unwrap()),
167             timestamp_magic_us: u64::from_be_bytes(ts_bytes.try_into().unwrap()),
168             data: vec![],
169         };
170 
171         Ok(packet)
172     }
173 }
174 
175 /// Reader for Linux snoop files.
176 pub struct LinuxSnoopReader<'a> {
177     fd: Box<dyn BufRead + 'a>,
178 }
179 
180 impl<'a> LinuxSnoopReader<'a> {
new(fd: Box<dyn BufRead + 'a>) -> Self181     fn new(fd: Box<dyn BufRead + 'a>) -> Self {
182         LinuxSnoopReader { fd }
183     }
184 }
185 
186 impl<'a> Iterator for LinuxSnoopReader<'a> {
187     type Item = LinuxSnoopPacket;
188 
next(&mut self) -> Option<Self::Item>189     fn next(&mut self) -> Option<Self::Item> {
190         let mut data = [0u8; LINUX_SNOOP_PACKET_PREAMBLE_SIZE];
191         match self.fd.read_exact(&mut data) {
192             Ok(()) => {}
193             Err(e) => {
194                 // |UnexpectedEof| could be seen since we're trying to read more
195                 // data than is available (i.e. end of file).
196                 if e.kind() != ErrorKind::UnexpectedEof {
197                     eprintln!("Error reading snoop file: {:?}", e);
198                 }
199                 return None;
200             }
201         };
202 
203         match LinuxSnoopPacket::try_from(&data[0..LINUX_SNOOP_PACKET_PREAMBLE_SIZE]) {
204             Ok(mut p) => {
205                 if p.included_length > 0 {
206                     let size: usize = p.included_length.try_into().unwrap();
207                     let mut rem_data = [0u8; LINUX_SNOOP_MAX_PACKET_SIZE];
208                     match self.fd.read_exact(&mut rem_data[0..size]) {
209                         Ok(()) => {
210                             p.data = rem_data[0..size].to_vec();
211                             Some(p)
212                         }
213                         Err(e) => {
214                             eprintln!("Couldn't read any packet data: {}", e);
215                             None
216                         }
217                     }
218                 } else {
219                     Some(p)
220                 }
221             }
222             Err(_) => None,
223         }
224     }
225 }
226 
227 /// What kind of log file is this?
228 #[derive(Clone, Debug)]
229 pub enum LogType {
230     /// Linux snoop file generated by something like `btmon`.
231     LinuxSnoop(LinuxSnoopHeader),
232 }
233 
234 /// Parses different Bluetooth log types.
235 pub struct LogParser {
236     fd: Box<dyn BufRead>,
237     log_type: Option<LogType>,
238 }
239 
240 impl<'a> LogParser {
new(filepath: &str) -> std::io::Result<Self>241     pub fn new(filepath: &str) -> std::io::Result<Self> {
242         let fd: Box<dyn BufRead>;
243         if filepath.len() == 0 {
244             fd = Box::new(BufReader::new(std::io::stdin()));
245         } else {
246             fd = Box::new(BufReader::new(File::open(filepath)?));
247         }
248 
249         Ok(Self { fd, log_type: None })
250     }
251 
252     /// Check the log file type for the current log file. This advances the read pointer.
253     /// For a non-intrusive query, use |get_log_type|.
read_log_type(&mut self) -> std::io::Result<LogType>254     pub fn read_log_type(&mut self) -> std::io::Result<LogType> {
255         let mut buf = [0; LINUX_SNOOP_HEADER_SIZE];
256 
257         self.fd.read_exact(&mut buf)?;
258 
259         if let Ok(header) = LinuxSnoopHeader::try_from(&buf[0..LINUX_SNOOP_HEADER_SIZE]) {
260             let log_type = LogType::LinuxSnoop(header);
261             self.log_type = Some(log_type.clone());
262             Ok(log_type)
263         } else {
264             Err(Error::new(ErrorKind::Other, "Unsupported log file type"))
265         }
266     }
267 
268     /// Get cached log type. To initially read the log type, use |read_log_type|.
get_log_type(&self) -> Option<LogType>269     pub fn get_log_type(&self) -> Option<LogType> {
270         self.log_type.clone()
271     }
272 
get_snoop_iterator(&mut self) -> Option<LinuxSnoopReader>273     pub fn get_snoop_iterator(&mut self) -> Option<LinuxSnoopReader> {
274         // Limit to LinuxSnoop files.
275         if !matches!(self.get_log_type()?, LogType::LinuxSnoop(_)) {
276             return None;
277         }
278 
279         Some(LinuxSnoopReader::new(Box::new(BufReader::new(&mut self.fd))))
280     }
281 }
282 
283 /// Data owned by a packet.
284 #[derive(Debug, Clone)]
285 pub enum PacketChild {
286     HciCommand(Command),
287     HciEvent(Event),
288     AclTx(Acl),
289     AclRx(Acl),
290     NewIndex(NewIndex),
291     SystemNote(String),
292 }
293 
294 impl<'a> TryFrom<&'a LinuxSnoopPacket> for PacketChild {
295     type Error = String;
296 
try_from(item: &'a LinuxSnoopPacket) -> Result<Self, Self::Error>297     fn try_from(item: &'a LinuxSnoopPacket) -> Result<Self, Self::Error> {
298         match item.opcode() {
299             LinuxSnoopOpcodes::Command => match Command::parse(item.data.as_slice()) {
300                 Ok(command) => Ok(PacketChild::HciCommand(command)),
301                 Err(e) => Err(format!("Couldn't parse command: {:?}", e)),
302             },
303 
304             LinuxSnoopOpcodes::Event => match Event::parse(item.data.as_slice()) {
305                 Ok(event) => Ok(PacketChild::HciEvent(event)),
306                 Err(e) => Err(format!("Couldn't parse event: {:?}", e)),
307             },
308 
309             LinuxSnoopOpcodes::AclTxPacket => match Acl::parse(item.data.as_slice()) {
310                 Ok(data) => Ok(PacketChild::AclTx(data)),
311                 Err(e) => Err(format!("Couldn't parse acl tx: {:?}", e)),
312             },
313 
314             LinuxSnoopOpcodes::AclRxPacket => match Acl::parse(item.data.as_slice()) {
315                 Ok(data) => Ok(PacketChild::AclRx(data)),
316                 Err(e) => Err(format!("Couldn't parse acl rx: {:?}", e)),
317             },
318 
319             LinuxSnoopOpcodes::NewIndex => match NewIndex::parse(item.data.as_slice()) {
320                 Ok(data) => Ok(PacketChild::NewIndex(data)),
321                 Err(e) => Err(format!("Couldn't parse new index: {:?}", e)),
322             },
323 
324             LinuxSnoopOpcodes::SystemNote => match String::from_utf8(item.data.to_vec()) {
325                 Ok(data) => Ok(PacketChild::SystemNote(data)),
326                 Err(e) => Err(format!("Couldn't parse system note: {:?}", e)),
327             },
328 
329             // TODO(b/262928525) - Add packet handlers for more packet types.
330             _ => Err(format!("Unhandled packet opcode: {:?}", item.opcode())),
331         }
332     }
333 }
334 
335 /// A single processable packet of data.
336 #[derive(Debug, Clone)]
337 pub struct Packet {
338     /// Timestamp of this packet
339     pub ts: NaiveDateTime,
340 
341     /// Which adapter this packet is for. Unassociated packets should use 0xFFFE.
342     pub adapter_index: u16,
343 
344     /// Packet number in current stream.
345     pub index: usize,
346 
347     /// Inner data for this packet.
348     pub inner: PacketChild,
349 }
350 
351 impl<'a> TryFrom<(usize, &'a LinuxSnoopPacket)> for Packet {
352     type Error = String;
353 
try_from(item: (usize, &'a LinuxSnoopPacket)) -> Result<Self, Self::Error>354     fn try_from(item: (usize, &'a LinuxSnoopPacket)) -> Result<Self, Self::Error> {
355         let (index, packet) = item;
356         match PacketChild::try_from(packet) {
357             Ok(inner) => {
358                 let base_ts = i64::try_from(packet.timestamp_magic_us)
359                     .map_err(|e| format!("u64 conversion error: {}", e))?;
360 
361                 let ts_secs = (base_ts / USECS_TO_SECS) + LINUX_SNOOP_OFFSET_TO_UNIXTIME_SECS;
362                 let ts_nsecs = u32::try_from((base_ts % USECS_TO_SECS) * 1000).unwrap_or(0);
363                 let ts = NaiveDateTime::from_timestamp_opt(ts_secs, ts_nsecs)
364                     .ok_or(format!("timestamp conversion error: {}", base_ts))?;
365                 let adapter_index = packet.adapter_index();
366 
367                 Ok(Packet { ts, adapter_index, index, inner })
368             }
369 
370             Err(e) => Err(e),
371         }
372     }
373 }
374 
375 pub enum AclContent {
376     Control(Control),
377     LeControl(LeControl),
378     ConnectionlessData(u16, Vec<u8>),
379     StandardData(Vec<u8>),
380     None,
381 }
382 
get_acl_content(acl: &Acl) -> AclContent383 pub fn get_acl_content(acl: &Acl) -> AclContent {
384     match acl.specialize() {
385         AclChild::Payload(bytes) => match BasicFrame::parse(bytes.as_ref()) {
386             Ok(bf) => match bf.specialize() {
387                 BasicFrameChild::ControlFrame(cf) => match cf.specialize() {
388                     ControlFrameChild::Payload(p) => match Control::parse(p.as_ref()) {
389                         Ok(control) => AclContent::Control(control),
390                         Err(_) => AclContent::None,
391                     },
392                     _ => AclContent::None,
393                 },
394                 BasicFrameChild::LeControlFrame(lcf) => match lcf.specialize() {
395                     LeControlFrameChild::Payload(p) => match LeControl::parse(p.as_ref()) {
396                         Ok(le_control) => AclContent::LeControl(le_control),
397                         Err(_) => AclContent::None,
398                     },
399                     _ => AclContent::None,
400                 },
401                 BasicFrameChild::GroupFrame(gf) => match gf.specialize() {
402                     GroupFrameChild::Payload(p) => {
403                         AclContent::ConnectionlessData(gf.get_psm(), p.to_vec())
404                     }
405                     _ => AclContent::None,
406                 },
407                 BasicFrameChild::Payload(p) => AclContent::StandardData(p.to_vec()),
408                 _ => AclContent::None,
409             },
410             Err(_) => AclContent::None,
411         },
412         _ => AclContent::None,
413     }
414 }
415 
416 #[derive(Clone, Debug)]
417 pub struct NewIndex {
418     _hci_type: u8,
419     _bus: u8,
420     bdaddr: [u8; 6],
421     _name: [u8; 8],
422 }
423 
424 impl NewIndex {
parse(data: &[u8]) -> Result<NewIndex, std::string::String>425     fn parse(data: &[u8]) -> Result<NewIndex, std::string::String> {
426         if data.len() != std::mem::size_of::<NewIndex>() {
427             return Err(format!("Invalid size for New Index packet: {}", data.len()));
428         }
429 
430         let rest = data;
431         let (hci_type, rest) = rest.split_at(std::mem::size_of::<u8>());
432         let (bus, rest) = rest.split_at(std::mem::size_of::<u8>());
433         let (bdaddr, rest) = rest.split_at(6 * std::mem::size_of::<u8>());
434         let (name, _rest) = rest.split_at(8 * std::mem::size_of::<u8>());
435 
436         Ok(NewIndex {
437             _hci_type: hci_type[0],
438             _bus: bus[0],
439             bdaddr: bdaddr.try_into().unwrap(),
440             _name: name.try_into().unwrap(),
441         })
442     }
443 
get_addr_str(&self) -> String444     pub fn get_addr_str(&self) -> String {
445         String::from(format!(
446             "[{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}]",
447             self.bdaddr[0],
448             self.bdaddr[1],
449             self.bdaddr[2],
450             self.bdaddr[3],
451             self.bdaddr[4],
452             self.bdaddr[5]
453         ))
454     }
455 }
456