From 60cb60dd74a00f41a9a67e4875d817a3e0f016bc Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 17 Jan 2025 12:02:52 -0800 Subject: [PATCH 1/8] check: don't count informational events as warnings, better logging --- Cargo.lock | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ bin/Cargo.toml | 1 + bin/src/check.rs | 54 +++++++++++++++++++++++------- 3 files changed, 129 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ec92b2..e0b9020 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.52.0", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -602,6 +612,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive-into-owned" version = "0.2.0" @@ -1310,6 +1329,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -1360,6 +1385,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.32.2" @@ -1487,6 +1521,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1679,6 +1719,7 @@ dependencies = [ "rayhunter", "serde", "serde_json", + "simple_logger", "tempfile", "thiserror", "tokio", @@ -1901,6 +1942,18 @@ dependencies = [ "quote", ] +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + [[package]] name = "slab" version = "0.4.9" @@ -2065,6 +2118,39 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.36.0" diff --git a/bin/Cargo.toml b/bin/Cargo.toml index 939985a..28d4e97 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml @@ -32,3 +32,4 @@ clap = { version = "4.5.2", features = ["derive"] } serde_json = "1.0.114" image = "0.25.1" tempfile = "3.10.1" +simple_logger = "5.0.0" diff --git a/bin/src/check.rs b/bin/src/check.rs index 02fa0bd..3637369 100644 --- a/bin/src/check.rs +++ b/bin/src/check.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, future, path::PathBuf, pin::pin}; -use rayhunter::{analysis::analyzer::Harness, diag::DataType, gsmtap_parser, pcap::GsmtapPcapWriter, qmdl::QmdlReader}; +use log::{info, warn}; +use rayhunter::{analysis::analyzer::{EventType, Harness}, diag::DataType, gsmtap_parser, pcap::GsmtapPcapWriter, qmdl::QmdlReader}; use tokio::fs::{metadata, read_dir, File}; use clap::Parser; use futures::TryStreamExt; @@ -9,7 +10,7 @@ mod dummy_analyzer; #[derive(Parser, Debug)] #[command(version, about)] struct Args { - #[arg(short, long)] + #[arg(long)] qmdl_path: PathBuf, #[arg(short, long)] @@ -20,6 +21,9 @@ struct Args { #[arg(long)] enable_dummy_analyzer: bool, + + #[arg(short, long)] + quiet: bool, } async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool) { @@ -41,20 +45,37 @@ async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool } for analysis in row.analysis { for maybe_event in analysis.events { - if let Some(event) = maybe_event { - warnings += 1; - println!("{}: {:?}", analysis.timestamp, event); + let Some(event) = maybe_event else { continue }; + match event.event_type { + EventType::Informational => { + info!( + "{}: INFO - {} {}", + qmdl_path, + analysis.timestamp, + event.message, + ); + } + EventType::QualitativeWarning { severity } => { + warn!( + "{}: WARNING (Severity: {:?}) - {} {}", + qmdl_path, + severity, + analysis.timestamp, + event.message, + ); + warnings += 1; + } } } } } if show_skipped && skipped > 0 { - println!("{}: messages skipped:", qmdl_path); + info!("{}: messages skipped:", qmdl_path); for (reason, count) in skipped_reasons.iter() { - println!(" - {}: \"{}\"", count, reason); + info!(" - {}: \"{}\"", count, reason); } } - println!("{}: {} messages analyzed, {} warnings, {} messages skipped", qmdl_path, total_messages, warnings, skipped); + info!("{}: {} messages analyzed, {} warnings, {} messages skipped", qmdl_path, total_messages, warnings, skipped); } async fn pcapify(qmdl_path: &PathBuf) { @@ -75,21 +96,30 @@ async fn pcapify(qmdl_path: &PathBuf) { } } } - println!("wrote pcap to {:?}", &pcap_path); + info!("wrote pcap to {:?}", &pcap_path); } #[tokio::main] async fn main() { - env_logger::init(); let args = Args::parse(); + let level = if args.quiet { + log::LevelFilter::Warn + } else { + log::LevelFilter::Trace + }; + simple_logger::SimpleLogger::new() + .with_colors(true) + .without_timestamps() + .with_level(level) + .init().unwrap(); let mut harness = Harness::new_with_all_analyzers(); if args.enable_dummy_analyzer { harness.add_analyzer(Box::new(dummy_analyzer::TestAnalyzer { count: 0 })); } - println!("Analyzers:"); + info!("Analyzers:"); for analyzer in harness.get_metadata().analyzers { - println!(" - {}: {}", analyzer.name, analyzer.description); + info!(" - {}: {}", analyzer.name, analyzer.description); } let metadata = metadata(&args.qmdl_path).await.expect("failed to get metadata"); From 9ebe5d86b5ac446d676cc731f1e6c776b93ac72f Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 17 Jan 2025 12:03:40 -0800 Subject: [PATCH 2/8] Better wording on IMSI requested warning --- lib/src/analysis/imsi_requested.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/analysis/imsi_requested.rs b/lib/src/analysis/imsi_requested.rs index 7b15621..75bb623 100644 --- a/lib/src/analysis/imsi_requested.rs +++ b/lib/src/analysis/imsi_requested.rs @@ -30,7 +30,7 @@ impl Analyzer for ImsiRequestedAnalyzer { return None; }; - // NAS identity request + // NAS identity request, ID type IMSI if payload == &[0x07, 0x55, 0x01] { if self.packet_num < PACKET_THRESHHOLD { return Some(Event { @@ -38,7 +38,7 @@ impl Analyzer for ImsiRequestedAnalyzer { severity: Severity::Medium }, message: format!( - "NAS IMSI request detected, however it was within \ + "NAS IMSI identity request detected, however it was within \ the first {} packets of this analysis. If you just \ turned your device on, this is likely a \ false-positive.", @@ -50,7 +50,7 @@ impl Analyzer for ImsiRequestedAnalyzer { event_type: EventType::QualitativeWarning { severity: Severity::High }, - message: format!("NAS IMSI request detected"), + message: format!("NAS IMSI identity request detected"), }) } } From 412d69efa2b361b16633c0153a47292e71fb1e6b Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 17 Jan 2025 12:33:14 -0800 Subject: [PATCH 3/8] Rewrite our 2G downgrade analyzer --- lib/src/analysis/analyzer.rs | 4 +- lib/src/analysis/lte_downgrade.rs | 86 +++++++++---------------------- lib/src/analysis/mod.rs | 1 + lib/src/analysis/util.rs | 7 +++ 4 files changed, 33 insertions(+), 65 deletions(-) create mode 100644 lib/src/analysis/util.rs diff --git a/lib/src/analysis/analyzer.rs b/lib/src/analysis/analyzer.rs index 03958d5..47fa5aa 100644 --- a/lib/src/analysis/analyzer.rs +++ b/lib/src/analysis/analyzer.rs @@ -7,7 +7,7 @@ use crate::{diag::MessagesContainer, gsmtap_parser}; use super::{ imsi_requested::ImsiRequestedAnalyzer, information_element::InformationElement, - lte_downgrade::LteSib6And7DowngradeAnalyzer, + lte_downgrade::ConnectionRedirect2GDowngradeAnalyzer, null_cipher::NullCipherAnalyzer, }; @@ -117,7 +117,7 @@ impl Harness { pub fn new_with_all_analyzers() -> Self { let mut harness = Harness::new(); - harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{})); + harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer{})); harness.add_analyzer(Box::new(ImsiRequestedAnalyzer::new())); harness.add_analyzer(Box::new(NullCipherAnalyzer{})); diff --git a/lib/src/analysis/lte_downgrade.rs b/lib/src/analysis/lte_downgrade.rs index 44003fd..101cbef 100644 --- a/lib/src/analysis/lte_downgrade.rs +++ b/lib/src/analysis/lte_downgrade.rs @@ -2,80 +2,40 @@ use std::borrow::Cow; use super::analyzer::{Analyzer, Event, EventType, Severity}; 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}; +use telcom_parser::lte_rrc::{DL_DCCH_Message, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions, RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo}; +use super::util::unpack; /// Based on heuristic T7 from Shinjo Park's "Why We Cannot Win". -pub struct LteSib6And7DowngradeAnalyzer { -} - -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 { - if let SystemInformationCriticalExtensions::SystemInformation_r8(sib) = &system_information.critical_extensions { - return Some(&sib.sib_type_and_info); - } - } - } - None - } +pub struct ConnectionRedirect2GDowngradeAnalyzer { } // TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones -impl Analyzer for LteSib6And7DowngradeAnalyzer { +impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer { fn get_name(&self) -> Cow { - Cow::from("LTE SIB 6/7 Downgrade") + Cow::from("Connection Release/Redirected Carrier 2G Downgrade") } fn get_description(&self) -> Cow { - Cow::from("Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.") + Cow::from("Tests if a cell releases our connection and redirects us to a 2G cell.") } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { - let sibs = &self.unpack_system_information(ie)?.0; - for sib in sibs { - match sib { - SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib6(sib6) => { - if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_fdd.as_ref() { - for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { - if p == 0 { - return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, - message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(), - }); - } - } - } - } - if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_tdd.as_ref() { - for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { - if p == 0 { - return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, - message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(), - }); - } - } - } - } - }, - SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib7(SystemInformationBlockType7 { carrier_freqs_info_list: Some(carrier_info_list), .. }) => { - for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.common_info.cell_reselection_priority { - if p == 0 { - return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, - message: "LTE cell advertised a 2G cell for priority 0 reselection".to_string(), - }); - } - } - } - }, - _ => {}, - } + fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { + unpack!(InformationElement::LTE(lte_ie) = ie); + unpack!(LteInformationElement::DlDcch(DL_DCCH_Message { message }) = lte_ie); + unpack!(DL_DCCH_MessageType::C1(c1) = message); + unpack!(DL_DCCH_MessageType_c1::RrcConnectionRelease(release) = c1); + unpack!(RRCConnectionReleaseCriticalExtensions::C1(c1) = &release.critical_extensions); + unpack!(RRCConnectionReleaseCriticalExtensions_c1::RrcConnectionRelease_r8(r8_ies) = c1); + unpack!(Some(carrier_info) = &r8_ies.redirected_carrier_info); + match carrier_info { + RedirectedCarrierInfo::Geran(_carrier_freqs_geran) => Some(Event { + event_type: EventType::QualitativeWarning { severity: Severity::High }, + message: format!("Detected 2G downgrade"), + }), + _ => Some(Event { + event_type: EventType::Informational, + message: format!("RRCConnectionRelease CarrierInfo: {:?}", carrier_info), + }), } - None } } diff --git a/lib/src/analysis/mod.rs b/lib/src/analysis/mod.rs index d777f98..5b70ed5 100644 --- a/lib/src/analysis/mod.rs +++ b/lib/src/analysis/mod.rs @@ -4,3 +4,4 @@ pub mod lte_downgrade; pub mod imsi_provided; pub mod imsi_requested; pub mod null_cipher; +pub mod util; diff --git a/lib/src/analysis/util.rs b/lib/src/analysis/util.rs new file mode 100644 index 0000000..a011dfb --- /dev/null +++ b/lib/src/analysis/util.rs @@ -0,0 +1,7 @@ +macro_rules! unpack { + ($pat:pat = $val:expr) => { + let $pat = $val else { return None; }; + }; +} + +pub(crate) use unpack; From d07a87608b56cd6fb824bf0bc5d2590276c43c6e Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 17 Jan 2025 13:00:28 -0800 Subject: [PATCH 4/8] fix attribution --- lib/src/analysis/lte_downgrade.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/analysis/lte_downgrade.rs b/lib/src/analysis/lte_downgrade.rs index 101cbef..e75c089 100644 --- a/lib/src/analysis/lte_downgrade.rs +++ b/lib/src/analysis/lte_downgrade.rs @@ -5,7 +5,8 @@ use super::information_element::{InformationElement, LteInformationElement}; use telcom_parser::lte_rrc::{DL_DCCH_Message, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions, RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo}; use super::util::unpack; -/// Based on heuristic T7 from Shinjo Park's "Why We Cannot Win". +// Based on HITBSecConf presentation "Forcing a targeted LTE cellphone into an +// eavesdropping network" by Lin Huang pub struct ConnectionRedirect2GDowngradeAnalyzer { } From 01f4bffddedd1a01dfc1b3e0f4fb7432baf17746 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Fri, 17 Jan 2025 13:31:50 -0800 Subject: [PATCH 5/8] Keep old 2G downgrade analyzer --- lib/src/analysis/analyzer.rs | 6 +- ...de.rs => connection_redirect_downgrade.rs} | 0 lib/src/analysis/mod.rs | 3 +- lib/src/analysis/priority_2g_downgrade.rs | 81 +++++++++++++++++++ lib/src/analysis/util.rs | 27 +++++++ 5 files changed, 114 insertions(+), 3 deletions(-) rename lib/src/analysis/{lte_downgrade.rs => connection_redirect_downgrade.rs} (100%) create mode 100644 lib/src/analysis/priority_2g_downgrade.rs diff --git a/lib/src/analysis/analyzer.rs b/lib/src/analysis/analyzer.rs index 47fa5aa..c1f8da6 100644 --- a/lib/src/analysis/analyzer.rs +++ b/lib/src/analysis/analyzer.rs @@ -7,7 +7,8 @@ use crate::{diag::MessagesContainer, gsmtap_parser}; use super::{ imsi_requested::ImsiRequestedAnalyzer, information_element::InformationElement, - lte_downgrade::ConnectionRedirect2GDowngradeAnalyzer, + connection_redirect_downgrade::ConnectionRedirect2GDowngradeAnalyzer, + priority_2g_downgrade::LteSib6And7DowngradeAnalyzer, null_cipher::NullCipherAnalyzer, }; @@ -117,8 +118,9 @@ impl Harness { pub fn new_with_all_analyzers() -> Self { let mut harness = Harness::new(); - harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer{})); harness.add_analyzer(Box::new(ImsiRequestedAnalyzer::new())); + harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer{})); + harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{})); harness.add_analyzer(Box::new(NullCipherAnalyzer{})); harness diff --git a/lib/src/analysis/lte_downgrade.rs b/lib/src/analysis/connection_redirect_downgrade.rs similarity index 100% rename from lib/src/analysis/lte_downgrade.rs rename to lib/src/analysis/connection_redirect_downgrade.rs diff --git a/lib/src/analysis/mod.rs b/lib/src/analysis/mod.rs index 5b70ed5..755172d 100644 --- a/lib/src/analysis/mod.rs +++ b/lib/src/analysis/mod.rs @@ -1,6 +1,7 @@ pub mod analyzer; pub mod information_element; -pub mod lte_downgrade; +pub mod priority_2g_downgrade; +pub mod connection_redirect_downgrade; pub mod imsi_provided; pub mod imsi_requested; pub mod null_cipher; diff --git a/lib/src/analysis/priority_2g_downgrade.rs b/lib/src/analysis/priority_2g_downgrade.rs new file mode 100644 index 0000000..44003fd --- /dev/null +++ b/lib/src/analysis/priority_2g_downgrade.rs @@ -0,0 +1,81 @@ +use std::borrow::Cow; + +use super::analyzer::{Analyzer, Event, EventType, Severity}; +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 LteSib6And7DowngradeAnalyzer { +} + +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 { + if let SystemInformationCriticalExtensions::SystemInformation_r8(sib) = &system_information.critical_extensions { + return Some(&sib.sib_type_and_info); + } + } + } + None + } +} + +// TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones +impl Analyzer for LteSib6And7DowngradeAnalyzer { + fn get_name(&self) -> Cow { + Cow::from("LTE SIB 6/7 Downgrade") + } + + fn get_description(&self) -> Cow { + 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 { + let sibs = &self.unpack_system_information(ie)?.0; + for sib in sibs { + match sib { + SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib6(sib6) => { + if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_fdd.as_ref() { + for carrier_info in &carrier_info_list.0 { + if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { + if p == 0 { + return Some(Event { + event_type: EventType::QualitativeWarning { severity: Severity::High }, + message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(), + }); + } + } + } + } + if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_tdd.as_ref() { + for carrier_info in &carrier_info_list.0 { + if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { + if p == 0 { + return Some(Event { + event_type: EventType::QualitativeWarning { severity: Severity::High }, + message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(), + }); + } + } + } + } + }, + SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib7(SystemInformationBlockType7 { carrier_freqs_info_list: Some(carrier_info_list), .. }) => { + for carrier_info in &carrier_info_list.0 { + if let Some(CellReselectionPriority(p)) = carrier_info.common_info.cell_reselection_priority { + if p == 0 { + return Some(Event { + event_type: EventType::QualitativeWarning { severity: Severity::High }, + message: "LTE cell advertised a 2G cell for priority 0 reselection".to_string(), + }); + } + } + } + }, + _ => {}, + } + } + None + } +} diff --git a/lib/src/analysis/util.rs b/lib/src/analysis/util.rs index a011dfb..c021ca8 100644 --- a/lib/src/analysis/util.rs +++ b/lib/src/analysis/util.rs @@ -1,7 +1,34 @@ + +/// Unpacks a pattern, or returns None. +/// +/// # Examples +/// Suppose you've got some highly nested enum: +/// ``` +/// enum Foo { +/// A(Bar), +/// B, +/// } +/// +/// enum Baz { +/// C(Bang) +/// } +/// +/// struct Bang; +/// ``` +/// +/// You can use `unpack!` to unroll it like this: +/// ``` +/// fn get_bang(foo: Foo) -> Option { +/// unpack!(Foo::A(bar) = foo); +/// unpack!(Baz::C(bang) = bar); +/// bang +/// } +/// ``` macro_rules! unpack { ($pat:pat = $val:expr) => { let $pat = $val else { return None; }; }; } +// this is apparently how you make a macro publicly usable from this module pub(crate) use unpack; From 1bfaa45efe6bb84bfccdcdffe30a4842864b8b51 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Tue, 21 Jan 2025 15:02:41 -0800 Subject: [PATCH 6/8] fix docstring code --- lib/src/analysis/util.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/src/analysis/util.rs b/lib/src/analysis/util.rs index c021ca8..84d5aeb 100644 --- a/lib/src/analysis/util.rs +++ b/lib/src/analysis/util.rs @@ -2,28 +2,28 @@ /// Unpacks a pattern, or returns None. /// /// # Examples -/// Suppose you've got some highly nested enum: +/// You can use `unpack!` to unroll highly nested enums like this: /// ``` +/// use super::util::unpack; +/// /// enum Foo { /// A(Bar), /// B, /// } /// -/// enum Baz { -/// C(Bang) +/// enum Bar { +/// C(Baz) /// } /// -/// struct Bang; -/// ``` +/// struct Baz; /// -/// You can use `unpack!` to unroll it like this: -/// ``` -/// fn get_bang(foo: Foo) -> Option { +/// fn get_bang(foo: Foo) -> Option { /// unpack!(Foo::A(bar) = foo); -/// unpack!(Baz::C(bang) = bar); -/// bang +/// unpack!(Bar::C(baz) = bar); +/// baz /// } /// ``` +/// macro_rules! unpack { ($pat:pat = $val:expr) => { let $pat = $val else { return None; }; From f96410e66b3d9edf83b090976f1f93c873beb2b8 Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Tue, 21 Jan 2025 15:11:32 -0800 Subject: [PATCH 7/8] this macro isn't public, so docstrings won't work --- lib/src/analysis/util.rs | 48 +++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/src/analysis/util.rs b/lib/src/analysis/util.rs index 84d5aeb..2076c61 100644 --- a/lib/src/analysis/util.rs +++ b/lib/src/analysis/util.rs @@ -1,29 +1,27 @@ -/// Unpacks a pattern, or returns None. -/// -/// # Examples -/// You can use `unpack!` to unroll highly nested enums like this: -/// ``` -/// use super::util::unpack; -/// -/// enum Foo { -/// A(Bar), -/// B, -/// } -/// -/// enum Bar { -/// C(Baz) -/// } -/// -/// struct Baz; -/// -/// fn get_bang(foo: Foo) -> Option { -/// unpack!(Foo::A(bar) = foo); -/// unpack!(Bar::C(baz) = bar); -/// baz -/// } -/// ``` -/// +// Unpacks a pattern, or returns None. +// +// # Examples +// You can use `unpack!` to unroll highly nested enums like this: +// ``` +// enum Foo { +// A(Bar), +// B, +// } +// +// enum Bar { +// C(Baz) +// } +// +// struct Baz; +// +// fn get_bang(foo: Foo) -> Option { +// unpack!(Foo::A(bar) = foo); +// unpack!(Bar::C(baz) = bar); +// baz +// } +// ``` +// macro_rules! unpack { ($pat:pat = $val:expr) => { let $pat = $val else { return None; }; From e24dac1184ff1874e6d90fff46ec1c37334df07c Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Tue, 21 Jan 2025 15:14:55 -0800 Subject: [PATCH 8/8] check: give qmdl-path a shorthand arg --- bin/src/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/src/check.rs b/bin/src/check.rs index 3637369..9a535a8 100644 --- a/bin/src/check.rs +++ b/bin/src/check.rs @@ -10,7 +10,7 @@ mod dummy_analyzer; #[derive(Parser, Debug)] #[command(version, about)] struct Args { - #[arg(long)] + #[arg(short = 'p', long)] qmdl_path: PathBuf, #[arg(short, long)]