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