Add rayhunter-check, a utility for running QMDL heuristics

This commit is contained in:
Will Greenberg
2024-03-11 18:21:52 -07:00
parent f19506b415
commit 531c10cf29
10 changed files with 233 additions and 49 deletions
+1
View File
@@ -18,3 +18,4 @@ telcom-parser = { path = "../telcom-parser" }
tokio = { version = "1.35.1", features = ["full"] }
futures-core = "0.3.30"
futures = "0.3.30"
serde = { version = "1.0.197", features = ["derive"] }
+40 -1
View File
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use serde::Serialize;
use super::information_element::InformationElement;
@@ -7,6 +8,7 @@ use super::information_element::InformationElement;
/// * Low: if combined with a large number of other Warnings, user should investigate
/// * Medium: if combined with a few other Warnings, user should investigate
/// * High: user should investigate
#[derive(Serialize, Debug, Clone)]
pub enum Severity {
Low,
Medium,
@@ -15,14 +17,17 @@ pub enum Severity {
/// [QualitativeWarning] events will always be shown to the user in some manner,
/// while `Informational` ones may be hidden based on user settings.
#[derive(Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum EventType {
Informational,
QualitativeWarning(Severity),
QualitativeWarning { severity: Severity },
}
/// Events are user-facing signals that can be emitted by an [Analyzer] upon a
/// message being received. They can be used to signifiy an IC detection
/// warning, or just to display some relevant information to the user.
#[derive(Serialize, Debug, Clone)]
pub struct Event {
pub event_type: EventType,
pub message: String,
@@ -49,3 +54,37 @@ pub trait Analyzer {
/// thousands of them alongside many other [Analyzers](Analyzer).
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event>;
}
pub struct Harness {
analyzers: Vec<Box<dyn Analyzer>>,
}
impl Harness {
pub fn new() -> Self {
Self {
analyzers: Vec::new(),
}
}
pub fn add_analyzer(&mut self, analyzer: Box<dyn Analyzer>) {
self.analyzers.push(analyzer);
}
pub fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
self.analyzers.iter_mut()
.map(|analyzer| analyzer.analyze_information_element(ie))
.collect()
}
pub fn get_names(&self) -> Vec<Cow<'_, str>> {
self.analyzers.iter()
.map(|analyzer| analyzer.get_name())
.collect()
}
pub fn get_descriptions(&self) -> Vec<Cow<'_, str>> {
self.analyzers.iter()
.map(|analyzer| analyzer.get_description())
.collect()
}
}
+25 -22
View File
@@ -52,31 +52,34 @@ pub enum LteInformationElement {
//ScMcchNb(),
}
impl TryFrom<&GsmtapMessage> for LteInformationElement {
impl TryFrom<&GsmtapMessage> for InformationElement {
type Error = InformationElementError;
fn try_from(gsmtap_msg: &GsmtapMessage) -> Result<Self, Self::Error> {
if let GsmtapType::LteRrc(lte_rrc_subtype) = gsmtap_msg.header.gsmtap_type {
use LteRrcSubtype as L;
use LteInformationElement as R;
return match lte_rrc_subtype {
L::DlCcch => Ok(R::DlCcch(decode(&gsmtap_msg.payload)?)),
L::DlDcch => Ok(R::DlDcch(decode(&gsmtap_msg.payload)?)),
L::UlCcch => Ok(R::UlCcch(decode(&gsmtap_msg.payload)?)),
L::UlDcch => Ok(R::UlDcch(decode(&gsmtap_msg.payload)?)),
L::BcchBch => Ok(R::BcchBch(decode(&gsmtap_msg.payload)?)),
L::BcchDlSch => Ok(R::BcchDlSch(decode(&gsmtap_msg.payload)?)),
L::PCCH => Ok(R::PCCH(decode(&gsmtap_msg.payload)?)),
L::MCCH => Ok(R::MCCH(decode(&gsmtap_msg.payload)?)),
L::ScMcch => Ok(R::ScMcch(decode(&gsmtap_msg.payload)?)),
L::BcchBchMbms => Ok(R::BcchBchMbms(decode(&gsmtap_msg.payload)?)),
L::BcchDlSchBr => Ok(R::BcchDlSchBr(decode(&gsmtap_msg.payload)?)),
L::BcchDlSchMbms => Ok(R::BcchDlSchMbms(decode(&gsmtap_msg.payload)?)),
L::SbcchSlBch => Ok(R::SbcchSlBch(decode(&gsmtap_msg.payload)?)),
L::SbcchSlBchV2x => Ok(R::SbcchSlBchV2x(decode(&gsmtap_msg.payload)?)),
_ => Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)),
};
match gsmtap_msg.header.gsmtap_type {
GsmtapType::LteRrc(lte_rrc_subtype) => {
use LteRrcSubtype as L;
use LteInformationElement as R;
let lte = match lte_rrc_subtype {
L::DlCcch => R::DlCcch(decode(&gsmtap_msg.payload)?),
L::DlDcch => R::DlDcch(decode(&gsmtap_msg.payload)?),
L::UlCcch => R::UlCcch(decode(&gsmtap_msg.payload)?),
L::UlDcch => R::UlDcch(decode(&gsmtap_msg.payload)?),
L::BcchBch => R::BcchBch(decode(&gsmtap_msg.payload)?),
L::BcchDlSch => R::BcchDlSch(decode(&gsmtap_msg.payload)?),
L::PCCH => R::PCCH(decode(&gsmtap_msg.payload)?),
L::MCCH => R::MCCH(decode(&gsmtap_msg.payload)?),
L::ScMcch => R::ScMcch(decode(&gsmtap_msg.payload)?),
L::BcchBchMbms => R::BcchBchMbms(decode(&gsmtap_msg.payload)?),
L::BcchDlSchBr => R::BcchDlSchBr(decode(&gsmtap_msg.payload)?),
L::BcchDlSchMbms => R::BcchDlSchMbms(decode(&gsmtap_msg.payload)?),
L::SbcchSlBch => R::SbcchSlBch(decode(&gsmtap_msg.payload)?),
L::SbcchSlBchV2x => R::SbcchSlBchV2x(decode(&gsmtap_msg.payload)?),
_ => return Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)),
};
Ok(InformationElement::LTE(lte))
},
_ => Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)),
}
Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type))
}
}
+8 -8
View File
@@ -5,10 +5,10 @@ use super::information_element::{InformationElement, LteInformationElement};
use telcom_parser::lte_rrc::{BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1, CellReselectionPriority, SystemInformationBlockType7, SystemInformationCriticalExtensions, SystemInformation_r8_IEsSib_TypeAndInfo, SystemInformation_r8_IEsSib_TypeAndInfo_Entry};
/// Based on heuristic T7 from Shinjo Park's "Why We Cannot Win".
pub struct LteSib7DowngradeAnalyzer {
pub struct LteSib6And7DowngradeAnalyzer {
}
impl LteSib7DowngradeAnalyzer {
impl LteSib6And7DowngradeAnalyzer {
fn unpack_system_information<'a>(&self, ie: &'a InformationElement) -> Option<&'a SystemInformation_r8_IEsSib_TypeAndInfo> {
if let InformationElement::LTE(LteInformationElement::BcchDlSch(bcch_dl_sch_message)) = ie {
if let BCCH_DL_SCH_MessageType::C1(BCCH_DL_SCH_MessageType_c1::SystemInformation(system_information)) = &bcch_dl_sch_message.message {
@@ -22,13 +22,13 @@ impl LteSib7DowngradeAnalyzer {
}
// TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones
impl Analyzer for LteSib7DowngradeAnalyzer {
impl Analyzer for LteSib6And7DowngradeAnalyzer {
fn get_name(&self) -> Cow<str> {
Cow::from("LTE SIB 7 Downgrade")
Cow::from("LTE SIB 6/7 Downgrade")
}
fn get_description(&self) -> Cow<str> {
Cow::from("Tests for LTE cells broadcasting a SIB type 7 which include 2G/3G frequencies with higher priorities.")
Cow::from("Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.")
}
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<super::analyzer::Event> {
@@ -41,7 +41,7 @@ impl Analyzer for LteSib7DowngradeAnalyzer {
if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority {
if p == 0 {
return Some(Event {
event_type: EventType::QualitativeWarning(Severity::High),
event_type: EventType::QualitativeWarning { severity: Severity::High },
message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(),
});
}
@@ -53,7 +53,7 @@ impl Analyzer for LteSib7DowngradeAnalyzer {
if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority {
if p == 0 {
return Some(Event {
event_type: EventType::QualitativeWarning(Severity::High),
event_type: EventType::QualitativeWarning { severity: Severity::High },
message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(),
});
}
@@ -66,7 +66,7 @@ impl Analyzer for LteSib7DowngradeAnalyzer {
if let Some(CellReselectionPriority(p)) = carrier_info.common_info.cell_reselection_priority {
if p == 0 {
return Some(Event {
event_type: EventType::QualitativeWarning(Severity::High),
event_type: EventType::QualitativeWarning { severity: Severity::High },
message: "LTE cell advertised a 2G cell for priority 0 reselection".to_string(),
});
}
+1 -1
View File
@@ -26,7 +26,7 @@ impl GsmtapParser {
GsmtapParser {}
}
pub fn recv_message(&mut self, msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapParserError> {
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))),
+11 -11
View File
@@ -42,7 +42,7 @@ fn test_lte_rrc_ota() {
}
}
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +85,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x10, 0x15,
]);
@@ -132,7 +132,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +183,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +229,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00,
0x00,
@@ -274,7 +274,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,
]);
@@ -324,7 +324,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +370,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +412,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = parser.parse(parsed).unwrap().unwrap();
assert_eq!(&gsmtap_msg.payload, &[
0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,
]);
@@ -455,7 +455,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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 +501,7 @@ fn test_lte_rrc_ota() {
},
},
});
let (_, gsmtap_msg) = parser.recv_message(parsed).unwrap().unwrap();
let (_, gsmtap_msg) = 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,