Parse log bodies

This commit is contained in:
Will Greenberg
2023-12-06 15:19:52 -08:00
parent bdb12aba9e
commit 196eeba520
3 changed files with 154 additions and 32 deletions

View File

@@ -1,7 +1,7 @@
//! Diag protocol serialization/deserialization //! Diag protocol serialization/deserialization
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use deku::prelude::*; use deku::{prelude::*, bitvec::{BitSlice, Msb0}};
#[derive(Debug, Clone, DekuWrite)] #[derive(Debug, Clone, DekuWrite)]
pub struct RequestContainer { pub struct RequestContainer {
@@ -58,7 +58,7 @@ pub struct HdlcEncapsulatedMessage {
pub data: Vec<u8>, pub data: Vec<u8>,
} }
#[derive(Debug, Clone, DekuRead)] #[derive(Debug, Clone, PartialEq, DekuRead)]
#[deku(type = "u8")] #[deku(type = "u8")]
pub enum Message { pub enum Message {
#[deku(id = "16")] #[deku(id = "16")]
@@ -68,8 +68,9 @@ pub enum Message {
inner_length: u16, inner_length: u16,
log_type: u16, log_type: u16,
timestamp: Timestamp, timestamp: Timestamp,
#[deku(count = "inner_length - 12")] //#[deku(count = "inner_length - 12")]
payload: Vec<u8>, #[deku(ctx = "*log_type, *inner_length - 12")]
body: LogBody,
}, },
// kinda unpleasant deku hackery here. deku expects an enum's variant to be // 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<u8>,
},
#[deku(id = "0x512f")]
GsmRrSignallingMessage {
channel_type: u8,
message_type: u8,
length: u8,
#[deku(count = "length")]
msg: Vec<u8>,
},
#[deku(id = "0x5226")]
GprsMacSignallingMessage {
channel_type: u8,
message_type: u8,
length: u8,
#[deku(count = "length")]
msg: Vec<u8>,
},
#[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<u16>,
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<u8>,
},
#[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<u8>,
},
#[deku(id = "0x11eb")]
IpTraffic {
#[deku(count = "hdr_len - 8")] // is this right???
msg: Vec<u8>,
},
#[deku(id = "0x713a")]
UmtsNasOtaMessage {
is_uplink: u8,
length: u32,
#[deku(count = "length")]
msg: Vec<u8>,
},
#[deku(id = "0xb821")]
NrRrcOtaMessage {
#[deku(count = "hdr_len")]
msg: Vec<u8>,
}
}
fn read_lte_rrc_ota_message_log_freq(rest: &BitSlice<u8, Msb0>, ext_header_version: u8) -> Result<(&BitSlice<u8, Msb0>, 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<u8, Msb0>) -> Result<(&BitSlice<u8, Msb0>, Vec<u8>), 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")] #[deku(endian = "little")]
pub struct Timestamp { pub struct Timestamp {
pub ts: u64, 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")] #[deku(ctx = "opcode: u32, subopcode: u32", id = "opcode")]
pub enum ResponsePayload { pub enum ResponsePayload {
#[deku(id = "115")] #[deku(id = "115")]
LogConfig(#[deku(ctx = "subopcode")] LogConfigResponse), LogConfig(#[deku(ctx = "subopcode")] LogConfigResponse),
} }
#[derive(Debug, Clone, DekuRead)] #[derive(Debug, Clone, PartialEq, DekuRead)]
#[deku(ctx = "subopcode: u32", id = "subopcode")] #[deku(ctx = "subopcode: u32", id = "subopcode")]
pub enum LogConfigResponse { pub enum LogConfigResponse {
#[deku(id = "1")] #[deku(id = "1")]
@@ -152,9 +257,10 @@ pub fn build_log_mask_request(log_type: u32, log_mask_bitsize: u32, accepted_log
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING;
use super::*; use super::*;
// Just about all of these test cases from manually parsing diag packets w/ QCSuper
#[test] #[test]
fn test_request_serialization() { fn test_request_serialization() {
let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges); let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges);
@@ -177,17 +283,17 @@ mod test {
fn test_build_log_mask_request() { fn test_build_log_mask_request() {
let log_type = 11; let log_type = 11;
let bitsize = 513; 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 { assert_eq!(req, Request::LogConfig(LogConfigRequest::SetMask {
log_type: log_type, log_type: log_type,
log_mask_bitsize: bitsize, log_mask_bitsize: bitsize,
log_mask: vec![ 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, 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,
0x1, 0x0, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 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, 0x0, 0x0, 0x0, 0x0, 0x0,
], ],
})); }));
} }
@@ -218,18 +324,32 @@ mod test {
} }
#[test] #[test]
fn test_message_parsing() { fn test_logs() {
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]; env_logger::init();
match Message::from_bytes((msg_bytes.as_slice(), 0)) { let data = vec![
Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => { 16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20,
assert_eq!(pending_msgs, 0); 14, 48, 0, 160, 0, 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1,
assert_eq!(outer_length, 26); 238, 173, 213, 77, 208
assert_eq!(inner_length, 26); ];
assert_eq!(log_type, 6311); let msg = Message::from_bytes((&data, 0)).unwrap().1;
assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap()); assert_eq!(msg, Message::Log {
assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]); 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"), });
}
} }
} }

View File

@@ -99,12 +99,13 @@ impl DiagDevice {
Ok(data) => match Message::from_bytes((&data, 0)) { Ok(data) => match Message::from_bytes((&data, 0)) {
Ok(((_, leftover_bytes), res)) => { Ok(((_, leftover_bytes), res)) => {
if leftover_bytes > 0 { if leftover_bytes > 0 {
println!("warning: {} leftover bytes when Response", leftover_bytes); println!("warning: {} leftover bytes when parsing Message", leftover_bytes);
} }
result.push(res); result.push(res);
}, },
Err(e) => { Err(e) => {
println!("error parsing response: {:?}", e); println!("error parsing response: {:?}", e);
println!("{:?}", data);
}, },
}, },
Err(err) => { Err(err) => {

View File

@@ -5,12 +5,13 @@ mod log_codes;
use crate::diag_device::DiagDevice; use crate::diag_device::DiagDevice;
fn main() -> std::io::Result<()> { fn main() -> diag_device::DiagResult<()> {
let mut dev = DiagDevice::new().unwrap(); env_logger::init();
dev.config_logs().unwrap(); let mut dev = DiagDevice::new()?;
dev.config_logs()?;
loop { loop {
for msg in dev.read_response().unwrap() { for msg in dev.read_response()? {
println!("msg: {:?}", msg); println!("msg: {:?}", msg);
} }
} }