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