daemon: run analysis in realtime

Currently we just show the results of analysis as a <pre> tagged
JSON blob, but eventually we can make some actual UI
This commit is contained in:
Will Greenberg
2024-04-30 14:43:38 -07:00
parent e8231ad142
commit 3c932f0ce9
11 changed files with 361 additions and 261 deletions
+126 -4
View File
@@ -1,7 +1,10 @@
use std::borrow::Cow;
use chrono::{DateTime, FixedOffset};
use serde::Serialize;
use super::information_element::InformationElement;
use crate::{diag::MessagesContainer, gsmtap_parser};
use super::{information_element::InformationElement, lte_downgrade::LteSib6And7DowngradeAnalyzer};
/// Qualitative measure of how severe a Warning event type is.
/// The levels should break down like this:
@@ -55,22 +58,117 @@ pub trait Analyzer {
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event>;
}
#[derive(Serialize, Debug)]
pub struct AnalyzerMetadata {
name: String,
description: String,
}
#[derive(Serialize, Debug)]
pub struct ReportMetadata {
num_packets_analyzed: usize,
num_packets_skipped: usize,
num_warnings: usize,
first_packet_time: Option<DateTime<FixedOffset>>,
last_packet_time: Option<DateTime<FixedOffset>>,
analyzers: Vec<AnalyzerMetadata>,
}
#[derive(Serialize, Debug, Clone)]
pub struct PacketAnalysis {
timestamp: DateTime<FixedOffset>,
events: Vec<Option<Event>>,
}
#[derive(Serialize, Debug)]
pub struct AnalysisReport {
metadata: ReportMetadata,
analysis: Vec<PacketAnalysis>,
}
pub struct Harness {
analyzers: Vec<Box<dyn Analyzer>>,
analyzers: Vec<Box<dyn Analyzer + Send>>,
pub num_packets_analyzed: usize,
pub num_warnings: usize,
pub skipped_message_reasons: Vec<String>,
pub first_packet_time: Option<DateTime<FixedOffset>>,
pub last_packet_time: Option<DateTime<FixedOffset>>,
pub analysis: Vec<PacketAnalysis>,
}
impl Harness {
pub fn new() -> Self {
Self {
analyzers: Vec::new(),
num_packets_analyzed: 0,
skipped_message_reasons: Vec::new(),
num_warnings: 0,
first_packet_time: None,
last_packet_time: None,
analysis: Vec::new(),
}
}
pub fn add_analyzer(&mut self, analyzer: Box<dyn Analyzer>) {
pub fn new_with_all_analyzers() -> Self {
let mut harness = Harness::new();
harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{}));
harness
}
pub fn add_analyzer(&mut self, analyzer: Box<dyn Analyzer + Send>) {
self.analyzers.push(analyzer);
}
pub fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
pub fn analyze_qmdl_messages(&mut self, container: MessagesContainer) {
for maybe_qmdl_message in container.into_messages() {
let qmdl_message = match maybe_qmdl_message {
Ok(msg) => msg,
Err(err) => {
self.skipped_message_reasons.push(format!("{:?}", err));
continue;
}
};
let gsmtap_message = match gsmtap_parser::parse(qmdl_message) {
Ok(msg) => msg,
Err(err) => {
self.skipped_message_reasons.push(format!("{:?}", err));
continue;
}
};
let Some((timestamp, gsmtap_msg)) = gsmtap_message else {
continue;
};
let element = match InformationElement::try_from(&gsmtap_msg) {
Ok(element) => element,
Err(err) => {
self.skipped_message_reasons.push(format!("{:?}", err));
continue;
}
};
if self.first_packet_time.is_none() {
self.first_packet_time = Some(timestamp.to_datetime());
}
self.last_packet_time = Some(timestamp.to_datetime());
self.num_packets_analyzed += 1;
let analysis_result = self.analyze_information_element(&element);
if analysis_result.iter().any(Option::is_some) {
self.num_warnings += analysis_result.iter()
.filter(|maybe_event| matches!(maybe_event, Some(Event { event_type: EventType::QualitativeWarning { .. }, .. })))
.count();
self.analysis.push(PacketAnalysis {
timestamp: timestamp.to_datetime(),
events: analysis_result,
});
}
}
}
fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
self.analyzers.iter_mut()
.map(|analyzer| analyzer.analyze_information_element(ie))
.collect()
@@ -87,4 +185,28 @@ impl Harness {
.map(|analyzer| analyzer.get_description())
.collect()
}
pub fn build_analysis_report(&self) -> AnalysisReport {
let names = self.get_names();
let descriptions = self.get_names();
let mut analyzers = Vec::new();
for (name, description) in names.iter().zip(descriptions.iter()) {
analyzers.push(AnalyzerMetadata {
name: name.to_string(),
description: description.to_string(),
});
}
AnalysisReport {
metadata: ReportMetadata {
num_packets_analyzed: self.num_packets_analyzed,
num_packets_skipped: self.skipped_message_reasons.len(),
num_warnings: self.num_warnings,
first_packet_time: self.first_packet_time,
last_packet_time: self.last_packet_time,
analyzers,
},
analysis: self.analysis.clone(),
}
}
}
+107 -122
View File
@@ -4,15 +4,6 @@ use crate::gsmtap::*;
use log::error;
use thiserror::Error;
pub struct GsmtapParser {
}
impl Default for GsmtapParser {
fn default() -> Self {
GsmtapParser::new()
}
}
#[derive(Debug, Error)]
pub enum GsmtapParserError {
#[error("Invalid LteRrcOtaMessage ext header version {0}")]
@@ -21,119 +12,113 @@ pub enum GsmtapParserError {
InvalidLteRrcOtaHeaderPduNum(u8, u8),
}
impl GsmtapParser {
pub fn new() -> Self {
GsmtapParser {}
}
pub fn parse(&mut self, msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapParserError> {
if let Message::Log { timestamp, body, .. } = msg {
match self.log_to_gsmtap(body)? {
Some(msg) => Ok(Some((timestamp, msg))),
None => Ok(None),
}
} else {
Ok(None)
}
}
fn log_to_gsmtap(&self, value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserError> {
match value {
LogBody::LteRrcOtaMessage { ext_header_version, packet } => {
let gsmtap_type = match ext_header_version {
0x02 | 0x03 | 0x04 | 0x06 | 0x07 | 0x08 | 0x0d | 0x16 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
3 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
4 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x09 | 0x0c => match packet.get_pdu_num() {
8 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
9 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
10 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
11 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
12 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
13 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
14 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
15 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x0e..=0x10 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x13 | 0x1a | 0x1b => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
3 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
6 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
7 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
8 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
10 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
11 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
45 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
46 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
47 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
48 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
49 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
50 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
52 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
}
0x14 | 0x18 | 0x19 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
54 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
55 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
56 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
57 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
58 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
59 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
61 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
_ => return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(ext_header_version)),
};
let mut header = GsmtapHeader::new(gsmtap_type);
// Wireshark GSMTAP only accepts 14 bits of ARFCN
header.arfcn = packet.get_earfcn().try_into().unwrap_or(0);
header.frame_number = packet.get_sfn();
header.subslot = packet.get_subfn();
Ok(Some(GsmtapMessage {
header,
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)
},
pub fn parse(msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapParserError> {
if let Message::Log { timestamp, body, .. } = msg {
match log_to_gsmtap(body)? {
Some(msg) => Ok(Some((timestamp, msg))),
None => Ok(None),
}
} else {
Ok(None)
}
}
fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserError> {
match value {
LogBody::LteRrcOtaMessage { ext_header_version, packet } => {
let gsmtap_type = match ext_header_version {
0x02 | 0x03 | 0x04 | 0x06 | 0x07 | 0x08 | 0x0d | 0x16 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
3 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
4 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x09 | 0x0c => match packet.get_pdu_num() {
8 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
9 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
10 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
11 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
12 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
13 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
14 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
15 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x0e..=0x10 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
0x13 | 0x1a | 0x1b => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
3 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
6 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
7 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
8 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
10 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
11 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
45 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
46 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
47 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
48 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
49 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
50 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
52 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
}
0x14 | 0x18 | 0x19 => match packet.get_pdu_num() {
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
54 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
55 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
56 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
57 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
58 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
59 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
61 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
},
_ => return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(ext_header_version)),
};
let mut header = GsmtapHeader::new(gsmtap_type);
// Wireshark GSMTAP only accepts 14 bits of ARFCN
header.arfcn = packet.get_earfcn().try_into().unwrap_or(0);
header.frame_number = packet.get_sfn();
header.subslot = packet.get_subfn();
Ok(Some(GsmtapMessage {
header,
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)
},
}
}
+15 -20
View File
@@ -1,17 +1,12 @@
use rayhunter::diag::{
Message,
LogBody,
LteRrcOtaPacket,
Timestamp,
};
use rayhunter::gsmtap_parser::GsmtapParser;
use rayhunter::{diag::{
LogBody, LteRrcOtaPacket, Message, Timestamp
}, gsmtap_parser};
use deku::prelude::*;
// Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py
// Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diaglteloggsmtap_parser::py
#[test]
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,
@@ -42,7 +37,7 @@ fn test_lte_rrc_ota() {
}
}
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(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);
@@ -85,7 +80,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x10, 0x15,
]);
@@ -132,7 +127,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50,
0xdc, 0x29, 0x15, 0x16, 0x00,
@@ -183,7 +178,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05,
0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30, 0x04, 0xa6,
@@ -229,7 +224,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00,
0x00,
@@ -274,7 +269,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,
]);
@@ -324,7 +319,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c,
0x31, 0x68, 0x04, 0x40, 0x1a, 0x00, 0x49, 0x16,
@@ -370,7 +365,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(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);
@@ -412,7 +407,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,
]);
@@ -455,7 +450,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(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);
@@ -501,7 +496,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3,
0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43,