diff options
author | 2024-08-15 03:26:10 +0000 | |
---|---|---|
committer | 2024-08-15 03:26:10 +0000 | |
commit | 2886d60f50c8ddfe3dbaafcb93b53d033fc32654 (patch) | |
tree | affa88e26aa4572a44c226e3561d6f8b633a1c1b /floss | |
parent | 8ba42e6bbb0fa94edab18e61c0ecbd7e7a54c7d7 (diff) | |
parent | 9d12639d7830038848815c6932ceed788d636b89 (diff) |
Merge changes I05da6242,I272f8f04 into main
* changes:
Floss: Hcidoc: Support AOSP snoop log
Floss: Hcidoc: Refactor hcidoc parser to support other snoop types
Diffstat (limited to 'floss')
-rw-r--r-- | floss/hcidoc/src/main.rs | 46 | ||||
-rw-r--r-- | floss/hcidoc/src/parser.rs | 384 |
2 files changed, 251 insertions, 179 deletions
diff --git a/floss/hcidoc/src/main.rs b/floss/hcidoc/src/main.rs index 82ded2701c..4589c3ba18 100644 --- a/floss/hcidoc/src/main.rs +++ b/floss/hcidoc/src/main.rs @@ -7,7 +7,7 @@ mod parser; use crate::engine::RuleEngine; use crate::groups::{collisions, connections, controllers, informational}; -use crate::parser::{LinuxSnoopOpcodes, LogParser, LogType, Packet}; +use crate::parser::{LogParser, Packet, SnoopOpcodes}; fn main() { let matches = Command::new("hcidoc") @@ -63,7 +63,7 @@ fn main() { report_signals = true; } - let mut parser = match LogParser::new(filename) { + let parser = match LogParser::new(filename) { Ok(p) => p, Err(e) => { println!( @@ -75,14 +75,6 @@ fn main() { } }; - let log_type = match parser.read_log_type() { - Ok(v) => v, - Err(e) => { - println!("Parsing {} failed: {}", filename, e); - return; - } - }; - // Create engine with default rule groups. let mut engine = RuleEngine::new(); engine.add_rule_group("Collisions".into(), collisions::get_collisions_group()); @@ -93,29 +85,27 @@ fn main() { // Decide where to write output. let mut writer: Box<dyn Write> = Box::new(std::io::stdout()); - if let LogType::LinuxSnoop(_header) = log_type { - for (pos, v) in parser.get_snoop_iterator().expect("Not a linux snoop file").enumerate() { - match Packet::try_from((pos, &v)) { - Ok(p) => engine.process(p), - Err(e) => { - if !ignore_unknown_opcode { - match v.opcode() { - LinuxSnoopOpcodes::Command | LinuxSnoopOpcodes::Event => { - eprintln!("#{}: {}", pos, e); - } - _ => (), + for (pos, v) in parser.get_snoop_iterator().enumerate() { + match Packet::try_from((pos, &*v)) { + Ok(p) => engine.process(p), + Err(e) => { + if !ignore_unknown_opcode { + match v.opcode() { + SnoopOpcodes::Command | SnoopOpcodes::Event => { + eprintln!("#{}: {}", pos, e); } + _ => (), } } } } + } - if !report_only_signals { - engine.report(&mut writer); - } - if report_signals { - let _ = writeln!(&mut writer, "### Signals ###"); - engine.report_signals(&mut writer); - } + if !report_only_signals { + engine.report(&mut writer); + } + if report_signals { + let _ = writeln!(&mut writer, "### Signals ###"); + engine.report_signals(&mut writer); } } diff --git a/floss/hcidoc/src/parser.rs b/floss/hcidoc/src/parser.rs index 4bba3aade3..26659bc424 100644 --- a/floss/hcidoc/src/parser.rs +++ b/floss/hcidoc/src/parser.rs @@ -1,5 +1,5 @@ //! Parsing of various Bluetooth packets. -use chrono::NaiveDateTime; +use chrono::{DateTime, NaiveDateTime}; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::cast::FromPrimitive; use std::convert::TryFrom; @@ -12,29 +12,32 @@ use hcidoc_packets::l2cap::{ LeControlFrameChild, }; -/// Linux snoop file header format. This format is used by `btmon` on Linux systems that have bluez -/// installed. -#[derive(Clone, Copy, Debug)] -pub struct LinuxSnoopHeader { - id: [u8; 8], - version: u32, - data_type: u32, +/// Snoop file header format. +#[derive(Debug)] +pub struct SnoopHeader { + _id: [u8; 8], + _version: u32, + datalink_type: SnoopDatalinkType, } -/// Identifier for a Linux snoop file. In ASCII, this is 'btsnoop\0'. -const LINUX_SNOOP_MAGIC: [u8; 8] = [0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00]; +/// Identifier for a snoop file. In ASCII, this is 'btsnoop\0'. +const SNOOP_MAGIC: [u8; 8] = [0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00]; -/// Snoop files in monitor format will have this value in link type. -const LINUX_SNOOP_MONITOR_TYPE: u32 = 2001; +/// Size of snoop header. 8 bytes for magic, 4 bytes for version, and 4 bytes for snoop type. +const SNOOP_HEADER_SIZE: usize = 16; -/// Size of snoop header. 8 bytes for magic and another 8 for additional info. -const LINUX_SNOOP_HEADER_SIZE: usize = 16; +#[derive(Debug, FromPrimitive, ToPrimitive)] +#[repr(u32)] +enum SnoopDatalinkType { + H4Uart = 1002, + LinuxMonitor = 2001, +} -impl TryFrom<&[u8]> for LinuxSnoopHeader { +impl TryFrom<&[u8]> for SnoopHeader { type Error = String; fn try_from(item: &[u8]) -> Result<Self, Self::Error> { - if item.len() != LINUX_SNOOP_HEADER_SIZE { + if item.len() != SNOOP_HEADER_SIZE { return Err(format!("Invalid size for snoop header: {}", item.len())); } @@ -43,35 +46,31 @@ impl TryFrom<&[u8]> for LinuxSnoopHeader { let (version_bytes, rest) = rest.split_at(std::mem::size_of::<u32>()); let (data_type_bytes, _rest) = rest.split_at(std::mem::size_of::<u32>()); - let header = LinuxSnoopHeader { - id: id_bytes.try_into().unwrap(), - version: u32::from_be_bytes(version_bytes.try_into().unwrap()), - data_type: u32::from_be_bytes(data_type_bytes.try_into().unwrap()), - }; + let id = id_bytes.try_into().unwrap(); + let version = u32::from_be_bytes(version_bytes.try_into().unwrap()); + let data_type = u32::from_be_bytes(data_type_bytes.try_into().unwrap()); - if header.id != LINUX_SNOOP_MAGIC { + if id != SNOOP_MAGIC { return Err(format!("Id is not 'btsnoop'.")); } - if header.version != 1 { - return Err(format!("Version is not supported. Got {}.", header.version)); + if version != 1 { + return Err(format!("Version is not supported. Got {}.", version)); } - if header.data_type != LINUX_SNOOP_MONITOR_TYPE { - return Err(format!( - "Invalid data type in snoop file. We want monitor type ({}) but got {}", - LINUX_SNOOP_MONITOR_TYPE, header.data_type - )); - } + let datalink_type = match SnoopDatalinkType::from_u32(data_type) { + Some(datalink_type) => datalink_type, + None => return Err(format!("Unsupported datalink type {}", data_type)), + }; - Ok(header) + return Ok(SnoopHeader { _id: id, _version: version, datalink_type }); } } -/// Opcodes for Linux snoop packets. +/// Opcodes for snoop packets. #[derive(Debug, FromPrimitive, ToPrimitive)] #[repr(u16)] -pub enum LinuxSnoopOpcodes { +pub enum SnoopOpcodes { NewIndex = 0, DeleteIndex, Command, @@ -96,9 +95,15 @@ pub enum LinuxSnoopOpcodes { Invalid = 0xffff, } -/// Linux snoop file packet format. +/// Size of packet preamble (everything except the data). +const SNOOP_PACKET_PREAMBLE_SIZE: usize = 24; + +/// Number of microseconds from btsnoop zero to Linux epoch. +const SNOOP_Y0_TO_Y1970_US: i64 = 62_168_256_000_000_000; + +/// Snoop file packet format. #[derive(Debug, Clone)] -pub struct LinuxSnoopPacket { +pub struct SnoopPacketPreamble { /// The original length of the captured packet as received via a network. pub original_length: u32, @@ -107,47 +112,39 @@ pub struct LinuxSnoopPacket { pub included_length: u32, pub flags: u32, pub drops: u32, - pub timestamp_magic_us: u64, - pub data: Vec<u8>, + pub timestamp_us: u64, } -impl LinuxSnoopPacket { - pub fn adapter_index(&self) -> u16 { - (self.flags >> 16).try_into().unwrap_or(0u16) - } +impl SnoopPacketPreamble { + fn from_fd<'a>(fd: &mut Box<dyn BufRead + 'a>) -> Option<SnoopPacketPreamble> { + let mut buf = [0u8; SNOOP_PACKET_PREAMBLE_SIZE]; + match fd.read_exact(&mut buf) { + Ok(()) => {} + Err(e) => { + // |UnexpectedEof| could be seen since we're trying to read more + // data than is available (i.e. end of file). + if e.kind() != ErrorKind::UnexpectedEof { + eprintln!("Error reading preamble: {:?}", e); + } + return None; + } + }; - pub fn opcode(&self) -> LinuxSnoopOpcodes { - LinuxSnoopOpcodes::from_u32(self.flags & 0xffff).unwrap_or(LinuxSnoopOpcodes::Invalid) + match SnoopPacketPreamble::try_from(&buf[0..SNOOP_PACKET_PREAMBLE_SIZE]) { + Ok(preamble) => Some(preamble), + Err(e) => { + eprintln!("Error reading preamble: {}", e); + None + } + } } } -/// Size of packet preamble (everything except the data). -const LINUX_SNOOP_PACKET_PREAMBLE_SIZE: usize = 24; - -/// Maximum packet size for snoop is the max ACL size + 4 bytes. -const LINUX_SNOOP_MAX_PACKET_SIZE: usize = 1486 + 4; - -/// Number of seconds from the year 1970 to the year 2000. -const LINUX_SNOOP_Y2K_OFFSET_IN_SECS: i64 = 946684800i64; - -/// Snoop timestamps start at year 0 instead of 1970 like unix timestamps. This -/// offset is used to represent Jan 1, 2000 AD and can be used to convert back -/// to unixtime. -const LINUX_SNOOP_Y2K_EPOCH_USECS: i64 = 0x00E03AB44A676000i64; - -/// Microseconds to seconds. -const USECS_TO_SECS: i64 = 1_000_000i64; - -/// Offset from the snoop timestamp to unixtimestamp in seconds. This is a negative number. -const LINUX_SNOOP_OFFSET_TO_UNIXTIME_SECS: i64 = - LINUX_SNOOP_Y2K_OFFSET_IN_SECS - (LINUX_SNOOP_Y2K_EPOCH_USECS / USECS_TO_SECS); - -// Expect specifically the pre-amble to be read here (and no data). -impl TryFrom<&[u8]> for LinuxSnoopPacket { +impl TryFrom<&[u8]> for SnoopPacketPreamble { type Error = String; fn try_from(item: &[u8]) -> Result<Self, Self::Error> { - if item.len() != LINUX_SNOOP_PACKET_PREAMBLE_SIZE { + if item.len() != SNOOP_PACKET_PREAMBLE_SIZE { return Err(format!("Wrong size for snoop packet preamble: {}", item.len())); } @@ -159,19 +156,91 @@ impl TryFrom<&[u8]> for LinuxSnoopPacket { let (ts_bytes, _rest) = rest.split_at(std::mem::size_of::<u64>()); // Note that all bytes are in big-endian because they're network order. - let packet = LinuxSnoopPacket { + let preamble = SnoopPacketPreamble { original_length: u32::from_be_bytes(orig_len_bytes.try_into().unwrap()), included_length: u32::from_be_bytes(included_len_bytes.try_into().unwrap()), flags: u32::from_be_bytes(flags_bytes.try_into().unwrap()), drops: u32::from_be_bytes(drops_bytes.try_into().unwrap()), - timestamp_magic_us: u64::from_be_bytes(ts_bytes.try_into().unwrap()), - data: vec![], + timestamp_us: u64::from_be_bytes(ts_bytes.try_into().unwrap()), }; - Ok(packet) + Ok(preamble) } } +pub trait GeneralSnoopPacket { + fn adapter_index(&self) -> u16; + fn opcode(&self) -> SnoopOpcodes; + fn preamble(&self) -> &SnoopPacketPreamble; + fn data(&self) -> &Vec<u8>; + + fn get_timestamp(&self) -> Option<NaiveDateTime> { + let preamble = self.preamble(); + let ts_i64 = i64::try_from(preamble.timestamp_us).unwrap_or(i64::MAX); + DateTime::from_timestamp_micros(ts_i64 - SNOOP_Y0_TO_Y1970_US).map(|date| date.naive_utc()) + } +} + +pub struct LinuxSnoopPacket { + pub preamble: SnoopPacketPreamble, + pub data: Vec<u8>, +} + +impl GeneralSnoopPacket for LinuxSnoopPacket { + fn adapter_index(&self) -> u16 { + (self.preamble.flags >> 16).try_into().unwrap_or(0u16) + } + fn opcode(&self) -> SnoopOpcodes { + SnoopOpcodes::from_u32(self.preamble.flags & 0xffff).unwrap_or(SnoopOpcodes::Invalid) + } + fn preamble(&self) -> &SnoopPacketPreamble { + &self.preamble + } + fn data(&self) -> &Vec<u8> { + &self.data + } +} + +pub struct H4SnoopPacket { + pub preamble: SnoopPacketPreamble, + pub data: Vec<u8>, + pub pkt_type: u8, +} + +impl GeneralSnoopPacket for H4SnoopPacket { + fn adapter_index(&self) -> u16 { + 0 + } + fn opcode(&self) -> SnoopOpcodes { + match self.pkt_type { + 0x01 => SnoopOpcodes::Command, + 0x02 => match self.preamble.flags & 0x01 { + 0x00 => SnoopOpcodes::AclTxPacket, + _ => SnoopOpcodes::AclRxPacket, + }, + 0x03 => match self.preamble.flags & 0x01 { + 0x00 => SnoopOpcodes::ScoTxPacket, + _ => SnoopOpcodes::ScoRxPacket, + }, + 0x04 => SnoopOpcodes::Event, + 0x05 => match self.preamble.flags & 0x01 { + 0x00 => SnoopOpcodes::IsoTx, + _ => SnoopOpcodes::IsoRx, + }, + _ => SnoopOpcodes::Invalid, + } + } + fn preamble(&self) -> &SnoopPacketPreamble { + &self.preamble + } + fn data(&self) -> &Vec<u8> { + &self.data + } +} + +/// Maximum packet size for snoop is the max ACL size + 4 bytes. +const SNOOP_MAX_PACKET_SIZE: usize = 1486 + 4; + /// Reader for Linux snoop files. pub struct LinuxSnoopReader<'a> { fd: Box<dyn BufRead + 'a>, @@ -184,99 +253,116 @@ impl<'a> LinuxSnoopReader<'a> { } impl<'a> Iterator for LinuxSnoopReader<'a> { - type Item = LinuxSnoopPacket; + type Item = Box<dyn GeneralSnoopPacket>; fn next(&mut self) -> Option<Self::Item> { - let mut data = [0u8; LINUX_SNOOP_PACKET_PREAMBLE_SIZE]; - match self.fd.read_exact(&mut data) { - Ok(()) => {} - Err(e) => { - // |UnexpectedEof| could be seen since we're trying to read more - // data than is available (i.e. end of file). - if e.kind() != ErrorKind::UnexpectedEof { - eprintln!("Error reading snoop file: {:?}", e); - } + let preamble = match SnoopPacketPreamble::from_fd(&mut self.fd) { + Some(preamble) => preamble, + None => { return None; } }; - match LinuxSnoopPacket::try_from(&data[0..LINUX_SNOOP_PACKET_PREAMBLE_SIZE]) { - Ok(mut p) => { - if p.included_length > 0 { - let size: usize = p.included_length.try_into().unwrap(); - let mut rem_data = [0u8; LINUX_SNOOP_MAX_PACKET_SIZE]; - match self.fd.read_exact(&mut rem_data[0..size]) { - Ok(()) => { - p.data = rem_data[0..size].to_vec(); - Some(p) - } - Err(e) => { - eprintln!("Couldn't read any packet data: {}", e); - None - } - } - } else { - Some(p) + if preamble.included_length > 0 { + let size: usize = (preamble.included_length).try_into().unwrap(); + let mut rem_data = [0u8; SNOOP_MAX_PACKET_SIZE]; + + match self.fd.read_exact(&mut rem_data[0..size]) { + Ok(()) => { + Some(Box::new(LinuxSnoopPacket { preamble, data: rem_data[0..size].to_vec() })) + } + Err(e) => { + eprintln!("Couldn't read any packet data: {}", e); + None } } - Err(_) => None, + } else { + Some(Box::new(LinuxSnoopPacket { preamble, data: vec![] })) } } } -/// What kind of log file is this? -#[derive(Clone, Debug)] -pub enum LogType { - /// Linux snoop file generated by something like `btmon`. - LinuxSnoop(LinuxSnoopHeader), +/// Reader for H4/UART/Android snoop files. +pub struct H4SnoopReader<'a> { + fd: Box<dyn BufRead + 'a>, +} + +impl<'a> H4SnoopReader<'a> { + fn new(fd: Box<dyn BufRead + 'a>) -> Self { + H4SnoopReader { fd } + } +} + +impl<'a> Iterator for H4SnoopReader<'a> { + type Item = Box<dyn GeneralSnoopPacket>; + + fn next(&mut self) -> Option<Self::Item> { + let preamble = match SnoopPacketPreamble::from_fd(&mut self.fd) { + Some(preamble) => preamble, + None => { + return None; + } + }; + + if preamble.included_length > 0 { + let size: usize = (preamble.included_length - 1).try_into().unwrap(); + let mut type_buf = [0u8; 1]; + match self.fd.read_exact(&mut type_buf) { + Ok(()) => {} + Err(e) => { + eprintln!("Couldn't read any packet data: {}", e); + return None; + } + }; + + let mut rem_data = [0u8; SNOOP_MAX_PACKET_SIZE]; + match self.fd.read_exact(&mut rem_data[0..size]) { + Ok(()) => Some(Box::new(H4SnoopPacket { + preamble, + data: rem_data[0..size].to_vec(), + pkt_type: type_buf[0], + })), + Err(e) => { + eprintln!("Couldn't read any packet data: {}", e); + None + } + } + } else { + eprintln!("Non-positive packet size: {}", preamble.included_length); + None + } + } } -/// Parses different Bluetooth log types. pub struct LogParser { fd: Box<dyn BufRead>, - log_type: Option<LogType>, + log_type: SnoopDatalinkType, } impl<'a> LogParser { pub fn new(filepath: &str) -> std::io::Result<Self> { - let fd: Box<dyn BufRead>; + let mut fd: Box<dyn BufRead>; if filepath.len() == 0 { fd = Box::new(BufReader::new(std::io::stdin())); } else { fd = Box::new(BufReader::new(File::open(filepath)?)); } - Ok(Self { fd, log_type: None }) - } - - /// Check the log file type for the current log file. This advances the read pointer. - /// For a non-intrusive query, use |get_log_type|. - pub fn read_log_type(&mut self) -> std::io::Result<LogType> { - let mut buf = [0; LINUX_SNOOP_HEADER_SIZE]; - - self.fd.read_exact(&mut buf)?; + let mut buf = [0; SNOOP_HEADER_SIZE]; + fd.read_exact(&mut buf)?; - if let Ok(header) = LinuxSnoopHeader::try_from(&buf[0..LINUX_SNOOP_HEADER_SIZE]) { - let log_type = LogType::LinuxSnoop(header); - self.log_type = Some(log_type.clone()); - Ok(log_type) - } else { - Err(Error::new(ErrorKind::Other, "Unsupported log file type")) + match SnoopHeader::try_from(&buf[0..SNOOP_HEADER_SIZE]) { + Ok(header) => Ok(Self { fd, log_type: header.datalink_type }), + Err(e) => Err(Error::new(ErrorKind::Other, e)), } } - /// Get cached log type. To initially read the log type, use |read_log_type|. - pub fn get_log_type(&self) -> Option<LogType> { - self.log_type.clone() - } - - pub fn get_snoop_iterator(&mut self) -> Option<LinuxSnoopReader> { - // Limit to LinuxSnoop files. - if !matches!(self.get_log_type()?, LogType::LinuxSnoop(_)) { - return None; + pub fn get_snoop_iterator(self) -> Box<dyn Iterator<Item = Box<dyn GeneralSnoopPacket>>> { + let reader = Box::new(BufReader::new(self.fd)); + match self.log_type { + SnoopDatalinkType::H4Uart => Box::new(H4SnoopReader::new(reader)), + SnoopDatalinkType::LinuxMonitor => Box::new(LinuxSnoopReader::new(reader)), } - - Some(LinuxSnoopReader::new(Box::new(BufReader::new(&mut self.fd)))) } } @@ -291,37 +377,37 @@ pub enum PacketChild { SystemNote(String), } -impl<'a> TryFrom<&'a LinuxSnoopPacket> for PacketChild { +impl<'a> TryFrom<&'a dyn GeneralSnoopPacket> for PacketChild { type Error = String; - fn try_from(item: &'a LinuxSnoopPacket) -> Result<Self, Self::Error> { + fn try_from(item: &'a dyn GeneralSnoopPacket) -> Result<Self, Self::Error> { match item.opcode() { - LinuxSnoopOpcodes::Command => match Command::parse(item.data.as_slice()) { + SnoopOpcodes::Command => match Command::parse(item.data().as_slice()) { Ok(command) => Ok(PacketChild::HciCommand(command)), Err(e) => Err(format!("Couldn't parse command: {:?}", e)), }, - LinuxSnoopOpcodes::Event => match Event::parse(item.data.as_slice()) { + SnoopOpcodes::Event => match Event::parse(item.data().as_slice()) { Ok(event) => Ok(PacketChild::HciEvent(event)), Err(e) => Err(format!("Couldn't parse event: {:?}", e)), }, - LinuxSnoopOpcodes::AclTxPacket => match Acl::parse(item.data.as_slice()) { + SnoopOpcodes::AclTxPacket => match Acl::parse(item.data().as_slice()) { Ok(data) => Ok(PacketChild::AclTx(data)), Err(e) => Err(format!("Couldn't parse acl tx: {:?}", e)), }, - LinuxSnoopOpcodes::AclRxPacket => match Acl::parse(item.data.as_slice()) { + SnoopOpcodes::AclRxPacket => match Acl::parse(item.data().as_slice()) { Ok(data) => Ok(PacketChild::AclRx(data)), Err(e) => Err(format!("Couldn't parse acl rx: {:?}", e)), }, - LinuxSnoopOpcodes::NewIndex => match NewIndex::parse(item.data.as_slice()) { + SnoopOpcodes::NewIndex => match NewIndex::parse(item.data().as_slice()) { Ok(data) => Ok(PacketChild::NewIndex(data)), Err(e) => Err(format!("Couldn't parse new index: {:?}", e)), }, - LinuxSnoopOpcodes::SystemNote => match String::from_utf8(item.data.to_vec()) { + SnoopOpcodes::SystemNote => match String::from_utf8(item.data().to_vec()) { Ok(data) => Ok(PacketChild::SystemNote(data)), Err(e) => Err(format!("Couldn't parse system note: {:?}", e)), }, @@ -348,22 +434,18 @@ pub struct Packet { pub inner: PacketChild, } -impl<'a> TryFrom<(usize, &'a LinuxSnoopPacket)> for Packet { +impl<'a> TryFrom<(usize, &'a dyn GeneralSnoopPacket)> for Packet { type Error = String; - fn try_from(item: (usize, &'a LinuxSnoopPacket)) -> Result<Self, Self::Error> { + fn try_from(item: (usize, &'a dyn GeneralSnoopPacket)) -> Result<Self, Self::Error> { let (index, packet) = item; match PacketChild::try_from(packet) { Ok(inner) => { - let base_ts = i64::try_from(packet.timestamp_magic_us) - .map_err(|e| format!("u64 conversion error: {}", e))?; - - let ts_secs = (base_ts / USECS_TO_SECS) + LINUX_SNOOP_OFFSET_TO_UNIXTIME_SECS; - let ts_nsecs = u32::try_from((base_ts % USECS_TO_SECS) * 1000).unwrap_or(0); - let ts = NaiveDateTime::from_timestamp_opt(ts_secs, ts_nsecs) - .ok_or(format!("timestamp conversion error: {}", base_ts))?; + let ts = packet.get_timestamp().ok_or(format!( + "timestamp conversion error: {}", + packet.preamble().timestamp_us + ))?; let adapter_index = packet.adapter_index(); - Ok(Packet { ts, adapter_index, index, inner }) } |