From eed983daaeee004e98423d862efc13676197bd06 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 15 Dec 2023 13:18:48 -0800 Subject: [PATCH 1/6] diag_device: rm 0 byte read warning --- src/diag_device.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/diag_device.rs b/src/diag_device.rs index ff42b8f..3fab969 100644 --- a/src/diag_device.rs +++ b/src/diag_device.rs @@ -69,9 +69,7 @@ impl DiagReader for DiagDevice { let mut bytes_read; loop { bytes_read = self.file.read(&mut self.read_buf)?; - if bytes_read == 0 { - println!("read 0 bytes from /dev/diag, retrying..."); - } else { + if bytes_read > 0 { break; } } From 5128f468cfb0539376c920de612202d106a3bfd2 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Wed, 20 Dec 2023 17:03:46 -0800 Subject: [PATCH 2/6] diag_reader: move error handling outside of trait --- src/bin/wavehunter_reader.rs | 23 ++++++++++++++++------- src/debug_file.rs | 10 ++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/bin/wavehunter_reader.rs b/src/bin/wavehunter_reader.rs index 3be7350..db9792c 100644 --- a/src/bin/wavehunter_reader.rs +++ b/src/bin/wavehunter_reader.rs @@ -1,6 +1,6 @@ use wavehunter::debug_file::DebugFileReader; use wavehunter::diag_reader::DiagReader; -use wavehunter::diag_device::DiagResult; +use wavehunter::diag_device::{DiagResult, DiagDeviceError}; use wavehunter::gsmtap_parser::GsmtapParser; use wavehunter::pcap::PcapFile; @@ -21,12 +21,21 @@ fn main() -> DiagResult<()> { pcap_file.write_iface_header().unwrap(); loop { - for msg in debug_reader.read_response()? { - debug!("msg: {:?}", msg); - if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).unwrap() { - debug!("gsmtap_msg: {:?}", gsmtap_msg); - pcap_file.write_gsmtap_message(gsmtap_msg, timestamp).unwrap(); - } + match debug_reader.read_response() { + Ok(msgs) => { + for msg in msgs { + debug!("msg: {:?}", msg); + if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).unwrap() { + debug!("gsmtap_msg: {:?}", gsmtap_msg); + pcap_file.write_gsmtap_message(gsmtap_msg, timestamp).unwrap(); + } + } + }, + Err(DiagDeviceError::IO(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => { + println!("Reached end of debug file, exiting..."); + std::process::exit(0); + }, + Err(err) => panic!("Error reading debug file {}", err), } } } diff --git a/src/debug_file.rs b/src/debug_file.rs index 89284d7..55ad776 100644 --- a/src/debug_file.rs +++ b/src/debug_file.rs @@ -5,7 +5,7 @@ use crate::diag::*; use deku::prelude::*; use std::fs::File; use std::io::Read; -use log::{warn, info}; +use log::warn; #[derive(Debug, DekuRead, DekuWrite)] #[deku(endian = "little")] @@ -31,13 +31,7 @@ impl DebugFileReader { impl DiagReader for DebugFileReader { fn get_next_messages_container(&mut self) -> DiagResult { let mut bytes_read_buf = [0; 4]; - if let Err(e) = self.file.read_exact(&mut bytes_read_buf) { - if e.kind() == std::io::ErrorKind::UnexpectedEof { - info!("reached end of debug file, exiting..."); - std::process::exit(0); - } - return Err(e.into()); - } + self.file.read_exact(&mut bytes_read_buf)?; let bytes_read = u32::from_le_bytes(bytes_read_buf) as usize; let mut data = vec![0; bytes_read as usize]; self.file.read_exact(&mut data)?; From 9fa82843435a315c6273ec9f8b0b15a5e28217c3 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Wed, 20 Dec 2023 07:18:31 -0800 Subject: [PATCH 3/6] Add integration tests for LTE RRC parsing --- src/gsmtap.rs | 4 +- tests/test_lte_parsing.rs | 505 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 tests/test_lte_parsing.rs diff --git a/src/gsmtap.rs b/src/gsmtap.rs index cf61ec0..434d91b 100644 --- a/src/gsmtap.rs +++ b/src/gsmtap.rs @@ -189,14 +189,14 @@ pub struct GsmtapHeader { #[deku(assert_eq = "4")] pub header_len: u8, // length in 4-byte words #[deku(update = "self.gsmtap_type.get_type()")] - packet_type: u8, + pub packet_type: u8, pub timeslot: u8, pub arfcn: u16, pub signal_dbm: i8, pub signal_noise_ratio_db: u8, pub frame_number: u32, #[deku(update = "self.gsmtap_type.get_subtype()")] - subtype: u8, + pub subtype: u8, pub antenna_number: u8, pub subslot: u8, #[deku(assert_eq = "0")] diff --git a/tests/test_lte_parsing.rs b/tests/test_lte_parsing.rs new file mode 100644 index 0000000..576fc20 --- /dev/null +++ b/tests/test_lte_parsing.rs @@ -0,0 +1,505 @@ +use wavehunter::diag::{ + Message, + LogBody, + LteRrcOtaPacket, + Timestamp, +}; +use wavehunter::gsmtap_parser::GsmtapParser; +use deku::prelude::*; + +// Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py + +#[test] +fn test_lte_rrc() { + let mut parser = GsmtapParser::new(); + let v26_binary = &[0x10, 0x0, 0x23, 0x0, 0x23, 0x0, 0xc0, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xf, 0x40, 0xf, 0x40, 0x1, 0xe, 0x1, 0x13, 0x7, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x10, 0x15]; + let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 0x23, + inner_length: 0x23, + timestamp: Timestamp { ts: 0 }, + log_type: 0xb0c0, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 26, + packet: LteRrcOtaPacket::V25 { + rrc_rel_maj: 15, + rrc_rel_min: 64, + nr_rrc_rel_maj: 15, + nr_rrc_rel_min: 64, + bearer_id: 1, + phy_cell_id: 270, + earfcn: 1811, + sfn_subfn: 0, + pdu_num: 11, + sib_mask: 0, + len: 2, + packet: vec![0x10, 0x15], + } + } + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[0x10, 0x15]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1811); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 3); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v26_binary = &[ + 0x10, 0x00, 0x23, 0x00, 0x23, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x0f, 0x40, 0x0f, 0x40, 0x01, 0x0e, 0x01, + 0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x15, + ]; + let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 35, + inner_length: 35, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 26, + packet: LteRrcOtaPacket::V25 { + rrc_rel_maj: 15, + rrc_rel_min: 64, + nr_rrc_rel_maj: 15, + nr_rrc_rel_min: 64, + bearer_id: 1, + phy_cell_id: 270, + earfcn: 1811, + sfn_subfn: 0, + pdu_num: 11, + sib_mask: 0, + len: 2, + packet: vec![0x10, 0x15], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x10, 0x15, + + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1811); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 3); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v24_binary = &[ + 0x10, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x0f, 0x22, 0x00, 0x68, 0x00, 0xe4, 0x0c, + 0x00, 0x00, 0x09, 0xdc, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x40, 0x85, 0x8e, 0xc4, 0xe5, + 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x00, + + ]; + let (_, parsed) = Message::from_bytes((v24_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 44, + inner_length: 44, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 24, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 15, + rrc_rel_min: 34, + bearer_id: 0, + phy_cell_id: 104, + earfcn: 3300, + sfn_subfn: 56329, + pdu_num: 5, + sib_mask: 0, + len: 13, + packet: vec![0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, + 0xdc, 0x29, 0x15, 0x16, 0x00, + + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 3300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 3520); + assert_eq!(gsmtap_msg.header.subtype, 6); + assert_eq!(gsmtap_msg.header.subslot, 9); + + let v20_binary = &[ + 0x10, 0x00, 0x37, 0x00, 0x37, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x0e, 0x30, 0x01, 0x09, 0x01, 0x9c, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x08, 0x10, 0xa7, 0x14, 0x53, + 0x59, 0xa6, 0x05, 0x43, 0x68, 0xc0, 0x3b, 0xda, + 0x30, 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, + 0x9a, 0x68, 0x40, + ]; + let (_, parsed) = Message::from_bytes((v20_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 55, + inner_length: 55, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 20, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 14, + rrc_rel_min: 48, + bearer_id: 1, + phy_cell_id: 265, + earfcn: 6300, + sfn_subfn: 0, + pdu_num: 9, + sib_mask: 0, + len: 24, + packet: vec![0x8, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x5, 0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30, 0x4, 0xa6, 0x88, 0x2, 0x8d, 0xa2, 0x0, 0x9a, 0x68, 0x40], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, + 0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30, 0x04, 0xa6, + 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40, + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 6300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 3); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v19_binary = &[ + 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x0e, 0x22, 0x00, 0x0b, 0x00, 0xfa, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x28, 0x18, 0x40, 0x16, 0x08, + 0x08, 0x80, 0x00, 0x00, + ]; + let (_, parsed) = Message::from_bytes((v19_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 40, + inner_length: 40, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 19, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 14, + rrc_rel_min: 34, + bearer_id: 0, + phy_cell_id: 11, + earfcn: 2554, + sfn_subfn: 0, + pdu_num: 50, + sib_mask: 0, + len: 9, + packet: vec![0x28, 0x18, 0x40, 0x16, 0x8, 0x8, 0x80, 0x0, 0x0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, + 0x00, + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 2554); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 16); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v15_binary = &[ + 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0d, 0x21, 0x00, 0x9e, 0x00, 0x14, 0x05, + 0x00, 0x00, 0x49, 0x8c, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x40, 0x0c, 0x8e, 0xc9, 0x42, + 0x89, 0xe0, + ]; + let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 15, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 13, + rrc_rel_min: 33, + bearer_id: 0, + phy_cell_id: 158, + earfcn: 1300, + sfn_subfn: 35913, + pdu_num: 5, + sib_mask: 0, + len: 7, + packet: vec![0x40, 0xc, 0x8e, 0xc9, 0x42, 0x89, 0xe0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0, + + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 2244); + assert_eq!(gsmtap_msg.header.subtype, 6); + assert_eq!(gsmtap_msg.header.subslot, 9); + + let v15_binary = &[ + 0x10, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0d, 0x21, 0x01, 0x9e, 0x00, 0x14, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x08, 0x10, 0xa5, 0x34, 0x61, + 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x04, 0x40, 0x1a, + 0x00, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, + 0x10, 0x67, 0xc1, 0x06, 0xd9, 0xe0, 0x00, + + ]; + let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 59, + inner_length: 59, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 15, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 13, + rrc_rel_min: 33, + bearer_id: 1, + phy_cell_id: 158, + earfcn: 1300, + sfn_subfn: 0, + pdu_num: 9, + sib_mask: 0, + len: 28, + packet: vec![0x8, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x4, 0x40, 0x1a, 0x0, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x0, 0x10, 0x67, 0xc1, 0x6, 0xd9, 0xe0, 0x0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, + 0x31, 0x68, 0x04, 0x40, 0x1a, 0x00, 0x49, 0x16, + 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, + 0x06, 0xd9, 0xe0, 0x00, + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 3); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v13_binary = &[ + 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x0c, 0x74, 0x01, 0x32, 0x00, 0x38, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x2c, 0x00, + ]; + let (_, parsed) = Message::from_bytes((v13_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 33, + inner_length: 33, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 13, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 12, + rrc_rel_min: 116, + bearer_id: 1, + phy_cell_id: 50, + earfcn: 6200, + sfn_subfn: 0, + pdu_num: 8, + sib_mask: 0, + len: 2, + packet: vec![0x2c, 0x0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[0x2c, 0x00]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 6200); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 0); + assert_eq!(gsmtap_msg.header.subtype, 3); + assert_eq!(gsmtap_msg.header.subslot, 0); + + let v9_binary = &[ + 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x0b, 0x70, 0x00, 0x00, 0x01, 0x14, 0x05, + 0x00, 0x00, 0x09, 0x91, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x40, 0x0b, 0x8e, 0xc1, 0xdd, + 0x13, 0xb0, + ]; + let (_, parsed) = Message::from_bytes((v9_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 9, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 11, + rrc_rel_min: 112, + bearer_id: 0, + phy_cell_id: 256, + earfcn: 1300, + sfn_subfn: 37129, + pdu_num: 11, + sib_mask: 0, + len: 7, + packet: vec![0x40, 0xb, 0x8e, 0xc1, 0xdd, 0x13, 0xb0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0, + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 2320); + assert_eq!(gsmtap_msg.header.subtype, 6); + assert_eq!(gsmtap_msg.header.subslot, 9); + + let v8_binary = &[ + 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x0a, 0x72, 0x01, 0x0e, 0x00, 0x9c, 0x18, + 0x00, 0x00, 0xa9, 0x33, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x2e, 0x02, + ]; + let (_, parsed) = Message::from_bytes((v8_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 33, + inner_length: 33, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 8, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 10, + rrc_rel_min: 114, + bearer_id: 1, + phy_cell_id: 14, + earfcn: 6300, + sfn_subfn: 13225, + pdu_num: 6, + sib_mask: 0, + len: 2, + packet: vec![0x2e, 0x2], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[0x2e, 0x02]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 6300); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 826); + assert_eq!(gsmtap_msg.header.subtype, 1); + assert_eq!(gsmtap_msg.header.subslot, 9); + + let v6_binary = &[ + 0x10, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0xc0, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x09, 0xb1, 0x00, 0x07, 0x01, 0x2c, 0x07, + 0x25, 0x34, 0x02, 0x02, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, + 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, + 0x43, 0x26, 0xd0, + ]; + let (_, parsed) = Message::from_bytes((v6_binary, 0)).unwrap(); + assert_eq!(&parsed, &Message::Log { + pending_msgs: 0, + outer_length: 47, + inner_length: 47, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 6, + packet: LteRrcOtaPacket::V5 { + rrc_rel_maj: 9, + rrc_rel_min: 177, + bearer_id: 0, + phy_cell_id: 263, + earfcn: 1836, + sfn_subfn: 13349, + pdu_num: 2, + sib_mask: 2, + len: 18, + packet: vec![0x40, 0x49, 0x88, 0x5, 0xc0, 0x97, 0x2, 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43, 0x26, 0xd0], + }, + }, + }); + let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap(); + assert_eq!(&gsmtap_msg.payload, &[ + 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, + 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43, + 0x26, 0xd0, + ]); + assert_eq!(gsmtap_msg.header.packet_type, 13); + assert_eq!(gsmtap_msg.header.timeslot, 0); + assert_eq!(gsmtap_msg.header.arfcn, 1836); + assert_eq!(gsmtap_msg.header.signal_dbm, 0); + assert_eq!(gsmtap_msg.header.signal_noise_ratio_db, 0); + assert_eq!(gsmtap_msg.header.frame_number, 834); + assert_eq!(gsmtap_msg.header.subtype, 5); + assert_eq!(gsmtap_msg.header.subslot, 5); +} From 73ed01de625ec24f111118f86f55b8fe45d5663c Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Wed, 20 Dec 2023 08:18:59 -0800 Subject: [PATCH 4/6] gstmtap_parser: Add support for NAS messages --- src/diag.rs | 7 ++++++- src/gsmtap.rs | 13 +++++++++++-- src/gsmtap_parser.rs | 8 ++++++++ tests/test_lte_parsing.rs | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/diag.rs b/src/diag.rs index 8d8ebfa..7b6624a 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -119,6 +119,11 @@ pub enum LogBody { #[deku(ctx = "*ext_header_version")] packet: LteRrcOtaPacket, }, + // the four NAS command opcodes refer to: + // * 0xb0e2: plain ESM NAS message (incoming) + // * 0xb0e3: plain ESM NAS message (outgoing) + // * 0xb0ec: plain EMM NAS message (incoming) + // * 0xb0ed: plain EMM NAS message (outgoing) #[deku(id_pat = "0xb0e2 | 0xb0e3 | 0xb0ec | 0xb0ed")] Nas4GMessage { ext_header_version: u8, @@ -132,7 +137,7 @@ pub enum LogBody { #[deku(id = "0x11eb")] IpTraffic { // is this right?? based on https://github.com/P1sec/QCSuper/blob/81dbaeee15ec7747e899daa8e3495e27cdcc1264/src/modules/pcap_dump.py#L378 - #[deku(count = "hdr_len - 8")] // is this right??? + #[deku(count = "hdr_len - 8")] msg: Vec, }, #[deku(id = "0x713a")] diff --git a/src/gsmtap.rs b/src/gsmtap.rs index 434d91b..ef57ac2 100644 --- a/src/gsmtap.rs +++ b/src/gsmtap.rs @@ -21,11 +21,19 @@ pub enum GsmtapType { LteMacFramed, /* LTE MAC with context hdr */ OsmocoreLog, /* libosmocore logging */ QcDiag, /* Qualcomm DIAG frame */ - LteNas, /* LTE Non-Access Stratum */ + LteNas(LteNasSubtype), /* LTE Non-Access Stratum */ E1T1, /* E1/T1 Lines */ GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */ } +// based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1337 +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum LteNasSubtype { + Plain = 0, + Secure = 1, +} + #[repr(u8)] #[derive(Debug, Copy, Clone, PartialEq)] pub enum UmSubtype { @@ -162,7 +170,7 @@ impl GsmtapType { GsmtapType::LteMacFramed => 0x0f, GsmtapType::OsmocoreLog => 0x10, GsmtapType::QcDiag => 0x11, - GsmtapType::LteNas => 0x12, + GsmtapType::LteNas(_) => 0x12, GsmtapType::E1T1 => 0x13, GsmtapType::GsmRlp => 0x14, } @@ -173,6 +181,7 @@ impl GsmtapType { GsmtapType::Um(subtype) => *subtype as u8, GsmtapType::UmtsRrc(subtype) => *subtype as u8, GsmtapType::LteRrc(subtype) => *subtype as u8, + GsmtapType::LteNas(subtype) => *subtype as u8, _ => 0, } } diff --git a/src/gsmtap_parser.rs b/src/gsmtap_parser.rs index e87e13c..540e300 100644 --- a/src/gsmtap_parser.rs +++ b/src/gsmtap_parser.rs @@ -116,6 +116,14 @@ impl GsmtapParser { payload: packet.take_payload(), })) }, + LogBody::Nas4GMessage { msg, .. } => { + // currently we only handle "plain" (i.e. non-secure) NAS messages + let header = GsmtapHeader::new(GsmtapType::LteNas(LteNasSubtype::Plain)); + Ok(Some(GsmtapMessage { + header, + payload: msg, + })) + }, _ => { error!("gsmtap_sink: ignoring unhandled log type: {:?}", value); Ok(None) diff --git a/tests/test_lte_parsing.rs b/tests/test_lte_parsing.rs index 576fc20..e79d951 100644 --- a/tests/test_lte_parsing.rs +++ b/tests/test_lte_parsing.rs @@ -10,7 +10,7 @@ use deku::prelude::*; // Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py #[test] -fn test_lte_rrc() { +fn test_lte_rrc_ota() { let mut parser = GsmtapParser::new(); let v26_binary = &[0x10, 0x0, 0x23, 0x0, 0x23, 0x0, 0xc0, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0xf, 0x40, 0xf, 0x40, 0x1, 0xe, 0x1, 0x13, 0x7, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x10, 0x15]; let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap(); From 20c6e9bf094e2b6362b51a9fe1ff97848b76009c Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Wed, 20 Dec 2023 09:43:45 -0800 Subject: [PATCH 5/6] diag: slightly improve logs --- src/diag.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diag.rs b/src/diag.rs index 7b6624a..3f5b2c5 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -68,6 +68,7 @@ pub enum Message { inner_length: u16, log_type: u16, timestamp: Timestamp, + // pass the log type and log length (inner_length - (sizeof(log_type) + sizeof(timestamp))) #[deku(ctx = "*log_type, *inner_length - 12")] body: LogBody, }, @@ -130,7 +131,7 @@ pub enum LogBody { rrc_rel: u8, rrc_version_minor: u8, rrc_version_major: u8, - // is this right?? based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1327 + // message length = hdr_len - (sizeof(ext_header_version) + sizeof(rrc_rel) + sizeof(rrc_version_minor) + sizeof(rrc_version_major)) #[deku(count = "hdr_len - 4")] msg: Vec, }, From 06efa2c1149841cd732a1d74776cd98b2ef2a3b9 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Tue, 2 Jan 2024 16:14:10 -0800 Subject: [PATCH 6/6] diag_device: minor fixup --- src/diag_device.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/diag_device.rs b/src/diag_device.rs index 3fab969..8cd9da3 100644 --- a/src/diag_device.rs +++ b/src/diag_device.rs @@ -66,12 +66,9 @@ pub struct DiagDevice { impl DiagReader for DiagDevice { fn get_next_messages_container(&mut self) -> DiagResult { - let mut bytes_read; - loop { + let mut bytes_read = 0; + while bytes_read == 0 { bytes_read = self.file.read(&mut self.read_buf)?; - if bytes_read > 0 { - break; - } } if let Some(debug_file) = self.debug_file.as_mut() { let debug_block = DebugFileBlock {