check: support pcaps

rayhunter-check will now analyze any PCAP files it finds in addition to
QMDL
This commit is contained in:
Will Greenberg
2025-07-15 14:31:32 -07:00
committed by Cooper Quintin
parent 3ddbaa07ca
commit c783831e78
7 changed files with 220 additions and 63 deletions
+36 -1
View File
@@ -1,7 +1,9 @@
use chrono::{DateTime, FixedOffset};
use pcap_file_tokio::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use crate::gsmtap::{GsmtapHeader, GsmtapMessage, GsmtapType};
use crate::util::RuntimeMetadata;
use crate::{diag::MessagesContainer, gsmtap_parser};
@@ -167,6 +169,39 @@ impl Harness {
self.analyzers.push(analyzer);
}
pub fn analyze_pcap_packet(&mut self, packet: EnhancedPacketBlock) -> AnalysisRow {
let epoch = DateTime::parse_from_rfc3339("1980-01-06T00:00:00-00:00").unwrap();
let mut row = AnalysisRow {
packet_timestamp: Some(epoch + packet.timestamp),
skipped_message_reason: None,
events: Vec::new(),
};
let gsmtap_offset = 20 + 8;
let gsmtap_data = &packet.data[gsmtap_offset..];
// the type and subtype are at byte offsets 3 and 13, respectively
let gsmtap_header = match GsmtapType::new(gsmtap_data[2], gsmtap_data[12]) {
Ok(gsmtap_type) => GsmtapHeader::new(gsmtap_type),
Err(err) => {
row.skipped_message_reason = Some(format!("failed to read GsmtapHeader: {err:?}"));
return row;
},
};
let packet_offset = gsmtap_offset + 16;
let packet_data = &packet.data[packet_offset..];
let gsmtap_message = GsmtapMessage {
header: gsmtap_header,
payload: packet_data.to_vec(),
};
row.events = match InformationElement::try_from(&gsmtap_message) {
Ok(element) => self.analyze_information_element(&element),
Err(err) => {
row.skipped_message_reason = Some(format!("failed to convert gsmtap message to IE: {err:?}"));
return row;
},
};
return row;
}
pub fn analyze_qmdl_messages(&mut self, container: MessagesContainer) -> Vec<AnalysisRow> {
let mut rows = Vec::new();
for maybe_qmdl_message in container.into_messages() {
@@ -211,7 +246,7 @@ impl Harness {
rows
}
fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
pub fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
self.analyzers
.iter_mut()
.map(|analyzer| analyzer.analyze_information_element(ie))
+6 -6
View File
@@ -293,7 +293,7 @@ impl DiagDevice {
// TPLINK M7350 v5 source code can be downloaded at https://www.tp-link.com/de/support/gpl-code/?app=omada
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct diag_logging_mode_param_t {
struct DiagLoggingModeParam {
req_mode: u32,
peripheral_mask: u32,
mode_param: u8,
@@ -303,16 +303,16 @@ struct diag_logging_mode_param_t {
fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> {
unsafe {
if libc::ioctl(fd, DIAG_IOCTL_SWITCH_LOGGING, mode, 0, 0, 0) < 0 {
let try_params: &[diag_logging_mode_param_t] = &[
let try_params: &[DiagLoggingModeParam] = &[
// tplink M7350 HW revision 3-8 need this mode
#[cfg(feature = "tplink")]
diag_logging_mode_param_t {
DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: 0,
mode_param: 1,
},
// tplink M7350 HW revision v9 requires the same parameters as orbic
diag_logging_mode_param_t {
DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: u32::MAX,
mode_param: 0,
@@ -326,8 +326,8 @@ fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> {
ret = libc::ioctl(
fd,
DIAG_IOCTL_SWITCH_LOGGING,
&mut params as *mut diag_logging_mode_param_t,
std::mem::size_of::<diag_logging_mode_param_t>(),
&mut params as *mut DiagLoggingModeParam,
std::mem::size_of::<DiagLoggingModeParam>(),
0,
0,
0,
+52 -4
View File
@@ -1,6 +1,7 @@
//! The spec for GSMTAP is here: https://github.com/osmocom/libosmocore/blob/master/include/osmocom/core/gsmtap.h
use deku::prelude::*;
use num_enum::TryFromPrimitive;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum GsmtapType {
@@ -28,14 +29,14 @@ pub enum GsmtapType {
// based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1337
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
pub enum LteNasSubtype {
Plain = 0,
Secure = 1,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
pub enum UmSubtype {
Unknown = 0x00,
Bcch = 0x01,
@@ -56,7 +57,7 @@ pub enum UmSubtype {
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
pub enum UmtsRrcSubtype {
DlDcch = 0,
UlDcch = 1,
@@ -123,7 +124,7 @@ pub enum UmtsRrcSubtype {
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
pub enum LteRrcSubtype {
DlCcch = 0,
DlDcch = 1,
@@ -150,7 +151,54 @@ pub enum LteRrcSubtype {
ScMcchNb = 22,
}
#[derive(Debug)]
pub enum GsmtapTypeError {
InvalidTypeSubtypeCombo(u8, u8),
}
impl GsmtapType {
pub fn new(gsmtap_type: u8, gsmtap_subtype: u8) -> Result<Self, GsmtapTypeError> {
let maybe_result = match gsmtap_type {
0x01 => match UmSubtype::try_from(gsmtap_subtype) {
Ok(subtype) => Some(GsmtapType::Um(subtype)),
_ => None,
},
0x02 => Some(GsmtapType::Abis),
0x03 => Some(GsmtapType::UmBurst),
0x04 => Some(GsmtapType::SIM),
0x05 => Some(GsmtapType::TetraI1),
0x06 => Some(GsmtapType::TetraI1Burst),
0x07 => Some(GsmtapType::WmxBurst),
0x08 => Some(GsmtapType::GbLlc),
0x09 => Some(GsmtapType::GbSndcp),
0x0a => Some(GsmtapType::Gmr1Um),
0x0b => Some(GsmtapType::UmtsRlcMac),
0x0c => match UmtsRrcSubtype::try_from(gsmtap_subtype) {
Ok(subtype) => Some(GsmtapType::UmtsRrc(subtype)),
_ => None,
},
0x0d => match LteRrcSubtype::try_from(gsmtap_subtype) {
Ok(subtype) => Some(GsmtapType::LteRrc(subtype)),
_ => None,
},
0x0e => Some(GsmtapType::LteMac),
0x0f => Some(GsmtapType::LteMacFramed),
0x10 => Some(GsmtapType::OsmocoreLog),
0x11 => Some(GsmtapType::QcDiag),
0x12 => match LteNasSubtype::try_from(gsmtap_subtype) {
Ok(subtype) => Some(GsmtapType::LteNas(subtype)),
_ => None,
},
0x13 => Some(GsmtapType::E1T1),
0x14 => Some(GsmtapType::GsmRlp),
_ => None,
};
match maybe_result {
Some(result) => Ok(result),
None => Err(GsmtapTypeError::InvalidTypeSubtypeCombo(gsmtap_type, gsmtap_subtype)),
}
}
pub fn get_type(&self) -> u8 {
match self {
GsmtapType::Um(_) => 0x01,