diff --git a/src/diag.rs b/src/diag.rs index 9430683..3379fd5 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -1,7 +1,7 @@ //! Diag protocol serialization/deserialization use chrono::{DateTime, FixedOffset}; -use deku::prelude::*; +use deku::{prelude::*, bitvec::{BitSlice, Msb0}}; #[derive(Debug, Clone, DekuWrite)] pub struct RequestContainer { @@ -58,7 +58,7 @@ pub struct HdlcEncapsulatedMessage { pub data: Vec, } -#[derive(Debug, Clone, DekuRead)] +#[derive(Debug, Clone, PartialEq, DekuRead)] #[deku(type = "u8")] pub enum Message { #[deku(id = "16")] @@ -68,8 +68,9 @@ pub enum Message { inner_length: u16, log_type: u16, timestamp: Timestamp, - #[deku(count = "inner_length - 12")] - payload: Vec, + //#[deku(count = "inner_length - 12")] + #[deku(ctx = "*log_type, *inner_length - 12")] + body: LogBody, }, // kinda unpleasant deku hackery here. deku expects an enum's variant to be @@ -86,7 +87,111 @@ pub enum Message { }, } -#[derive(Debug, Clone, DekuRead)] +#[derive(Debug, Clone, PartialEq, DekuRead)] +#[deku(ctx = "log_type: u16, hdr_len: u16", id = "log_type")] +pub enum LogBody { + #[deku(id = "0x412f")] + WcdmaSignallingMessage { + channel_type: u8, + radio_bearer: u8, + length: u16, + #[deku(count = "length")] + msg: Vec, + }, + #[deku(id = "0x512f")] + GsmRrSignallingMessage { + channel_type: u8, + message_type: u8, + length: u8, + #[deku(count = "length")] + msg: Vec, + }, + #[deku(id = "0x5226")] + GprsMacSignallingMessage { + channel_type: u8, + message_type: u8, + length: u8, + #[deku(count = "length")] + msg: Vec, + }, + #[deku(id = "0xb0c0")] + LteRrcOtaMessage { + ext_header_version: u8, + rrc_rel: u8, + rrc_version: u8, + #[deku(skip, cond = "*ext_header_version < 25")] // handle post-NR releases + nc_rrc_rel: Option, + bearer_id: u8, + phy_cell_id: u16, + + // extended header. some of these fields need manual parsing based on + // header version + #[deku(reader = "read_lte_rrc_ota_message_log_freq(deku::rest, *ext_header_version)")] + freq: u32, + sfn: u16, + channel_type: u8, + #[deku(reader = "read_lte_rrc_ota_message_log_msg(deku::rest)")] + msg: Vec, + }, + #[deku(id_pat = "0xb0e2 | 0xb0e3 | 0xb0ec | 0xb0ed")] + Nas4GMessage { + ext_header_version: u8, + rrc_rel: u8, + rrc_version_minor: u8, + rrc_version_major: u8, + #[deku(count = "hdr_len - 4")] // is this right?? + msg: Vec, + }, + #[deku(id = "0x11eb")] + IpTraffic { + #[deku(count = "hdr_len - 8")] // is this right??? + msg: Vec, + }, + #[deku(id = "0x713a")] + UmtsNasOtaMessage { + is_uplink: u8, + length: u32, + #[deku(count = "length")] + msg: Vec, + }, + #[deku(id = "0xb821")] + NrRrcOtaMessage { + #[deku(count = "hdr_len")] + msg: Vec, + } +} + +fn read_lte_rrc_ota_message_log_freq(rest: &BitSlice, ext_header_version: u8) -> Result<(&BitSlice, u32), DekuError> { + if ext_header_version < 8 { + let (rest, freq) = u16::read(rest, ())?; + Ok((rest, freq as u32)) + } else { + let (rest, freq) = u32::read(rest, ())?; + Ok((rest, freq)) + } +} + +fn read_lte_rrc_ota_message_log_msg(rest: &BitSlice) -> Result<(&BitSlice, Vec), DekuError> { + let (mut rest, length) = u16::read(rest, ())?; + if length != rest.len() as u16 / 8 { + let (new_rest, _) = u16::read(rest, ())?; + rest = new_rest; + } + let (new_rest, length) = u16::read(rest, ())?; + rest = new_rest; + if length != rest.len() as u16 / 8 { + return Err(DekuError::Incomplete(NeedSize::new(length as usize * 8))); + } + let mut result = Vec::new(); + for _ in 0..length { + let (new_rest, byte) = u8::read(rest, ())?; + rest = new_rest; + result.push(byte); + } + Ok((rest, result)) +} + +#[derive(Debug, Clone, PartialEq, DekuRead)] #[deku(endian = "little")] pub struct Timestamp { pub ts: u64, @@ -106,14 +211,14 @@ impl Timestamp { } } -#[derive(Debug, Clone, DekuRead)] +#[derive(Debug, Clone, PartialEq, DekuRead)] #[deku(ctx = "opcode: u32, subopcode: u32", id = "opcode")] pub enum ResponsePayload { #[deku(id = "115")] LogConfig(#[deku(ctx = "subopcode")] LogConfigResponse), } -#[derive(Debug, Clone, DekuRead)] +#[derive(Debug, Clone, PartialEq, DekuRead)] #[deku(ctx = "subopcode: u32", id = "subopcode")] pub enum LogConfigResponse { #[deku(id = "1")] @@ -152,9 +257,10 @@ pub fn build_log_mask_request(log_type: u32, log_mask_bitsize: u32, accepted_log #[cfg(test)] mod test { - use crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING; use super::*; + // Just about all of these test cases from manually parsing diag packets w/ QCSuper + #[test] fn test_request_serialization() { let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges); @@ -177,17 +283,17 @@ mod test { fn test_build_log_mask_request() { let log_type = 11; let bitsize = 513; - let req = build_log_mask_request(log_type, bitsize, &LOG_CODES_FOR_RAW_PACKET_LOGGING); + let req = build_log_mask_request(log_type, bitsize, &crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING); assert_eq!(req, Request::LogConfig(LogConfigRequest::SetMask { log_type: log_type, log_mask_bitsize: bitsize, log_mask: vec![ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x0, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, + 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, ], })); } @@ -218,18 +324,32 @@ mod test { } #[test] - fn test_message_parsing() { - let msg_bytes = vec![16, 0, 26, 0, 26, 0, 167, 24, 38, 161, 72, 107, 146, 30, 2, 1, 1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]; - match Message::from_bytes((msg_bytes.as_slice(), 0)) { - Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => { - assert_eq!(pending_msgs, 0); - assert_eq!(outer_length, 26); - assert_eq!(inner_length, 26); - assert_eq!(log_type, 6311); - assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap()); - assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]); + fn test_logs() { + env_logger::init(); + let data = vec![ + 16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20, + 14, 48, 0, 160, 0, 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1, + 238, 173, 213, 77, 208 + ]; + let msg = Message::from_bytes((&data, 0)).unwrap().1; + assert_eq!(msg, Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + log_type: 0xb0c0, + timestamp: Timestamp { ts: 72659535985485082 }, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 20, + rrc_rel: 14, + rrc_version: 48, + nc_rrc_rel: None, + bearer_id: 0, + phy_cell_id: 160, + freq: 2050, + sfn: 4057, + channel_type: 5, + msg: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0], }, - _ => panic!("failed to parse message"), - } + }); } } diff --git a/src/diag_device.rs b/src/diag_device.rs index eb712f5..413adf5 100644 --- a/src/diag_device.rs +++ b/src/diag_device.rs @@ -99,12 +99,13 @@ impl DiagDevice { Ok(data) => match Message::from_bytes((&data, 0)) { Ok(((_, leftover_bytes), res)) => { if leftover_bytes > 0 { - println!("warning: {} leftover bytes when Response", leftover_bytes); + println!("warning: {} leftover bytes when parsing Message", leftover_bytes); } result.push(res); }, Err(e) => { println!("error parsing response: {:?}", e); + println!("{:?}", data); }, }, Err(err) => { diff --git a/src/main.rs b/src/main.rs index 8a63cae..68fbe6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,13 @@ mod log_codes; use crate::diag_device::DiagDevice; -fn main() -> std::io::Result<()> { - let mut dev = DiagDevice::new().unwrap(); - dev.config_logs().unwrap(); +fn main() -> diag_device::DiagResult<()> { + env_logger::init(); + let mut dev = DiagDevice::new()?; + dev.config_logs()?; loop { - for msg in dev.read_response().unwrap() { + for msg in dev.read_response()? { println!("msg: {:?}", msg); } }