1 //! Parsing of various Bluetooth packets.
2 use chrono::{DateTime, 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 /// Snoop file header format.
16 #[derive(Debug)]
17 pub struct SnoopHeader {
18 _id: [u8; 8],
19 _version: u32,
20 datalink_type: SnoopDatalinkType,
21 }
22
23 /// Identifier for a snoop file. In ASCII, this is 'btsnoop\0'.
24 const SNOOP_MAGIC: [u8; 8] = [0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00];
25
26 /// Size of snoop header. 8 bytes for magic, 4 bytes for version, and 4 bytes for snoop type.
27 const SNOOP_HEADER_SIZE: usize = 16;
28
29 #[derive(Debug, FromPrimitive, ToPrimitive)]
30 #[repr(u32)]
31 enum SnoopDatalinkType {
32 H4Uart = 1002,
33 LinuxMonitor = 2001,
34 }
35
36 impl TryFrom<&[u8]> for SnoopHeader {
37 type Error = String;
38
try_from(item: &[u8]) -> Result<Self, Self::Error>39 fn try_from(item: &[u8]) -> Result<Self, Self::Error> {
40 if item.len() != SNOOP_HEADER_SIZE {
41 return Err(format!("Invalid size for snoop header: {}", item.len()));
42 }
43
44 let rest = item;
45 let (id_bytes, rest) = rest.split_at(8);
46 let (version_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
47 let (data_type_bytes, _rest) = rest.split_at(std::mem::size_of::<u32>());
48
49 let id = id_bytes.try_into().unwrap();
50 let version = u32::from_be_bytes(version_bytes.try_into().unwrap());
51 let data_type = u32::from_be_bytes(data_type_bytes.try_into().unwrap());
52
53 if id != SNOOP_MAGIC {
54 return Err(format!("Id is not 'btsnoop'."));
55 }
56
57 if version != 1 {
58 return Err(format!("Version is not supported. Got {}.", version));
59 }
60
61 let datalink_type = match SnoopDatalinkType::from_u32(data_type) {
62 Some(datalink_type) => datalink_type,
63 None => return Err(format!("Unsupported datalink type {}", data_type)),
64 };
65
66 return Ok(SnoopHeader { _id: id, _version: version, datalink_type });
67 }
68 }
69
70 /// Opcodes for snoop packets.
71 #[derive(Debug, FromPrimitive, ToPrimitive)]
72 #[repr(u16)]
73 pub enum SnoopOpcodes {
74 NewIndex = 0,
75 DeleteIndex,
76 Command,
77 Event,
78 AclTxPacket,
79 AclRxPacket,
80 ScoTxPacket,
81 ScoRxPacket,
82 OpenIndex,
83 CloseIndex,
84 IndexInfo,
85 VendorDiag,
86 SystemNote,
87 UserLogging,
88 CtrlOpen,
89 CtrlClose,
90 CtrlCommand,
91 CtrlEvent,
92 IsoTx,
93 IsoRx,
94
95 Invalid = 0xffff,
96 }
97
98 /// Size of packet preamble (everything except the data).
99 const SNOOP_PACKET_PREAMBLE_SIZE: usize = 24;
100
101 /// Number of microseconds from btsnoop zero to Linux epoch.
102 const SNOOP_Y0_TO_Y1970_US: i64 = 62_168_256_000_000_000;
103
104 /// Snoop file packet format.
105 #[derive(Debug, Clone)]
106 #[allow(dead_code)]
107 pub struct SnoopPacketPreamble {
108 /// The original length of the captured packet as received via a network.
109 pub original_length: u32,
110
111 /// The length of the included data (can be smaller than original_length if
112 /// the received packet was truncated).
113 pub included_length: u32,
114 pub flags: u32,
115 pub drops: u32,
116 pub timestamp_us: u64,
117 }
118
119 impl SnoopPacketPreamble {
from_fd<'a>(fd: &mut Box<dyn BufRead + 'a>) -> Option<SnoopPacketPreamble>120 fn from_fd<'a>(fd: &mut Box<dyn BufRead + 'a>) -> Option<SnoopPacketPreamble> {
121 let mut buf = [0u8; SNOOP_PACKET_PREAMBLE_SIZE];
122 match fd.read_exact(&mut buf) {
123 Ok(()) => {}
124 Err(e) => {
125 // |UnexpectedEof| could be seen since we're trying to read more
126 // data than is available (i.e. end of file).
127 if e.kind() != ErrorKind::UnexpectedEof {
128 eprintln!("Error reading preamble: {:?}", e);
129 }
130 return None;
131 }
132 };
133
134 match SnoopPacketPreamble::try_from(&buf[0..SNOOP_PACKET_PREAMBLE_SIZE]) {
135 Ok(preamble) => Some(preamble),
136 Err(e) => {
137 eprintln!("Error reading preamble: {}", e);
138 None
139 }
140 }
141 }
142 }
143
144 impl TryFrom<&[u8]> for SnoopPacketPreamble {
145 type Error = String;
146
try_from(item: &[u8]) -> Result<Self, Self::Error>147 fn try_from(item: &[u8]) -> Result<Self, Self::Error> {
148 if item.len() != SNOOP_PACKET_PREAMBLE_SIZE {
149 return Err(format!("Wrong size for snoop packet preamble: {}", item.len()));
150 }
151
152 let rest = item;
153 let (orig_len_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
154 let (included_len_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
155 let (flags_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
156 let (drops_bytes, rest) = rest.split_at(std::mem::size_of::<u32>());
157 let (ts_bytes, _rest) = rest.split_at(std::mem::size_of::<u64>());
158
159 // Note that all bytes are in big-endian because they're network order.
160 let preamble = SnoopPacketPreamble {
161 original_length: u32::from_be_bytes(orig_len_bytes.try_into().unwrap()),
162 included_length: u32::from_be_bytes(included_len_bytes.try_into().unwrap()),
163 flags: u32::from_be_bytes(flags_bytes.try_into().unwrap()),
164 drops: u32::from_be_bytes(drops_bytes.try_into().unwrap()),
165 timestamp_us: u64::from_be_bytes(ts_bytes.try_into().unwrap()),
166 };
167
168 Ok(preamble)
169 }
170 }
171
172 pub trait GeneralSnoopPacket {
adapter_index(&self) -> u16173 fn adapter_index(&self) -> u16;
opcode(&self) -> SnoopOpcodes174 fn opcode(&self) -> SnoopOpcodes;
preamble(&self) -> &SnoopPacketPreamble175 fn preamble(&self) -> &SnoopPacketPreamble;
data(&self) -> &Vec<u8>176 fn data(&self) -> &Vec<u8>;
177
get_timestamp(&self) -> Option<NaiveDateTime>178 fn get_timestamp(&self) -> Option<NaiveDateTime> {
179 let preamble = self.preamble();
180 let ts_i64 = i64::try_from(preamble.timestamp_us).unwrap_or(i64::MAX);
181
182 // TODO: directly use Datetime::from_timestamp_micros() once chrono package is re-vendored
183 // to v0.4.35. Below is the actual implementation of that function.
184 let from_timestamp_micros = |micros: i64| -> Option<DateTime<_>> {
185 let secs = micros.div_euclid(1_000_000);
186 let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000;
187 DateTime::from_timestamp(secs, nsecs)
188 };
189 from_timestamp_micros(ts_i64 - SNOOP_Y0_TO_Y1970_US).map(|date| date.naive_utc())
190 }
191 }
192
193 pub struct LinuxSnoopPacket {
194 pub preamble: SnoopPacketPreamble,
195 pub data: Vec<u8>,
196 }
197
198 impl GeneralSnoopPacket for LinuxSnoopPacket {
adapter_index(&self) -> u16199 fn adapter_index(&self) -> u16 {
200 (self.preamble.flags >> 16).try_into().unwrap_or(0u16)
201 }
opcode(&self) -> SnoopOpcodes202 fn opcode(&self) -> SnoopOpcodes {
203 SnoopOpcodes::from_u32(self.preamble.flags & 0xffff).unwrap_or(SnoopOpcodes::Invalid)
204 }
preamble(&self) -> &SnoopPacketPreamble205 fn preamble(&self) -> &SnoopPacketPreamble {
206 &self.preamble
207 }
data(&self) -> &Vec<u8>208 fn data(&self) -> &Vec<u8> {
209 &self.data
210 }
211 }
212
213 pub struct H4SnoopPacket {
214 pub preamble: SnoopPacketPreamble,
215 pub data: Vec<u8>,
216 pub pkt_type: u8,
217 }
218
219 impl GeneralSnoopPacket for H4SnoopPacket {
adapter_index(&self) -> u16220 fn adapter_index(&self) -> u16 {
221 0
222 }
opcode(&self) -> SnoopOpcodes223 fn opcode(&self) -> SnoopOpcodes {
224 match self.pkt_type {
225 0x01 => SnoopOpcodes::Command,
226 0x02 => match self.preamble.flags & 0x01 {
227 0x00 => SnoopOpcodes::AclTxPacket,
228 _ => SnoopOpcodes::AclRxPacket,
229 },
230 0x03 => match self.preamble.flags & 0x01 {
231 0x00 => SnoopOpcodes::ScoTxPacket,
232 _ => SnoopOpcodes::ScoRxPacket,
233 },
234 0x04 => SnoopOpcodes::Event,
235 0x05 => match self.preamble.flags & 0x01 {
236 0x00 => SnoopOpcodes::IsoTx,
237 _ => SnoopOpcodes::IsoRx,
238 },
239 _ => SnoopOpcodes::Invalid,
240 }
241 }
preamble(&self) -> &SnoopPacketPreamble242 fn preamble(&self) -> &SnoopPacketPreamble {
243 &self.preamble
244 }
data(&self) -> &Vec<u8>245 fn data(&self) -> &Vec<u8> {
246 &self.data
247 }
248 }
249
250 /// Maximum packet size for snoop is the max ACL size + 4 bytes.
251 const SNOOP_MAX_PACKET_SIZE: usize = 1486 + 4;
252
253 /// Reader for Linux snoop files.
254 pub struct LinuxSnoopReader<'a> {
255 fd: Box<dyn BufRead + 'a>,
256 }
257
258 impl<'a> LinuxSnoopReader<'a> {
new(fd: Box<dyn BufRead + 'a>) -> Self259 fn new(fd: Box<dyn BufRead + 'a>) -> Self {
260 LinuxSnoopReader { fd }
261 }
262 }
263
264 impl<'a> Iterator for LinuxSnoopReader<'a> {
265 type Item = Box<dyn GeneralSnoopPacket>;
266
next(&mut self) -> Option<Self::Item>267 fn next(&mut self) -> Option<Self::Item> {
268 let preamble = match SnoopPacketPreamble::from_fd(&mut self.fd) {
269 Some(preamble) => preamble,
270 None => {
271 return None;
272 }
273 };
274
275 if preamble.included_length > 0 {
276 let size: usize = (preamble.included_length).try_into().unwrap();
277 let mut rem_data = [0u8; SNOOP_MAX_PACKET_SIZE];
278
279 match self.fd.read_exact(&mut rem_data[0..size]) {
280 Ok(()) => {
281 Some(Box::new(LinuxSnoopPacket { preamble, data: rem_data[0..size].to_vec() }))
282 }
283 Err(e) => {
284 eprintln!("Couldn't read any packet data: {}", e);
285 None
286 }
287 }
288 } else {
289 Some(Box::new(LinuxSnoopPacket { preamble, data: vec![] }))
290 }
291 }
292 }
293
294 /// Reader for H4/UART/Android snoop files.
295 pub struct H4SnoopReader<'a> {
296 fd: Box<dyn BufRead + 'a>,
297 }
298
299 impl<'a> H4SnoopReader<'a> {
new(fd: Box<dyn BufRead + 'a>) -> Self300 fn new(fd: Box<dyn BufRead + 'a>) -> Self {
301 H4SnoopReader { fd }
302 }
303 }
304
305 impl<'a> Iterator for H4SnoopReader<'a> {
306 type Item = Box<dyn GeneralSnoopPacket>;
307
next(&mut self) -> Option<Self::Item>308 fn next(&mut self) -> Option<Self::Item> {
309 let preamble = match SnoopPacketPreamble::from_fd(&mut self.fd) {
310 Some(preamble) => preamble,
311 None => {
312 return None;
313 }
314 };
315
316 if preamble.included_length > 0 {
317 let size: usize = (preamble.included_length - 1).try_into().unwrap();
318 let mut type_buf = [0u8; 1];
319 match self.fd.read_exact(&mut type_buf) {
320 Ok(()) => {}
321 Err(e) => {
322 eprintln!("Couldn't read any packet data: {}", e);
323 return None;
324 }
325 };
326
327 let mut rem_data = [0u8; SNOOP_MAX_PACKET_SIZE];
328 match self.fd.read_exact(&mut rem_data[0..size]) {
329 Ok(()) => Some(Box::new(H4SnoopPacket {
330 preamble,
331 data: rem_data[0..size].to_vec(),
332 pkt_type: type_buf[0],
333 })),
334 Err(e) => {
335 eprintln!("Couldn't read any packet data: {}", e);
336 None
337 }
338 }
339 } else {
340 eprintln!("Non-positive packet size: {}", preamble.included_length);
341 None
342 }
343 }
344 }
345
346 pub struct LogParser {
347 fd: Box<dyn BufRead>,
348 log_type: SnoopDatalinkType,
349 }
350
351 impl<'a> LogParser {
new(filepath: &str) -> std::io::Result<Self>352 pub fn new(filepath: &str) -> std::io::Result<Self> {
353 let mut fd: Box<dyn BufRead>;
354 if filepath.len() == 0 {
355 fd = Box::new(BufReader::new(std::io::stdin()));
356 } else {
357 fd = Box::new(BufReader::new(File::open(filepath)?));
358 }
359
360 let mut buf = [0; SNOOP_HEADER_SIZE];
361 fd.read_exact(&mut buf)?;
362
363 match SnoopHeader::try_from(&buf[0..SNOOP_HEADER_SIZE]) {
364 Ok(header) => Ok(Self { fd, log_type: header.datalink_type }),
365 Err(e) => Err(Error::new(ErrorKind::Other, e)),
366 }
367 }
368
get_snoop_iterator(self) -> Box<dyn Iterator<Item = Box<dyn GeneralSnoopPacket>>>369 pub fn get_snoop_iterator(self) -> Box<dyn Iterator<Item = Box<dyn GeneralSnoopPacket>>> {
370 let reader = Box::new(BufReader::new(self.fd));
371 match self.log_type {
372 SnoopDatalinkType::H4Uart => Box::new(H4SnoopReader::new(reader)),
373 SnoopDatalinkType::LinuxMonitor => Box::new(LinuxSnoopReader::new(reader)),
374 }
375 }
376 }
377
378 /// Data owned by a packet.
379 #[derive(Debug, Clone)]
380 pub enum PacketChild {
381 HciCommand(Command),
382 HciEvent(Event),
383 AclTx(Acl),
384 AclRx(Acl),
385 NewIndex(NewIndex),
386 SystemNote(String),
387 }
388
389 impl<'a> TryFrom<&'a dyn GeneralSnoopPacket> for PacketChild {
390 type Error = String;
391
try_from(item: &'a dyn GeneralSnoopPacket) -> Result<Self, Self::Error>392 fn try_from(item: &'a dyn GeneralSnoopPacket) -> Result<Self, Self::Error> {
393 match item.opcode() {
394 SnoopOpcodes::Command => match Command::parse(item.data().as_slice()) {
395 Ok(command) => Ok(PacketChild::HciCommand(command)),
396 Err(e) => Err(format!("Couldn't parse command: {:?}", e)),
397 },
398
399 SnoopOpcodes::Event => match Event::parse(item.data().as_slice()) {
400 Ok(event) => Ok(PacketChild::HciEvent(event)),
401 Err(e) => Err(format!("Couldn't parse event: {:?}", e)),
402 },
403
404 SnoopOpcodes::AclTxPacket => match Acl::parse(item.data().as_slice()) {
405 Ok(data) => Ok(PacketChild::AclTx(data)),
406 Err(e) => Err(format!("Couldn't parse acl tx: {:?}", e)),
407 },
408
409 SnoopOpcodes::AclRxPacket => match Acl::parse(item.data().as_slice()) {
410 Ok(data) => Ok(PacketChild::AclRx(data)),
411 Err(e) => Err(format!("Couldn't parse acl rx: {:?}", e)),
412 },
413
414 SnoopOpcodes::NewIndex => match NewIndex::parse(item.data().as_slice()) {
415 Ok(data) => Ok(PacketChild::NewIndex(data)),
416 Err(e) => Err(format!("Couldn't parse new index: {:?}", e)),
417 },
418
419 SnoopOpcodes::SystemNote => match String::from_utf8(item.data().to_vec()) {
420 Ok(data) => Ok(PacketChild::SystemNote(data)),
421 Err(e) => Err(format!("Couldn't parse system note: {:?}", e)),
422 },
423
424 // TODO(b/262928525) - Add packet handlers for more packet types.
425 _ => Err(format!("Unhandled packet opcode: {:?}", item.opcode())),
426 }
427 }
428 }
429
430 /// A single processable packet of data.
431 #[derive(Debug, Clone)]
432 #[allow(dead_code)]
433 pub struct Packet {
434 /// Timestamp of this packet
435 pub ts: NaiveDateTime,
436
437 /// Which adapter this packet is for. Unassociated packets should use 0xFFFE.
438 pub adapter_index: u16,
439
440 /// Packet number in current stream.
441 pub index: usize,
442
443 /// Inner data for this packet.
444 pub inner: PacketChild,
445 }
446
447 impl<'a> TryFrom<(usize, &'a dyn GeneralSnoopPacket)> for Packet {
448 type Error = String;
449
try_from(item: (usize, &'a dyn GeneralSnoopPacket)) -> Result<Self, Self::Error>450 fn try_from(item: (usize, &'a dyn GeneralSnoopPacket)) -> Result<Self, Self::Error> {
451 let (index, packet) = item;
452 match PacketChild::try_from(packet) {
453 Ok(inner) => {
454 let ts = packet.get_timestamp().ok_or(format!(
455 "timestamp conversion error: {}",
456 packet.preamble().timestamp_us
457 ))?;
458 let adapter_index = packet.adapter_index();
459 Ok(Packet { ts, adapter_index, index, inner })
460 }
461
462 Err(e) => Err(e),
463 }
464 }
465 }
466
467 #[allow(dead_code)]
468 pub enum AclContent {
469 Control(Control),
470 LeControl(LeControl),
471 ConnectionlessData(u16, Vec<u8>),
472 StandardData(Vec<u8>),
473 None,
474 }
475
get_acl_content(acl: &Acl) -> AclContent476 pub fn get_acl_content(acl: &Acl) -> AclContent {
477 match acl.specialize() {
478 AclChild::Payload(bytes) => match BasicFrame::parse(bytes.as_ref()) {
479 Ok(bf) => match bf.specialize() {
480 BasicFrameChild::ControlFrame(cf) => match cf.specialize() {
481 ControlFrameChild::Payload(p) => match Control::parse(p.as_ref()) {
482 Ok(control) => AclContent::Control(control),
483 Err(_) => AclContent::None,
484 },
485 _ => AclContent::None,
486 },
487 BasicFrameChild::LeControlFrame(lcf) => match lcf.specialize() {
488 LeControlFrameChild::Payload(p) => match LeControl::parse(p.as_ref()) {
489 Ok(le_control) => AclContent::LeControl(le_control),
490 Err(_) => AclContent::None,
491 },
492 _ => AclContent::None,
493 },
494 BasicFrameChild::GroupFrame(gf) => match gf.specialize() {
495 GroupFrameChild::Payload(p) => {
496 AclContent::ConnectionlessData(gf.get_psm(), p.to_vec())
497 }
498 _ => AclContent::None,
499 },
500 BasicFrameChild::Payload(p) => AclContent::StandardData(p.to_vec()),
501 _ => AclContent::None,
502 },
503 Err(_) => AclContent::None,
504 },
505 _ => AclContent::None,
506 }
507 }
508
509 #[derive(Clone, Debug)]
510 pub struct NewIndex {
511 _hci_type: u8,
512 _bus: u8,
513 bdaddr: [u8; 6],
514 _name: [u8; 8],
515 }
516
517 impl NewIndex {
parse(data: &[u8]) -> Result<NewIndex, std::string::String>518 fn parse(data: &[u8]) -> Result<NewIndex, std::string::String> {
519 if data.len() != std::mem::size_of::<NewIndex>() {
520 return Err(format!("Invalid size for New Index packet: {}", data.len()));
521 }
522
523 let rest = data;
524 let (hci_type, rest) = rest.split_at(std::mem::size_of::<u8>());
525 let (bus, rest) = rest.split_at(std::mem::size_of::<u8>());
526 let (bdaddr, rest) = rest.split_at(6 * std::mem::size_of::<u8>());
527 let (name, _rest) = rest.split_at(8 * std::mem::size_of::<u8>());
528
529 Ok(NewIndex {
530 _hci_type: hci_type[0],
531 _bus: bus[0],
532 bdaddr: bdaddr.try_into().unwrap(),
533 _name: name.try_into().unwrap(),
534 })
535 }
536
get_addr_str(&self) -> String537 pub fn get_addr_str(&self) -> String {
538 String::from(format!(
539 "[{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}]",
540 self.bdaddr[0],
541 self.bdaddr[1],
542 self.bdaddr[2],
543 self.bdaddr[3],
544 self.bdaddr[4],
545 self.bdaddr[5]
546 ))
547 }
548 }
549