mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-06-08 06:01:53 -07:00
chore: cargo fmt
This commit is contained in:
committed by
Will Greenberg
parent
151e186ef9
commit
9fe75ac961
+9
-3
@@ -18,9 +18,9 @@ use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::{RwLock, RwLockWriteGuard};
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
use crate::dummy_analyzer::TestAnalyzer;
|
||||
use crate::qmdl_store::RecordingStore;
|
||||
use crate::server::ServerState;
|
||||
use crate::dummy_analyzer::TestAnalyzer;
|
||||
|
||||
pub struct AnalysisWriter {
|
||||
writer: BufWriter<File>,
|
||||
@@ -53,7 +53,10 @@ impl AnalysisWriter {
|
||||
|
||||
// Runs the analysis harness on the given container, serializing the results
|
||||
// to the analysis file and returning the file's new length.
|
||||
pub async fn analyze(&mut self, container: MessagesContainer) -> Result<(usize, bool), std::io::Error> {
|
||||
pub async fn analyze(
|
||||
&mut self,
|
||||
container: MessagesContainer,
|
||||
) -> Result<(usize, bool), std::io::Error> {
|
||||
let row = self.harness.analyze_qmdl_messages(container);
|
||||
if !row.is_empty() {
|
||||
self.write(&row).await?;
|
||||
@@ -182,7 +185,10 @@ pub fn run_analysis_thread(
|
||||
let count = queued_len(analysis_status_lock.clone()).await;
|
||||
for _ in 0..count {
|
||||
let name = dequeue_to_running(analysis_status_lock.clone()).await;
|
||||
if let Err(err) = perform_analysis(&name, qmdl_store_lock.clone(), enable_dummy_analyzer).await {
|
||||
if let Err(err) =
|
||||
perform_analysis(&name, qmdl_store_lock.clone(), enable_dummy_analyzer)
|
||||
.await
|
||||
{
|
||||
error!("failed to analyze {}: {}", name, err);
|
||||
}
|
||||
clear_running(analysis_status_lock.clone()).await;
|
||||
|
||||
+48
-21
@@ -1,9 +1,15 @@
|
||||
use std::{collections::HashMap, future, path::PathBuf, pin::pin};
|
||||
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;
|
||||
use log::{info, warn};
|
||||
use rayhunter::{
|
||||
analysis::analyzer::{EventType, Harness},
|
||||
diag::DataType,
|
||||
gsmtap_parser,
|
||||
pcap::GsmtapPcapWriter,
|
||||
qmdl::QmdlReader,
|
||||
};
|
||||
use std::{collections::HashMap, future, path::PathBuf, pin::pin};
|
||||
use tokio::fs::{metadata, read_dir, File};
|
||||
|
||||
mod dummy_analyzer;
|
||||
|
||||
@@ -28,15 +34,24 @@ struct Args {
|
||||
|
||||
async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool) {
|
||||
let qmdl_file = &mut File::open(&qmdl_path).await.expect("failed to open file");
|
||||
let file_size = qmdl_file.metadata().await.expect("failed to get QMDL file metadata").len();
|
||||
let file_size = qmdl_file
|
||||
.metadata()
|
||||
.await
|
||||
.expect("failed to get QMDL file metadata")
|
||||
.len();
|
||||
let mut qmdl_reader = QmdlReader::new(qmdl_file, Some(file_size as usize));
|
||||
let mut qmdl_stream = pin!(qmdl_reader.as_stream()
|
||||
let mut qmdl_stream = pin!(qmdl_reader
|
||||
.as_stream()
|
||||
.try_filter(|container| future::ready(container.data_type == DataType::UserSpace)));
|
||||
let mut skipped_reasons: HashMap<String, i32> = HashMap::new();
|
||||
let mut total_messages = 0;
|
||||
let mut warnings = 0;
|
||||
let mut skipped = 0;
|
||||
while let Some(container) = qmdl_stream.try_next().await.expect("failed getting QMDL container") {
|
||||
while let Some(container) = qmdl_stream
|
||||
.try_next()
|
||||
.await
|
||||
.expect("failed getting QMDL container")
|
||||
{
|
||||
let row = harness.analyze_qmdl_messages(container);
|
||||
total_messages += 1;
|
||||
for reason in row.skipped_message_reasons {
|
||||
@@ -50,18 +65,13 @@ async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool
|
||||
EventType::Informational => {
|
||||
info!(
|
||||
"{}: INFO - {} {}",
|
||||
qmdl_path,
|
||||
analysis.timestamp,
|
||||
event.message,
|
||||
qmdl_path, analysis.timestamp, event.message,
|
||||
);
|
||||
}
|
||||
EventType::QualitativeWarning { severity } => {
|
||||
warn!(
|
||||
"{}: WARNING (Severity: {:?}) - {} {}",
|
||||
qmdl_path,
|
||||
severity,
|
||||
analysis.timestamp,
|
||||
event.message,
|
||||
qmdl_path, severity, analysis.timestamp, event.message,
|
||||
);
|
||||
warnings += 1;
|
||||
}
|
||||
@@ -75,22 +85,36 @@ async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool
|
||||
info!(" - {}: \"{}\"", count, reason);
|
||||
}
|
||||
}
|
||||
info!("{}: {} 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) {
|
||||
let qmdl_file = &mut File::open(&qmdl_path).await.expect("failed to open qmdl file");
|
||||
let qmdl_file = &mut File::open(&qmdl_path)
|
||||
.await
|
||||
.expect("failed to open qmdl file");
|
||||
let qmdl_file_size = qmdl_file.metadata().await.unwrap().len();
|
||||
let mut qmdl_reader = QmdlReader::new(qmdl_file, Some(qmdl_file_size as usize));
|
||||
let mut pcap_path = qmdl_path.clone();
|
||||
pcap_path.set_extension("pcap");
|
||||
let pcap_file = &mut File::create(&pcap_path).await.expect("failed to open pcap file");
|
||||
let pcap_file = &mut File::create(&pcap_path)
|
||||
.await
|
||||
.expect("failed to open pcap file");
|
||||
let mut pcap_writer = GsmtapPcapWriter::new(pcap_file).await.unwrap();
|
||||
pcap_writer.write_iface_header().await.unwrap();
|
||||
while let Some(container) = qmdl_reader.get_next_messages_container().await.expect("failed to get container") {
|
||||
while let Some(container) = qmdl_reader
|
||||
.get_next_messages_container()
|
||||
.await
|
||||
.expect("failed to get container")
|
||||
{
|
||||
for msg in container.into_messages().into_iter().flatten() {
|
||||
if let Ok(Some((timestamp, parsed))) = gsmtap_parser::parse(msg) {
|
||||
pcap_writer.write_gsmtap_message(parsed, timestamp).await.expect("failed to write");
|
||||
pcap_writer
|
||||
.write_gsmtap_message(parsed, timestamp)
|
||||
.await
|
||||
.expect("failed to write");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +133,8 @@ async fn main() {
|
||||
.with_colors(true)
|
||||
.without_timestamps()
|
||||
.with_level(level)
|
||||
.init().unwrap();
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
let mut harness = Harness::new_with_all_analyzers();
|
||||
if args.enable_dummy_analyzer {
|
||||
@@ -120,7 +145,9 @@ async fn main() {
|
||||
info!(" - {}: {}", analyzer.name, analyzer.description);
|
||||
}
|
||||
|
||||
let metadata = metadata(&args.qmdl_path).await.expect("failed to get metadata");
|
||||
let metadata = metadata(&args.qmdl_path)
|
||||
.await
|
||||
.expect("failed to get metadata");
|
||||
if metadata.is_dir() {
|
||||
let mut dir = read_dir(&args.qmdl_path).await.expect("failed to read dir");
|
||||
while let Some(entry) = dir.next_entry().await.expect("failed to get entry") {
|
||||
|
||||
+5
-3
@@ -2,8 +2,7 @@ use crate::error::RayhunterError;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
pub qmdl_store_path: String,
|
||||
@@ -27,7 +26,10 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_config<P>(path: P) -> Result<Config, RayhunterError> where P: AsRef<std::path::Path> {
|
||||
pub fn parse_config<P>(path: P) -> Result<Config, RayhunterError>
|
||||
where
|
||||
P: AsRef<std::path::Path>,
|
||||
{
|
||||
if let Ok(config_file) = std::fs::read_to_string(&path) {
|
||||
Ok(toml::from_str(&config_file).map_err(RayhunterError::ConfigFileParsingError)?)
|
||||
} else {
|
||||
|
||||
+68
-33
@@ -1,38 +1,43 @@
|
||||
mod analysis;
|
||||
mod config;
|
||||
mod error;
|
||||
mod pcap;
|
||||
mod server;
|
||||
mod stats;
|
||||
mod qmdl_store;
|
||||
mod diag;
|
||||
mod display;
|
||||
mod dummy_analyzer;
|
||||
mod error;
|
||||
mod pcap;
|
||||
mod qmdl_store;
|
||||
mod server;
|
||||
mod stats;
|
||||
|
||||
use crate::config::{parse_config, parse_args};
|
||||
use crate::config::{parse_args, parse_config};
|
||||
use crate::diag::run_diag_read_thread;
|
||||
use crate::qmdl_store::RecordingStore;
|
||||
use crate::server::{ServerState, get_qmdl, serve_static};
|
||||
use crate::pcap::get_pcap;
|
||||
use crate::stats::get_system_stats;
|
||||
use crate::error::RayhunterError;
|
||||
use crate::pcap::get_pcap;
|
||||
use crate::qmdl_store::RecordingStore;
|
||||
use crate::server::{get_qmdl, serve_static, ServerState};
|
||||
use crate::stats::get_system_stats;
|
||||
|
||||
use analysis::{get_analysis_status, run_analysis_thread, start_analysis, AnalysisCtrlMessage, AnalysisStatus};
|
||||
use analysis::{
|
||||
get_analysis_status, run_analysis_thread, start_analysis, AnalysisCtrlMessage, AnalysisStatus,
|
||||
};
|
||||
use axum::response::Redirect;
|
||||
use diag::{delete_all_recordings, delete_recording, get_analysis_report, start_recording, stop_recording, DiagDeviceCtrlMessage};
|
||||
use log::{info, error};
|
||||
use qmdl_store::RecordingStoreError;
|
||||
use rayhunter::diag_device::DiagDevice;
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use diag::{
|
||||
delete_all_recordings, delete_recording, get_analysis_report, start_recording, stop_recording,
|
||||
DiagDeviceCtrlMessage,
|
||||
};
|
||||
use log::{error, info};
|
||||
use qmdl_store::RecordingStoreError;
|
||||
use rayhunter::diag_device::DiagDevice;
|
||||
use stats::get_qmdl_manifest;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::mpsc::{self, Sender};
|
||||
use tokio::sync::{oneshot, RwLock};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::{RwLock, oneshot};
|
||||
use std::sync::Arc;
|
||||
|
||||
type AppRouter = Router<Arc<ServerState>>;
|
||||
|
||||
@@ -70,7 +75,8 @@ async fn run_server(
|
||||
info!("The orca is hunting for stingrays...");
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(server_shutdown_signal(server_shutdown_rx))
|
||||
.await.unwrap();
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -88,7 +94,9 @@ async fn init_qmdl_store(config: &config::Config) -> Result<RecordingStore, Rayh
|
||||
if store_exists {
|
||||
Ok(RecordingStore::load(&config.qmdl_store_path).await?)
|
||||
} else {
|
||||
Err(RayhunterError::NoStoreDebugMode(config.qmdl_store_path.clone()))
|
||||
Err(RayhunterError::NoStoreDebugMode(
|
||||
config.qmdl_store_path.clone(),
|
||||
))
|
||||
}
|
||||
} else if store_exists {
|
||||
match RecordingStore::load(&config.qmdl_store_path).await {
|
||||
@@ -97,7 +105,7 @@ async fn init_qmdl_store(config: &config::Config) -> Result<RecordingStore, Rayh
|
||||
error!("failed to parse QMDL manifest: {}", err);
|
||||
info!("creating new empty manifest...");
|
||||
Ok(RecordingStore::create(&config.qmdl_store_path).await?)
|
||||
},
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
} else {
|
||||
@@ -126,18 +134,24 @@ fn run_ctrl_c_thread(
|
||||
info!("Done!");
|
||||
}
|
||||
|
||||
server_shutdown_tx.send(())
|
||||
server_shutdown_tx
|
||||
.send(())
|
||||
.expect("couldn't send server shutdown signal");
|
||||
info!("sending UI shutdown");
|
||||
if let Some(ui_shutdown_tx) = maybe_ui_shutdown_tx {
|
||||
ui_shutdown_tx.send(())
|
||||
ui_shutdown_tx
|
||||
.send(())
|
||||
.expect("couldn't send ui shutdown signal");
|
||||
}
|
||||
diag_device_sender.send(DiagDeviceCtrlMessage::Exit).await
|
||||
diag_device_sender
|
||||
.send(DiagDeviceCtrlMessage::Exit)
|
||||
.await
|
||||
.expect("couldn't send Exit message to diag thread");
|
||||
analysis_tx.send(AnalysisCtrlMessage::Exit).await
|
||||
analysis_tx
|
||||
.send(AnalysisCtrlMessage::Exit)
|
||||
.await
|
||||
.expect("couldn't send Exit message to analysis thread");
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Unable to listen for shutdown signal: {}", err);
|
||||
}
|
||||
@@ -146,7 +160,6 @@ fn run_ctrl_c_thread(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), RayhunterError> {
|
||||
env_logger::init();
|
||||
@@ -167,21 +180,43 @@ async fn main() -> Result<(), RayhunterError> {
|
||||
if !config.debug_mode {
|
||||
let (ui_shutdown_tx, ui_shutdown_rx) = oneshot::channel();
|
||||
maybe_ui_shutdown_tx = Some(ui_shutdown_tx);
|
||||
let mut dev = DiagDevice::new().await
|
||||
let mut dev = DiagDevice::new()
|
||||
.await
|
||||
.map_err(RayhunterError::DiagInitError)?;
|
||||
dev.config_logs().await
|
||||
dev.config_logs()
|
||||
.await
|
||||
.map_err(RayhunterError::DiagInitError)?;
|
||||
|
||||
info!("Starting Diag Thread");
|
||||
run_diag_read_thread(&task_tracker, dev, rx, ui_update_tx.clone(), qmdl_store_lock.clone(), config.enable_dummy_analyzer);
|
||||
run_diag_read_thread(
|
||||
&task_tracker,
|
||||
dev,
|
||||
rx,
|
||||
ui_update_tx.clone(),
|
||||
qmdl_store_lock.clone(),
|
||||
config.enable_dummy_analyzer,
|
||||
);
|
||||
info!("Starting UI");
|
||||
display::update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx);
|
||||
}
|
||||
let (server_shutdown_tx, server_shutdown_rx) = oneshot::channel::<()>();
|
||||
info!("create shutdown thread");
|
||||
let analysis_status_lock = Arc::new(RwLock::new(AnalysisStatus::default()));
|
||||
run_analysis_thread(&task_tracker, analysis_rx, qmdl_store_lock.clone(), analysis_status_lock.clone(), config.enable_dummy_analyzer);
|
||||
run_ctrl_c_thread(&task_tracker, tx.clone(), server_shutdown_tx, maybe_ui_shutdown_tx, qmdl_store_lock.clone(), analysis_tx.clone());
|
||||
run_analysis_thread(
|
||||
&task_tracker,
|
||||
analysis_rx,
|
||||
qmdl_store_lock.clone(),
|
||||
analysis_status_lock.clone(),
|
||||
config.enable_dummy_analyzer,
|
||||
);
|
||||
run_ctrl_c_thread(
|
||||
&task_tracker,
|
||||
tx.clone(),
|
||||
server_shutdown_tx,
|
||||
maybe_ui_shutdown_tx,
|
||||
qmdl_store_lock.clone(),
|
||||
analysis_tx.clone(),
|
||||
);
|
||||
let state = Arc::new(ServerState {
|
||||
qmdl_store_lock: qmdl_store_lock.clone(),
|
||||
diag_device_ctrl_sender: tx,
|
||||
|
||||
+137
-37
@@ -6,21 +6,21 @@ use axum::extract::{Path, State};
|
||||
use axum::http::header::CONTENT_TYPE;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use log::{debug, error, info};
|
||||
use rayhunter::diag::DataType;
|
||||
use rayhunter::diag_device::DiagDevice;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use rayhunter::qmdl::QmdlWriter;
|
||||
use log::{debug, error, info};
|
||||
use tokio::fs::File;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_util::io::ReaderStream;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
|
||||
use crate::analysis::AnalysisWriter;
|
||||
use crate::display;
|
||||
use crate::qmdl_store::{RecordingStore, RecordingStoreError};
|
||||
use crate::server::ServerState;
|
||||
use crate::analysis::AnalysisWriter;
|
||||
|
||||
pub enum DiagDeviceCtrlMessage {
|
||||
StopRecording,
|
||||
@@ -119,35 +119,82 @@ pub fn run_diag_read_thread(
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn start_recording(State(state): State<Arc<ServerState>>) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
pub async fn start_recording(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
if state.debug_mode {
|
||||
return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string()));
|
||||
}
|
||||
let mut qmdl_store = state.qmdl_store_lock.write().await;
|
||||
let (qmdl_file, analysis_file) = qmdl_store.new_entry().await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't create new qmdl entry: {}", e)))?;
|
||||
let (qmdl_file, analysis_file) = qmdl_store.new_entry().await.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't create new qmdl entry: {}", e),
|
||||
)
|
||||
})?;
|
||||
let qmdl_writer = QmdlWriter::new(qmdl_file);
|
||||
state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StartRecording((qmdl_writer, analysis_file))).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?;
|
||||
state
|
||||
.diag_device_ctrl_sender
|
||||
.send(DiagDeviceCtrlMessage::StartRecording((
|
||||
qmdl_writer,
|
||||
analysis_file,
|
||||
)))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send stop recording message: {}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
let display_state = display::DisplayState::Recording;
|
||||
state.ui_update_sender.send(display_state).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?;
|
||||
state
|
||||
.ui_update_sender
|
||||
.send(display_state)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send ui update message: {}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((StatusCode::ACCEPTED, "ok".to_string()))
|
||||
}
|
||||
|
||||
pub async fn stop_recording(State(state): State<Arc<ServerState>>) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
pub async fn stop_recording(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
if state.debug_mode {
|
||||
return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string()));
|
||||
}
|
||||
let mut qmdl_store = state.qmdl_store_lock.write().await;
|
||||
qmdl_store.close_current_entry().await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't close current qmdl entry: {}", e)))?;
|
||||
state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?;
|
||||
state.ui_update_sender.send(display::DisplayState::Paused).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?;
|
||||
qmdl_store.close_current_entry().await.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't close current qmdl entry: {}", e),
|
||||
)
|
||||
})?;
|
||||
state
|
||||
.diag_device_ctrl_sender
|
||||
.send(DiagDeviceCtrlMessage::StopRecording)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send stop recording message: {}", e),
|
||||
)
|
||||
})?;
|
||||
state
|
||||
.ui_update_sender
|
||||
.send(display::DisplayState::Paused)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send ui update message: {}", e),
|
||||
)
|
||||
})?;
|
||||
Ok((StatusCode::ACCEPTED, "ok".to_string()))
|
||||
}
|
||||
|
||||
@@ -160,45 +207,98 @@ pub async fn delete_recording(
|
||||
}
|
||||
let mut qmdl_store = state.qmdl_store_lock.write().await;
|
||||
match qmdl_store.delete_entry(&qmdl_name).await {
|
||||
Err(RecordingStoreError::NoSuchEntryError) => return Err((StatusCode::BAD_REQUEST, format!("no recording with name {qmdl_name}"))),
|
||||
Err(e) => return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't delete recording: {e}"))),
|
||||
Ok(_) => {},
|
||||
Err(RecordingStoreError::NoSuchEntryError) => {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("no recording with name {qmdl_name}"),
|
||||
))
|
||||
}
|
||||
Err(e) => {
|
||||
return Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't delete recording: {e}"),
|
||||
))
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?;
|
||||
state.ui_update_sender.send(display::DisplayState::Paused).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?;
|
||||
state
|
||||
.diag_device_ctrl_sender
|
||||
.send(DiagDeviceCtrlMessage::StopRecording)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send stop recording message: {}", e),
|
||||
)
|
||||
})?;
|
||||
state
|
||||
.ui_update_sender
|
||||
.send(display::DisplayState::Paused)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send ui update message: {}", e),
|
||||
)
|
||||
})?;
|
||||
Ok((StatusCode::ACCEPTED, "ok".to_string()))
|
||||
}
|
||||
|
||||
pub async fn delete_all_recordings(State(state): State<Arc<ServerState>>) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
pub async fn delete_all_recordings(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
if state.debug_mode {
|
||||
return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string()));
|
||||
}
|
||||
let mut qmdl_store = state.qmdl_store_lock.write().await;
|
||||
qmdl_store.delete_all_entries().await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't delete all recordings: {}", e)))?;
|
||||
state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?;
|
||||
state.ui_update_sender.send(display::DisplayState::Paused).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?;
|
||||
qmdl_store.delete_all_entries().await.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't delete all recordings: {}", e),
|
||||
)
|
||||
})?;
|
||||
state
|
||||
.diag_device_ctrl_sender
|
||||
.send(DiagDeviceCtrlMessage::StopRecording)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send stop recording message: {}", e),
|
||||
)
|
||||
})?;
|
||||
state
|
||||
.ui_update_sender
|
||||
.send(display::DisplayState::Paused)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't send ui update message: {}", e),
|
||||
)
|
||||
})?;
|
||||
Ok((StatusCode::ACCEPTED, "ok".to_string()))
|
||||
}
|
||||
|
||||
pub async fn get_analysis_report(State(state): State<Arc<ServerState>>, Path(qmdl_name): Path<String>) -> Result<Response, (StatusCode, String)> {
|
||||
pub async fn get_analysis_report(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
Path(qmdl_name): Path<String>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let qmdl_store = state.qmdl_store_lock.read().await;
|
||||
let (entry_index, _) = if qmdl_name == "live" {
|
||||
qmdl_store.get_current_entry().ok_or((
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
"No QMDL data's being recorded to analyze, try starting a new recording!".to_string()
|
||||
"No QMDL data's being recorded to analyze, try starting a new recording!".to_string(),
|
||||
))?
|
||||
} else {
|
||||
qmdl_store.entry_for_name(&qmdl_name).ok_or((
|
||||
StatusCode::NOT_FOUND,
|
||||
format!("Couldn't find QMDL entry with name \"{}\"", qmdl_name)
|
||||
format!("Couldn't find QMDL entry with name \"{}\"", qmdl_name),
|
||||
))?
|
||||
};
|
||||
let analysis_file = qmdl_store.open_entry_analysis(entry_index).await
|
||||
let analysis_file = qmdl_store
|
||||
.open_entry_analysis(entry_index)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)))?;
|
||||
let analysis_stream = ReaderStream::new(analysis_file);
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::config;
|
||||
use crate::display::generic_framebuffer::{self, Dimensions, GenericFramebuffer};
|
||||
use crate::display::DisplayState;
|
||||
use crate::display::generic_framebuffer::{self, GenericFramebuffer, Dimensions};
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
const FB_PATH: &str = "/dev/fb0";
|
||||
@@ -20,10 +20,7 @@ impl GenericFramebuffer for Framebuffer {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_buffer(
|
||||
&mut self,
|
||||
buffer: &[(u8, u8, u8)],
|
||||
) {
|
||||
fn write_buffer(&mut self, buffer: &[(u8, u8, u8)]) {
|
||||
let mut raw_buffer = Vec::new();
|
||||
for (r, g, b) in buffer {
|
||||
let mut rgb565: u16 = (*r as u16 & 0b11111000) << 8;
|
||||
@@ -40,7 +37,7 @@ pub fn update_ui(
|
||||
task_tracker: &TaskTracker,
|
||||
config: &config::Config,
|
||||
ui_shutdown_rx: oneshot::Receiver<()>,
|
||||
ui_update_rx: Receiver<DisplayState>
|
||||
ui_update_rx: Receiver<DisplayState>,
|
||||
) {
|
||||
generic_framebuffer::update_ui(
|
||||
task_tracker,
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio::sync::oneshot;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
use crate::config;
|
||||
use crate::display::{DisplayState, tplink_onebit, tplink_framebuffer};
|
||||
use crate::display::{tplink_framebuffer, tplink_onebit, DisplayState};
|
||||
|
||||
use std::fs;
|
||||
|
||||
@@ -21,19 +21,9 @@ pub fn update_ui(
|
||||
|
||||
if fs::exists(tplink_onebit::OLED_PATH).unwrap_or_default() {
|
||||
info!("detected one-bit display");
|
||||
tplink_onebit::update_ui(
|
||||
task_tracker,
|
||||
config,
|
||||
ui_shutdown_rx,
|
||||
ui_update_rx
|
||||
)
|
||||
tplink_onebit::update_ui(task_tracker, config, ui_shutdown_rx, ui_update_rx)
|
||||
} else {
|
||||
info!("fallback to framebuffer");
|
||||
tplink_framebuffer::update_ui(
|
||||
task_tracker,
|
||||
config,
|
||||
ui_shutdown_rx,
|
||||
ui_update_rx
|
||||
)
|
||||
tplink_framebuffer::update_ui(task_tracker, config, ui_shutdown_rx, ui_update_rx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::io::Write;
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
use crate::config;
|
||||
use crate::display::generic_framebuffer::{self, Dimensions, GenericFramebuffer};
|
||||
use crate::display::DisplayState;
|
||||
use crate::display::generic_framebuffer::{self, GenericFramebuffer, Dimensions};
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
const FB_PATH: &str = "/dev/fb0";
|
||||
@@ -33,10 +33,7 @@ impl GenericFramebuffer for Framebuffer {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_buffer(
|
||||
&mut self,
|
||||
buffer: &[(u8, u8, u8)],
|
||||
) {
|
||||
fn write_buffer(&mut self, buffer: &[(u8, u8, u8)]) {
|
||||
// for how to write to the buffer, consult M7350v5_en_gpl/bootable/recovery/recovery_color_oled.c
|
||||
let dimensions = self.dimensions();
|
||||
let width = dimensions.width;
|
||||
@@ -65,7 +62,7 @@ impl GenericFramebuffer for Framebuffer {
|
||||
unsafe {
|
||||
let res = libc::ioctl(
|
||||
f.as_raw_fd(),
|
||||
0x4619, // FBIORECT_DISPLAY
|
||||
0x4619, // FBIORECT_DISPLAY
|
||||
&mut arg as *mut _,
|
||||
std::mem::size_of::<fb_fillrect>(),
|
||||
);
|
||||
@@ -81,7 +78,7 @@ pub fn update_ui(
|
||||
task_tracker: &TaskTracker,
|
||||
config: &config::Config,
|
||||
ui_shutdown_rx: oneshot::Receiver<()>,
|
||||
ui_update_rx: Receiver<DisplayState>
|
||||
ui_update_rx: Receiver<DisplayState>,
|
||||
) {
|
||||
generic_framebuffer::update_ui(
|
||||
task_tracker,
|
||||
|
||||
@@ -5,11 +5,11 @@ use rayhunter::telcom_parser::lte_rrc::{PCCH_MessageType, PCCH_MessageType_c1, P
|
||||
use rayhunter::analysis::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use rayhunter::analysis::information_element::{InformationElement, LteInformationElement};
|
||||
|
||||
pub struct TestAnalyzer{
|
||||
pub struct TestAnalyzer {
|
||||
pub count: i32,
|
||||
}
|
||||
|
||||
impl Analyzer for TestAnalyzer{
|
||||
impl Analyzer for TestAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
Cow::from("Example Analyzer")
|
||||
}
|
||||
@@ -22,15 +22,15 @@ impl Analyzer for TestAnalyzer{
|
||||
self.count += 1;
|
||||
if self.count % 100 == 0 {
|
||||
return Some(Event {
|
||||
event_type: EventType::Informational ,
|
||||
event_type: EventType::Informational,
|
||||
message: "multiple of 100 events processed".to_string(),
|
||||
})
|
||||
});
|
||||
}
|
||||
let pcch_msg = match ie {
|
||||
InformationElement::LTE(lte_ie) => match &** lte_ie {
|
||||
InformationElement::LTE(lte_ie) => match &**lte_ie {
|
||||
LteInformationElement::PCCH(pcch_msg) => pcch_msg,
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
let PCCH_MessageType::C1(PCCH_MessageType_c1::Paging(paging)) = &pcch_msg.message else {
|
||||
@@ -39,9 +39,11 @@ impl Analyzer for TestAnalyzer{
|
||||
for record in &paging.paging_record_list.as_ref()?.0 {
|
||||
if let PagingUE_Identity::S_TMSI(_) = record.ue_identity {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::Low },
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::Low,
|
||||
},
|
||||
message: "TMSI was provided to cell".to_string(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
use thiserror::Error;
|
||||
use rayhunter::diag_device::DiagDeviceError;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::qmdl_store::RecordingStoreError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RayhunterError{
|
||||
pub enum RayhunterError {
|
||||
#[error("Config file parsing error: {0}")]
|
||||
ConfigFileParsingError(#[from] toml::de::Error),
|
||||
#[error("Diag intialization error: {0}")]
|
||||
|
||||
+34
-20
@@ -1,36 +1,43 @@
|
||||
use crate::ServerState;
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::header::CONTENT_TYPE;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use futures::TryStreamExt;
|
||||
use log::error;
|
||||
use rayhunter::diag::DataType;
|
||||
use rayhunter::gsmtap_parser;
|
||||
use rayhunter::pcap::GsmtapPcapWriter;
|
||||
use rayhunter::qmdl::QmdlReader;
|
||||
use axum::body::Body;
|
||||
use axum::http::header::CONTENT_TYPE;
|
||||
use axum::extract::{State, Path};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{Response, IntoResponse};
|
||||
use std::sync::Arc;
|
||||
use std::{future, pin::pin};
|
||||
use tokio::io::duplex;
|
||||
use tokio_util::io::ReaderStream;
|
||||
use std::{future, pin::pin};
|
||||
use std::sync::Arc;
|
||||
use log::error;
|
||||
use futures::TryStreamExt;
|
||||
|
||||
// Streams a pcap file chunk-by-chunk to the client by reading the QMDL data
|
||||
// written so far. This is done by spawning a thread which streams chunks of
|
||||
// pcap data to a channel that's piped to the client.
|
||||
pub async fn get_pcap(State(state): State<Arc<ServerState>>, Path(qmdl_name): Path<String>) -> Result<Response, (StatusCode, String)> {
|
||||
pub async fn get_pcap(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
Path(qmdl_name): Path<String>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let qmdl_store = state.qmdl_store_lock.read().await;
|
||||
let (entry_index, entry) = qmdl_store.entry_for_name(&qmdl_name)
|
||||
.ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_name)))?;
|
||||
let (entry_index, entry) = qmdl_store.entry_for_name(&qmdl_name).ok_or((
|
||||
StatusCode::NOT_FOUND,
|
||||
format!("couldn't find qmdl file with name {}", qmdl_name),
|
||||
))?;
|
||||
if entry.qmdl_size_bytes == 0 {
|
||||
return Err((
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
"QMDL file is empty, try again in a bit!".to_string()
|
||||
"QMDL file is empty, try again in a bit!".to_string(),
|
||||
));
|
||||
}
|
||||
let qmdl_size_bytes = entry.qmdl_size_bytes;
|
||||
let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await
|
||||
let qmdl_file = qmdl_store
|
||||
.open_entry_qmdl(entry_index)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)))?;
|
||||
// the QMDL reader should stop at the last successfully written data chunk
|
||||
// (entry.size_bytes)
|
||||
@@ -40,20 +47,27 @@ pub async fn get_pcap(State(state): State<Arc<ServerState>>, Path(qmdl_name): Pa
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut reader = QmdlReader::new(qmdl_file, Some(qmdl_size_bytes));
|
||||
let mut messages_stream = pin!(reader.as_stream()
|
||||
let mut messages_stream = pin!(reader
|
||||
.as_stream()
|
||||
.try_filter(|container| future::ready(container.data_type == DataType::UserSpace)));
|
||||
|
||||
while let Some(container) = messages_stream.try_next().await.expect("failed getting QMDL container") {
|
||||
while let Some(container) = messages_stream
|
||||
.try_next()
|
||||
.await
|
||||
.expect("failed getting QMDL container")
|
||||
{
|
||||
for maybe_msg in container.into_messages() {
|
||||
match maybe_msg {
|
||||
Ok(msg) => {
|
||||
let maybe_gsmtap_msg = gsmtap_parser::parse(msg)
|
||||
.expect("error parsing gsmtap message");
|
||||
let maybe_gsmtap_msg =
|
||||
gsmtap_parser::parse(msg).expect("error parsing gsmtap message");
|
||||
if let Some((timestamp, gsmtap_msg)) = maybe_gsmtap_msg {
|
||||
pcap_writer.write_gsmtap_message(gsmtap_msg, timestamp).await
|
||||
pcap_writer
|
||||
.write_gsmtap_message(gsmtap_msg, timestamp)
|
||||
.await
|
||||
.expect("error writing pcap packet");
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => error!("error parsing message: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
+11
-12
@@ -1,11 +1,11 @@
|
||||
use rayhunter::util::RuntimeMetadata;
|
||||
use chrono::{DateTime, Local};
|
||||
use rayhunter::util::RuntimeMetadata;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
fs::{self, try_exists, File, OpenOptions},
|
||||
io::AsyncWriteExt
|
||||
io::AsyncWriteExt,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -127,7 +127,7 @@ impl RecordingStore {
|
||||
let mut store = RecordingStore {
|
||||
path: path.as_ref().to_owned(),
|
||||
manifest: Manifest {
|
||||
entries: Vec::new()
|
||||
entries: Vec::new(),
|
||||
},
|
||||
current_entry: None,
|
||||
};
|
||||
@@ -171,10 +171,7 @@ impl RecordingStore {
|
||||
}
|
||||
|
||||
// Returns the corresponding QMDL file for a given entry
|
||||
pub async fn open_entry_qmdl(
|
||||
&self,
|
||||
entry_index: usize,
|
||||
) -> Result<File, RecordingStoreError> {
|
||||
pub async fn open_entry_qmdl(&self, entry_index: usize) -> Result<File, RecordingStoreError> {
|
||||
let entry = &self.manifest.entries[entry_index];
|
||||
File::open(entry.get_qmdl_filepath(&self.path))
|
||||
.await
|
||||
@@ -203,8 +200,7 @@ impl RecordingStore {
|
||||
.open(entry.get_analysis_filepath(&self.path))
|
||||
.await
|
||||
.map_err(RecordingStoreError::ReadFileError)?;
|
||||
self.update_entry_analysis_size(entry_index, 0)
|
||||
.await?;
|
||||
self.update_entry_analysis_size(entry_index, 0).await?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
@@ -253,7 +249,8 @@ impl RecordingStore {
|
||||
.await
|
||||
.map_err(RecordingStoreError::WriteManifestError)?;
|
||||
|
||||
fs::rename(tmp_path, self.path.join("manifest.toml")).await
|
||||
fs::rename(tmp_path, self.path.join("manifest.toml"))
|
||||
.await
|
||||
.map_err(RecordingStoreError::WriteManifestError)?;
|
||||
|
||||
Ok(())
|
||||
@@ -261,7 +258,8 @@ impl RecordingStore {
|
||||
|
||||
// Finds an entry by filename
|
||||
pub fn entry_for_name(&self, name: &str) -> Option<(usize, &ManifestEntry)> {
|
||||
let entry_index = self.manifest
|
||||
let entry_index = self
|
||||
.manifest
|
||||
.entries
|
||||
.iter()
|
||||
.position(|entry| entry.name == name)?;
|
||||
@@ -274,7 +272,8 @@ impl RecordingStore {
|
||||
}
|
||||
|
||||
pub async fn delete_entry(&mut self, name: &str) -> Result<ManifestEntry, RecordingStoreError> {
|
||||
let entry_to_delete_idx = self.manifest
|
||||
let entry_to_delete_idx = self
|
||||
.manifest
|
||||
.entries
|
||||
.iter()
|
||||
.position(|entry| entry.name == name)
|
||||
|
||||
+30
-16
@@ -1,20 +1,20 @@
|
||||
use axum::body::Body;
|
||||
use axum::http::header::{self, CONTENT_LENGTH, CONTENT_TYPE};
|
||||
use axum::extract::State;
|
||||
use axum::http::{StatusCode, HeaderValue};
|
||||
use axum::response::{Response, IntoResponse};
|
||||
use axum::extract::Path;
|
||||
use axum::extract::State;
|
||||
use axum::http::header::{self, CONTENT_LENGTH, CONTENT_TYPE};
|
||||
use axum::http::{HeaderValue, StatusCode};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use include_dir::{include_dir, Dir};
|
||||
use std::sync::Arc;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_util::io::ReaderStream;
|
||||
use include_dir::{include_dir, Dir};
|
||||
|
||||
use crate::{display, DiagDeviceCtrlMessage};
|
||||
use crate::analysis::{AnalysisCtrlMessage, AnalysisStatus};
|
||||
use crate::qmdl_store::RecordingStore;
|
||||
use crate::{display, DiagDeviceCtrlMessage};
|
||||
|
||||
pub struct ServerState {
|
||||
pub qmdl_store_lock: Arc<RwLock<RecordingStore>>,
|
||||
@@ -25,13 +25,22 @@ pub struct ServerState {
|
||||
pub debug_mode: bool,
|
||||
}
|
||||
|
||||
pub async fn get_qmdl(State(state): State<Arc<ServerState>>, Path(qmdl_name): Path<String>) -> Result<Response, (StatusCode, String)> {
|
||||
pub async fn get_qmdl(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
Path(qmdl_name): Path<String>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let qmdl_idx = qmdl_name.trim_end_matches(".qmdl");
|
||||
let qmdl_store = state.qmdl_store_lock.read().await;
|
||||
let (entry_index, entry) = qmdl_store.entry_for_name(qmdl_idx)
|
||||
.ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_idx)))?;
|
||||
let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("error opening QMDL file: {}", e)))?;
|
||||
let (entry_index, entry) = qmdl_store.entry_for_name(qmdl_idx).ok_or((
|
||||
StatusCode::NOT_FOUND,
|
||||
format!("couldn't find qmdl file with name {}", qmdl_idx),
|
||||
))?;
|
||||
let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("error opening QMDL file: {}", e),
|
||||
)
|
||||
})?;
|
||||
let limited_qmdl_file = qmdl_file.take(entry.qmdl_size_bytes as u64);
|
||||
let qmdl_stream = ReaderStream::new(limited_qmdl_file);
|
||||
|
||||
@@ -46,7 +55,10 @@ pub async fn get_qmdl(State(state): State<Arc<ServerState>>, Path(qmdl_name): Pa
|
||||
// Bundles the server's static files (html/css/js) into the binary for easy distribution
|
||||
static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static");
|
||||
|
||||
pub async fn serve_static(State(state): State<Arc<ServerState>>, Path(path): Path<String>) -> impl IntoResponse {
|
||||
pub async fn serve_static(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
Path(path): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
let path = path.trim_start_matches('/');
|
||||
let mime_type = mime_guess::from_path(path).first_or_text_plain();
|
||||
|
||||
@@ -62,7 +74,9 @@ pub async fn serve_static(State(state): State<Arc<ServerState>>, Path(path): Pat
|
||||
return match File::open(build_path).await {
|
||||
Ok(mut file) => {
|
||||
let mut body = String::new();
|
||||
file.read_to_string(&mut body).await.expect("failed to read file");
|
||||
file.read_to_string(&mut body)
|
||||
.await
|
||||
.expect("failed to read file");
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(
|
||||
@@ -71,11 +85,11 @@ pub async fn serve_static(State(state): State<Arc<ServerState>>, Path(path): Pat
|
||||
)
|
||||
.body(Body::from(body))
|
||||
.unwrap()
|
||||
},
|
||||
}
|
||||
Err(_) => Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::empty())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+20
-9
@@ -3,9 +3,9 @@ use std::sync::Arc;
|
||||
use crate::qmdl_store::ManifestEntry;
|
||||
use crate::server::ServerState;
|
||||
|
||||
use axum::Json;
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::Json;
|
||||
use log::error;
|
||||
use serde::Serialize;
|
||||
use tokio::process::Command;
|
||||
@@ -65,10 +65,16 @@ pub struct MemoryStats {
|
||||
// runs the given command and returns its stdout as a string
|
||||
async fn get_cmd_output(mut cmd: Command) -> Result<String, String> {
|
||||
let cmd_str = format!("{:?}", &cmd);
|
||||
let output = cmd.output().await
|
||||
let output = cmd
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("error running command {}: {}", &cmd_str, e))?;
|
||||
if !output.status.success() {
|
||||
return Err(format!("command {} failed with exit code {}", &cmd_str, output.status.code().unwrap()));
|
||||
return Err(format!(
|
||||
"command {} failed with exit code {}",
|
||||
&cmd_str,
|
||||
output.status.code().unwrap()
|
||||
));
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
}
|
||||
@@ -79,7 +85,8 @@ impl MemoryStats {
|
||||
let mut free_cmd = Command::new("free");
|
||||
free_cmd.arg("-k");
|
||||
let stdout = get_cmd_output(free_cmd).await?;
|
||||
let mut numbers = stdout.split_whitespace()
|
||||
let mut numbers = stdout
|
||||
.split_whitespace()
|
||||
.flat_map(|part| part.parse::<usize>());
|
||||
Ok(Self {
|
||||
total: humanize_kb(numbers.next().ok_or("error parsing free output")?),
|
||||
@@ -91,13 +98,15 @@ impl MemoryStats {
|
||||
|
||||
// turns a number of kilobytes (like 28293) into a human-readable string (like "28.3M")
|
||||
fn humanize_kb(kb: usize) -> String {
|
||||
if kb < 1000{
|
||||
if kb < 1000 {
|
||||
return format!("{}K", kb);
|
||||
}
|
||||
format!("{:.1}M", kb as f64 / 1024.0)
|
||||
}
|
||||
|
||||
pub async fn get_system_stats(State(state): State<Arc<ServerState>>) -> Result<Json<SystemStats>, (StatusCode, String)> {
|
||||
pub async fn get_system_stats(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
) -> Result<Json<SystemStats>, (StatusCode, String)> {
|
||||
let qmdl_store = state.qmdl_store_lock.read().await;
|
||||
match SystemStats::new(qmdl_store.path.to_str().unwrap()).await {
|
||||
Ok(stats) => Ok(Json(stats)),
|
||||
@@ -105,9 +114,9 @@ pub async fn get_system_stats(State(state): State<Arc<ServerState>>) -> Result<J
|
||||
error!("error getting system stats: {}", err);
|
||||
Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"error getting system stats".to_string()
|
||||
"error getting system stats".to_string(),
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +126,9 @@ pub struct ManifestStats {
|
||||
pub current_entry: Option<ManifestEntry>,
|
||||
}
|
||||
|
||||
pub async fn get_qmdl_manifest(State(state): State<Arc<ServerState>>) -> Result<Json<ManifestStats>, (StatusCode, String)> {
|
||||
pub async fn get_qmdl_manifest(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
) -> Result<Json<ManifestStats>, (StatusCode, String)> {
|
||||
let qmdl_store = state.qmdl_store_lock.read().await;
|
||||
let mut entries = qmdl_store.manifest.entries.clone();
|
||||
let current_entry = qmdl_store.current_entry.map(|index| entries.remove(index));
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::borrow::Cow;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{diag::MessagesContainer, gsmtap_parser};
|
||||
use crate::util::RuntimeMetadata;
|
||||
use crate::{diag::MessagesContainer, gsmtap_parser};
|
||||
|
||||
use super::{
|
||||
imsi_requested::ImsiRequestedAnalyzer,
|
||||
information_element::InformationElement,
|
||||
connection_redirect_downgrade::ConnectionRedirect2GDowngradeAnalyzer,
|
||||
imsi_requested::ImsiRequestedAnalyzer, information_element::InformationElement,
|
||||
priority_2g_downgrade::LteSib6And7DowngradeAnalyzer,
|
||||
};
|
||||
|
||||
@@ -118,14 +117,16 @@ impl Default for Harness {
|
||||
|
||||
impl Harness {
|
||||
pub fn new() -> Self {
|
||||
Self { analyzers: Vec::new() }
|
||||
Self {
|
||||
analyzers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_all_analyzers() -> Self {
|
||||
let mut harness = Harness::new();
|
||||
harness.add_analyzer(Box::new(ImsiRequestedAnalyzer::new()));
|
||||
harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer{}));
|
||||
harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{}));
|
||||
harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer {}));
|
||||
harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer {}));
|
||||
|
||||
// FIXME: our RRC parser is reporting false positives for this due to an
|
||||
// upstream hampi bug (https://github.com/ystero-dev/hampi/issues/133).
|
||||
@@ -186,19 +187,22 @@ impl Harness {
|
||||
}
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec<Option<Event>> {
|
||||
self.analyzers.iter_mut()
|
||||
self.analyzers
|
||||
.iter_mut()
|
||||
.map(|analyzer| analyzer.analyze_information_element(ie))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_names(&self) -> Vec<Cow<'_, str>> {
|
||||
self.analyzers.iter()
|
||||
self.analyzers
|
||||
.iter()
|
||||
.map(|analyzer| analyzer.get_name())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_descriptions(&self) -> Vec<Cow<'_, str>> {
|
||||
self.analyzers.iter()
|
||||
self.analyzers
|
||||
.iter()
|
||||
.map(|analyzer| analyzer.get_description())
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ use std::borrow::Cow;
|
||||
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
use telcom_parser::lte_rrc::{DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions, RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo};
|
||||
use super::util::unpack;
|
||||
use telcom_parser::lte_rrc::{
|
||||
DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions,
|
||||
RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo,
|
||||
};
|
||||
|
||||
// Based on HITBSecConf presentation "Forcing a targeted LTE cellphone into an
|
||||
// eavesdropping network" by Lin Huang
|
||||
pub struct ConnectionRedirect2GDowngradeAnalyzer {
|
||||
}
|
||||
pub struct ConnectionRedirect2GDowngradeAnalyzer {}
|
||||
|
||||
// TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones
|
||||
impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer {
|
||||
@@ -33,7 +35,9 @@ impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer {
|
||||
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 },
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: "Detected 2G downgrade".to_owned(),
|
||||
}),
|
||||
_ => Some(Event {
|
||||
|
||||
@@ -5,8 +5,7 @@ use telcom_parser::lte_rrc::{PCCH_MessageType, PCCH_MessageType_c1, PagingUE_Ide
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
|
||||
pub struct ImsiProvidedAnalyzer {
|
||||
}
|
||||
pub struct ImsiProvidedAnalyzer {}
|
||||
|
||||
impl Analyzer for ImsiProvidedAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
@@ -19,10 +18,10 @@ impl Analyzer for ImsiProvidedAnalyzer {
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
||||
let pcch_msg = match ie {
|
||||
InformationElement::LTE(lte_ie) => match &** lte_ie {
|
||||
InformationElement::LTE(lte_ie) => match &**lte_ie {
|
||||
LteInformationElement::PCCH(pcch_msg) => pcch_msg,
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
let PCCH_MessageType::C1(PCCH_MessageType_c1::Paging(paging)) = &pcch_msg.message else {
|
||||
@@ -31,9 +30,11 @@ impl Analyzer for ImsiProvidedAnalyzer {
|
||||
for record in &paging.paging_record_list.as_ref()?.0 {
|
||||
if let PagingUE_Identity::Imsi(_) = record.ue_identity {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: "IMSI was provided to cell".to_string(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
||||
InformationElement::LTE(inner) => match &**inner {
|
||||
LteInformationElement::NAS(payload) => payload,
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
||||
if self.packet_num < PACKET_THRESHHOLD {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::Medium
|
||||
severity: Severity::Medium,
|
||||
},
|
||||
message: format!(
|
||||
"NAS IMSI identity request detected, however it was within \
|
||||
@@ -53,15 +53,15 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
||||
turned your device on, this is likely a \
|
||||
false-positive.",
|
||||
PACKET_THRESHHOLD
|
||||
)
|
||||
})
|
||||
),
|
||||
});
|
||||
} else {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: "NAS IMSI identity request detected".to_owned(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! the term to refer to a structured, fully parsed message in any telcom
|
||||
//! standard.
|
||||
|
||||
use crate::gsmtap::{GsmtapMessage, GsmtapType, LteNasSubtype, LteRrcSubtype};
|
||||
use telcom_parser::{decode, lte_rrc};
|
||||
use thiserror::Error;
|
||||
use crate::gsmtap::{GsmtapMessage, GsmtapType, LteNasSubtype, LteRrcSubtype};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum InformationElementError {
|
||||
@@ -46,7 +46,6 @@ pub enum LteInformationElement {
|
||||
|
||||
// FIXME: actually parse NAS messages
|
||||
NAS(Vec<u8>),
|
||||
|
||||
// FIXME: unclear which message these "NB" types map to
|
||||
//DlCcchNb(),
|
||||
//DlDcchNb(),
|
||||
@@ -65,8 +64,8 @@ impl TryFrom<&GsmtapMessage> for InformationElement {
|
||||
fn try_from(gsmtap_msg: &GsmtapMessage) -> Result<Self, Self::Error> {
|
||||
match gsmtap_msg.header.gsmtap_type {
|
||||
GsmtapType::LteRrc(lte_rrc_subtype) => {
|
||||
use LteRrcSubtype as L;
|
||||
use LteInformationElement as R;
|
||||
use LteRrcSubtype as L;
|
||||
let lte = match lte_rrc_subtype {
|
||||
L::DlCcch => R::DlCcch(decode(&gsmtap_msg.payload)?),
|
||||
L::DlDcch => R::DlDcch(Box::new(decode(&gsmtap_msg.payload)?)),
|
||||
@@ -82,14 +81,20 @@ impl TryFrom<&GsmtapMessage> for InformationElement {
|
||||
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)),
|
||||
_ => {
|
||||
return Err(InformationElementError::UnsupportedGsmtapType(
|
||||
gsmtap_msg.header.gsmtap_type,
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(InformationElement::LTE(Box::new(lte)))
|
||||
},
|
||||
GsmtapType::LteNas(LteNasSubtype::Plain) => {
|
||||
Ok(InformationElement::LTE(Box::new(LteInformationElement::NAS(gsmtap_msg.payload.clone()))))
|
||||
},
|
||||
_ => Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)),
|
||||
}
|
||||
GsmtapType::LteNas(LteNasSubtype::Plain) => Ok(InformationElement::LTE(Box::new(
|
||||
LteInformationElement::NAS(gsmtap_msg.payload.clone()),
|
||||
))),
|
||||
_ => Err(InformationElementError::UnsupportedGsmtapType(
|
||||
gsmtap_msg.header.gsmtap_type,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
pub mod analyzer;
|
||||
pub mod information_element;
|
||||
pub mod priority_2g_downgrade;
|
||||
pub mod connection_redirect_downgrade;
|
||||
pub mod imsi_provided;
|
||||
pub mod imsi_requested;
|
||||
pub mod information_element;
|
||||
pub mod null_cipher;
|
||||
pub mod priority_2g_downgrade;
|
||||
pub mod util;
|
||||
|
||||
@@ -1,25 +1,41 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use telcom_parser::lte_rrc::{CipheringAlgorithm_r12, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions, RRCConnectionReconfigurationCriticalExtensions_c1, SCG_Configuration_r12, SecurityConfigHO_v1530HandoverType_v1530, SecurityModeCommand, SecurityModeCommandCriticalExtensions, SecurityModeCommandCriticalExtensions_c1};
|
||||
use telcom_parser::lte_rrc::{
|
||||
CipheringAlgorithm_r12, DL_DCCH_MessageType, DL_DCCH_MessageType_c1,
|
||||
RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions,
|
||||
RRCConnectionReconfigurationCriticalExtensions_c1, SCG_Configuration_r12,
|
||||
SecurityConfigHO_v1530HandoverType_v1530, SecurityModeCommand,
|
||||
SecurityModeCommandCriticalExtensions, SecurityModeCommandCriticalExtensions_c1,
|
||||
};
|
||||
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
|
||||
pub struct NullCipherAnalyzer {
|
||||
}
|
||||
pub struct NullCipherAnalyzer {}
|
||||
|
||||
impl NullCipherAnalyzer {
|
||||
fn check_rrc_connection_reconfiguration_cipher(&self, reconfiguration: &RRCConnectionReconfiguration) -> bool {
|
||||
let RRCConnectionReconfigurationCriticalExtensions::C1(c1) = &reconfiguration.critical_extensions else {
|
||||
fn check_rrc_connection_reconfiguration_cipher(
|
||||
&self,
|
||||
reconfiguration: &RRCConnectionReconfiguration,
|
||||
) -> bool {
|
||||
let RRCConnectionReconfigurationCriticalExtensions::C1(c1) =
|
||||
&reconfiguration.critical_extensions
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let RRCConnectionReconfigurationCriticalExtensions_c1::RrcConnectionReconfiguration_r8(c1) = c1 else {
|
||||
let RRCConnectionReconfigurationCriticalExtensions_c1::RrcConnectionReconfiguration_r8(c1) =
|
||||
c1
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if let Some(handover) = &c1.security_config_ho {
|
||||
let maybe_security_config = match &handover.handover_type {
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::IntraLTE(lte) => lte.security_algorithm_config.as_ref(),
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::InterRAT(rat) => Some(&rat.security_algorithm_config),
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::IntraLTE(lte) => {
|
||||
lte.security_algorithm_config.as_ref()
|
||||
}
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::InterRAT(rat) => {
|
||||
Some(&rat.security_algorithm_config)
|
||||
}
|
||||
};
|
||||
if let Some(security_config) = maybe_security_config {
|
||||
if security_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
@@ -28,7 +44,9 @@ impl NullCipherAnalyzer {
|
||||
}
|
||||
}
|
||||
// Use map/flatten to dig into a long chain of nested Option types
|
||||
let maybe_v1250 = c1.non_critical_extension.as_ref()
|
||||
let maybe_v1250 = c1
|
||||
.non_critical_extension
|
||||
.as_ref()
|
||||
.and_then(|v890| v890.non_critical_extension.as_ref())
|
||||
.and_then(|v920| v920.non_critical_extension.as_ref())
|
||||
.and_then(|v1020| v1020.non_critical_extension.as_ref())
|
||||
@@ -37,8 +55,11 @@ impl NullCipherAnalyzer {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Some(SCG_Configuration_r12::Setup(scg_setup)) = v1250.scg_configuration_r12.as_ref() {
|
||||
let maybe_cipher = scg_setup.scg_config_part_scg_r12.as_ref()
|
||||
if let Some(SCG_Configuration_r12::Setup(scg_setup)) = v1250.scg_configuration_r12.as_ref()
|
||||
{
|
||||
let maybe_cipher = scg_setup
|
||||
.scg_config_part_scg_r12
|
||||
.as_ref()
|
||||
.and_then(|scg| scg.mobility_control_info_scg_r12.as_ref())
|
||||
.and_then(|mci| mci.ciphering_algorithm_scg_r12.as_ref());
|
||||
if let Some(cipher) = maybe_cipher {
|
||||
@@ -48,7 +69,9 @@ impl NullCipherAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_v1530_security_config = v1250.non_critical_extension.as_ref()
|
||||
let maybe_v1530_security_config = v1250
|
||||
.non_critical_extension
|
||||
.as_ref()
|
||||
.and_then(|v1310| v1310.non_critical_extension.as_ref())
|
||||
.and_then(|v1430| v1430.non_critical_extension.as_ref())
|
||||
.and_then(|v1510| v1510.non_critical_extension.as_ref())
|
||||
@@ -57,9 +80,15 @@ impl NullCipherAnalyzer {
|
||||
return false;
|
||||
};
|
||||
let maybe_security_algorithm = match &v1530_security_config.handover_type_v1530 {
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Intra5GC(intra_5gc) => intra_5gc.security_algorithm_config_r15.as_ref(),
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Fivegc_ToEPC(to_epc) => Some(&to_epc.security_algorithm_config_r15),
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Epc_To5GC(to_5gc) => Some(&to_5gc.security_algorithm_config_r15),
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Intra5GC(intra_5gc) => {
|
||||
intra_5gc.security_algorithm_config_r15.as_ref()
|
||||
}
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Fivegc_ToEPC(to_epc) => {
|
||||
Some(&to_epc.security_algorithm_config_r15)
|
||||
}
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Epc_To5GC(to_5gc) => {
|
||||
Some(&to_5gc.security_algorithm_config_r15)
|
||||
}
|
||||
};
|
||||
if let Some(security_algorithm) = maybe_security_algorithm {
|
||||
if security_algorithm.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
@@ -76,7 +105,13 @@ impl NullCipherAnalyzer {
|
||||
let SecurityModeCommandCriticalExtensions_c1::SecurityModeCommand_r8(r8) = &c1 else {
|
||||
return false;
|
||||
};
|
||||
if r8.security_config_smc.security_algorithm_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
if r8
|
||||
.security_config_smc
|
||||
.security_algorithm_config
|
||||
.ciphering_algorithm
|
||||
.0
|
||||
== CipheringAlgorithm_r12::EEA0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
@@ -94,23 +129,29 @@ impl Analyzer for NullCipherAnalyzer {
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
||||
let dcch_msg = match ie {
|
||||
InformationElement::LTE(lte_ie) => match &** lte_ie {
|
||||
InformationElement::LTE(lte_ie) => match &**lte_ie {
|
||||
LteInformationElement::DlDcch(dcch_msg) => dcch_msg,
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
let DL_DCCH_MessageType::C1(c1) = &dcch_msg.message else {
|
||||
return None;
|
||||
};
|
||||
let null_cipher_detected = match c1 {
|
||||
DL_DCCH_MessageType_c1::RrcConnectionReconfiguration(reconfiguration) => self.check_rrc_connection_reconfiguration_cipher(reconfiguration),
|
||||
DL_DCCH_MessageType_c1::SecurityModeCommand(command) => self.check_security_mode_command_cipher(command),
|
||||
DL_DCCH_MessageType_c1::RrcConnectionReconfiguration(reconfiguration) => {
|
||||
self.check_rrc_connection_reconfiguration_cipher(reconfiguration)
|
||||
}
|
||||
DL_DCCH_MessageType_c1::SecurityModeCommand(command) => {
|
||||
self.check_security_mode_command_cipher(command)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
if null_cipher_detected {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: "Cell suggested use of null cipher".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,18 +2,29 @@ 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::{
|
||||
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 {
|
||||
}
|
||||
pub struct LteSib6And7DowngradeAnalyzer {}
|
||||
|
||||
impl LteSib6And7DowngradeAnalyzer {
|
||||
fn unpack_system_information<'a>(&self, ie: &'a InformationElement) -> Option<&'a SystemInformation_r8_IEsSib_TypeAndInfo> {
|
||||
fn unpack_system_information<'a>(
|
||||
&self,
|
||||
ie: &'a InformationElement,
|
||||
) -> Option<&'a SystemInformation_r8_IEsSib_TypeAndInfo> {
|
||||
if let InformationElement::LTE(lte_ie) = ie {
|
||||
if let LteInformationElement::BcchDlSch(bcch_dl_sch_message) = &**lte_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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -33,14 +44,19 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
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> {
|
||||
fn analyze_information_element(
|
||||
&mut self,
|
||||
ie: &InformationElement,
|
||||
) -> Option<super::analyzer::Event> {
|
||||
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 let Some(CellReselectionPriority(p)) =
|
||||
carrier_info.cell_reselection_priority
|
||||
{
|
||||
if p == 0 {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
@@ -52,7 +68,9 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
}
|
||||
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 let Some(CellReselectionPriority(p)) =
|
||||
carrier_info.cell_reselection_priority
|
||||
{
|
||||
if p == 0 {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
@@ -62,20 +80,31 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib7(SystemInformationBlockType7 { carrier_freqs_info_list: Some(carrier_info_list), .. }) => {
|
||||
}
|
||||
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 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(),
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message:
|
||||
"LTE cell advertised a 2G cell for priority 0 reselection"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// Unpacks a pattern, or returns None.
|
||||
//
|
||||
// # Examples
|
||||
@@ -24,7 +23,9 @@
|
||||
//
|
||||
macro_rules! unpack {
|
||||
($pat:pat = $val:expr) => {
|
||||
let $pat = $val else { return None; };
|
||||
let $pat = $val else {
|
||||
return None;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+88
-66
@@ -5,7 +5,7 @@ use crc::{Algorithm, Crc};
|
||||
use deku::prelude::*;
|
||||
|
||||
use crate::hdlc::{self, hdlc_decapsulate};
|
||||
use log::{warn, error};
|
||||
use log::{error, warn};
|
||||
use thiserror::Error;
|
||||
|
||||
pub const MESSAGE_TERMINATOR: u8 = 0x7e;
|
||||
@@ -42,7 +42,7 @@ pub enum LogConfigRequest {
|
||||
log_type: u32,
|
||||
log_mask_bitsize: u32,
|
||||
log_mask: Vec<u8>,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
|
||||
@@ -93,13 +93,19 @@ impl MessagesContainer {
|
||||
Ok(data) => match Message::from_bytes((&data, 0)) {
|
||||
Ok(((leftover_bytes, _), res)) => {
|
||||
if !leftover_bytes.is_empty() {
|
||||
warn!("warning: {} leftover bytes when parsing Message", leftover_bytes.len());
|
||||
warn!(
|
||||
"warning: {} leftover bytes when parsing Message",
|
||||
leftover_bytes.len()
|
||||
);
|
||||
}
|
||||
result.push(Ok(res));
|
||||
},
|
||||
}
|
||||
Err(e) => result.push(Err(DiagParsingError::MessageParsingError(e, data))),
|
||||
},
|
||||
Err(err) => result.push(Err(DiagParsingError::HdlcDecapsulationError(err, sub_msg.to_vec()))),
|
||||
Err(err) => result.push(Err(DiagParsingError::HdlcDecapsulationError(
|
||||
err,
|
||||
sub_msg.to_vec(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +177,7 @@ pub enum LogBody {
|
||||
msg: Vec<u8>,
|
||||
},
|
||||
#[deku(id = "0xb0c0")]
|
||||
LteRrcOtaMessage{
|
||||
LteRrcOtaMessage {
|
||||
ext_header_version: u8,
|
||||
#[deku(ctx = "*ext_header_version")]
|
||||
packet: LteRrcOtaPacket,
|
||||
@@ -210,7 +216,7 @@ pub enum LogBody {
|
||||
NrRrcOtaMessage {
|
||||
#[deku(count = "hdr_len")]
|
||||
msg: Vec<u8>,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
|
||||
@@ -364,15 +370,17 @@ pub enum ResponsePayload {
|
||||
#[deku(ctx = "subopcode: u32", id = "subopcode")]
|
||||
pub enum LogConfigResponse {
|
||||
#[deku(id = "1")]
|
||||
RetrieveIdRanges {
|
||||
log_mask_sizes: [u32; 16],
|
||||
},
|
||||
RetrieveIdRanges { log_mask_sizes: [u32; 16] },
|
||||
|
||||
#[deku(id = "3")]
|
||||
SetMask,
|
||||
}
|
||||
|
||||
pub fn build_log_mask_request(log_type: u32, log_mask_bitsize: u32, accepted_log_codes: &[u32]) -> Request {
|
||||
pub fn build_log_mask_request(
|
||||
log_type: u32,
|
||||
log_mask_bitsize: u32,
|
||||
accepted_log_codes: &[u32],
|
||||
) -> Request {
|
||||
let mut current_byte: u8 = 0;
|
||||
let mut num_bits_written: u8 = 0;
|
||||
let mut log_mask: Vec<u8> = vec![];
|
||||
@@ -413,31 +421,35 @@ mod test {
|
||||
log_mask_bitsize: 0,
|
||||
log_mask: vec![],
|
||||
});
|
||||
assert_eq!(req.to_bytes().unwrap(), vec![
|
||||
115, 0, 0, 0,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
]);
|
||||
assert_eq!(
|
||||
req.to_bytes().unwrap(),
|
||||
vec![115, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_log_mask_request() {
|
||||
let log_type = 11;
|
||||
let bitsize = 513;
|
||||
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 {
|
||||
let req = build_log_mask_request(
|
||||
log_type,
|
||||
log_mask_bitsize: bitsize,
|
||||
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, 0x1, 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,
|
||||
],
|
||||
}));
|
||||
bitsize,
|
||||
&crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING,
|
||||
);
|
||||
assert_eq!(
|
||||
req,
|
||||
Request::LogConfig(LogConfigRequest::SetMask {
|
||||
log_type,
|
||||
log_mask_bitsize: bitsize,
|
||||
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, 0x1, 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,
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -448,53 +460,53 @@ mod test {
|
||||
mdm_field: -1,
|
||||
hdlc_encapsulated_request: vec![1, 2, 3, 4],
|
||||
};
|
||||
assert_eq!(req.to_bytes().unwrap(), vec![
|
||||
32, 0, 0, 0,
|
||||
1, 2, 3, 4,
|
||||
]);
|
||||
assert_eq!(req.to_bytes().unwrap(), vec![32, 0, 0, 0, 1, 2, 3, 4,]);
|
||||
let req = RequestContainer {
|
||||
data_type: DataType::UserSpace,
|
||||
use_mdm: true,
|
||||
mdm_field: -1,
|
||||
hdlc_encapsulated_request: vec![1, 2, 3, 4],
|
||||
};
|
||||
assert_eq!(req.to_bytes().unwrap(), vec![
|
||||
32, 0, 0, 0,
|
||||
255, 255, 255, 255,
|
||||
1, 2, 3, 4,
|
||||
]);
|
||||
assert_eq!(
|
||||
req.to_bytes().unwrap(),
|
||||
vec![32, 0, 0, 0, 255, 255, 255, 255, 1, 2, 3, 4,]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logs() {
|
||||
let data = vec![
|
||||
16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20,
|
||||
14, 48, 0, 160, 0, 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1,
|
||||
238, 173, 213, 77, 208
|
||||
16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20, 14, 48, 0, 160, 0,
|
||||
2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1, 238, 173, 213, 77, 208,
|
||||
];
|
||||
let msg = Message::from_bytes((&data, 0)).unwrap().1;
|
||||
assert_eq!(msg, Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
log_type: 0xb0c0,
|
||||
timestamp: Timestamp { ts: 72659535985485082 },
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 20,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 48,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 160,
|
||||
earfcn: 2050,
|
||||
sfn_subfn: 4057,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0],
|
||||
assert_eq!(
|
||||
msg,
|
||||
Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
log_type: 0xb0c0,
|
||||
timestamp: Timestamp {
|
||||
ts: 72659535985485082
|
||||
},
|
||||
},
|
||||
});
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 20,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 48,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 160,
|
||||
earfcn: 2050,
|
||||
sfn_subfn: 4057,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0],
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn make_container(data_type: DataType, message: HdlcEncapsulatedMessage) -> MessagesContainer {
|
||||
@@ -515,7 +527,9 @@ mod test {
|
||||
outer_length: length_with_payload,
|
||||
inner_length: length_with_payload,
|
||||
log_type: 0xb0c0,
|
||||
timestamp: Timestamp { ts: 72659535985485082 },
|
||||
timestamp: Timestamp {
|
||||
ts: 72659535985485082,
|
||||
},
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 20,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
@@ -532,7 +546,9 @@ mod test {
|
||||
},
|
||||
},
|
||||
};
|
||||
let serialized = message.to_bytes().expect("failed to serialize test message");
|
||||
let serialized = message
|
||||
.to_bytes()
|
||||
.expect("failed to serialize test message");
|
||||
let encapsulated_data = hdlc::hdlc_encapsulate(&serialized, &CRC_CCITT);
|
||||
let encapsulated = HdlcEncapsulatedMessage {
|
||||
len: encapsulated_data.len() as u32,
|
||||
@@ -574,7 +590,10 @@ mod test {
|
||||
container.num_messages += 1;
|
||||
let result = container.into_messages();
|
||||
assert_eq!(result[0], Ok(message1));
|
||||
assert!(matches!(result[1], Err(DiagParsingError::MessageParsingError(_, _))));
|
||||
assert!(matches!(
|
||||
result[1],
|
||||
Err(DiagParsingError::MessageParsingError(_, _))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -589,6 +608,9 @@ mod test {
|
||||
container.num_messages += 1;
|
||||
let result = container.into_messages();
|
||||
assert_eq!(result[0], Ok(message1));
|
||||
assert!(matches!(result[1], Err(DiagParsingError::HdlcDecapsulationError(_, _))));
|
||||
assert!(matches!(
|
||||
result[1],
|
||||
Err(DiagParsingError::HdlcDecapsulationError(_, _))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+60
-32
@@ -1,13 +1,16 @@
|
||||
use crate::diag::{
|
||||
build_log_mask_request, DataType, DiagParsingError, LogConfigRequest, LogConfigResponse,
|
||||
Message, MessagesContainer, Request, RequestContainer, ResponsePayload, CRC_CCITT,
|
||||
};
|
||||
use crate::hdlc::hdlc_encapsulate;
|
||||
use crate::diag::{build_log_mask_request, DataType, DiagParsingError, LogConfigRequest, LogConfigResponse, Message, MessagesContainer, Request, RequestContainer, ResponsePayload, CRC_CCITT};
|
||||
use crate::log_codes;
|
||||
|
||||
use deku::prelude::*;
|
||||
use futures_core::TryStream;
|
||||
use log::{error, info};
|
||||
use std::io::ErrorKind;
|
||||
use std::os::fd::AsRawFd;
|
||||
use futures_core::TryStream;
|
||||
use thiserror::Error;
|
||||
use log::{info, error};
|
||||
use deku::prelude::*;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
@@ -38,22 +41,19 @@ pub enum DiagDeviceError {
|
||||
pub const LOG_CODES_FOR_RAW_PACKET_LOGGING: [u32; 11] = [
|
||||
// Layer 2:
|
||||
log_codes::LOG_GPRS_MAC_SIGNALLING_MESSAGE_C, // 0x5226
|
||||
|
||||
// Layer 3:
|
||||
log_codes::LOG_GSM_RR_SIGNALING_MESSAGE_C, // 0x512f
|
||||
log_codes::WCDMA_SIGNALLING_MESSAGE, // 0x412f
|
||||
log_codes::LOG_LTE_RRC_OTA_MSG_LOG_C, // 0xb0c0
|
||||
log_codes::LOG_NR_RRC_OTA_MSG_LOG_C, // 0xb821
|
||||
|
||||
log_codes::WCDMA_SIGNALLING_MESSAGE, // 0x412f
|
||||
log_codes::LOG_LTE_RRC_OTA_MSG_LOG_C, // 0xb0c0
|
||||
log_codes::LOG_NR_RRC_OTA_MSG_LOG_C, // 0xb821
|
||||
// NAS:
|
||||
log_codes::LOG_UMTS_NAS_OTA_MESSAGE_LOG_PACKET_C, // 0x713a
|
||||
log_codes::LOG_LTE_NAS_ESM_OTA_IN_MSG_LOG_C, // 0xb0e2
|
||||
log_codes::LOG_LTE_NAS_ESM_OTA_OUT_MSG_LOG_C, // 0xb0e3
|
||||
log_codes::LOG_LTE_NAS_EMM_OTA_IN_MSG_LOG_C, // 0xb0ec
|
||||
log_codes::LOG_LTE_NAS_EMM_OTA_OUT_MSG_LOG_C, // 0xb0ed
|
||||
|
||||
log_codes::LOG_LTE_NAS_ESM_OTA_IN_MSG_LOG_C, // 0xb0e2
|
||||
log_codes::LOG_LTE_NAS_ESM_OTA_OUT_MSG_LOG_C, // 0xb0e3
|
||||
log_codes::LOG_LTE_NAS_EMM_OTA_IN_MSG_LOG_C, // 0xb0ec
|
||||
log_codes::LOG_LTE_NAS_EMM_OTA_OUT_MSG_LOG_C, // 0xb0ed
|
||||
// User IP traffic:
|
||||
log_codes::LOG_DATA_PROTOCOL_LOGGING_C // 0x11eb
|
||||
log_codes::LOG_DATA_PROTOCOL_LOGGING_C, // 0x11eb
|
||||
];
|
||||
|
||||
const BUFFER_LEN: usize = 1024 * 1024 * 10;
|
||||
@@ -68,9 +68,9 @@ const DIAG_IOCTL_REMOTE_DEV: u64 = 32;
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
const DIAG_IOCTL_SWITCH_LOGGING: u32 = 7;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const DIAG_IOCTL_SWITCH_LOGGING: u64 = 7;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const DIAG_IOCTL_SWITCH_LOGGING: u64 = 7;
|
||||
|
||||
pub struct DiagDevice {
|
||||
@@ -99,7 +99,9 @@ impl DiagDevice {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_stream(&mut self) -> impl TryStream<Ok = MessagesContainer, Error = DiagDeviceError> + '_ {
|
||||
pub fn as_stream(
|
||||
&mut self,
|
||||
) -> impl TryStream<Ok = MessagesContainer, Error = DiagDeviceError> + '_ {
|
||||
futures::stream::try_unfold(self, |dev| async {
|
||||
let container = dev.get_next_messages_container().await?;
|
||||
Ok(Some((container, dev)))
|
||||
@@ -110,11 +112,18 @@ impl DiagDevice {
|
||||
let mut bytes_read = 0;
|
||||
// TP-Link M7350 sometimes sends too small messages, we need to be able to deal with short reads.
|
||||
while bytes_read <= 8 {
|
||||
bytes_read = self.file.read(&mut self.read_buf).await
|
||||
bytes_read = self
|
||||
.file
|
||||
.read(&mut self.read_buf)
|
||||
.await
|
||||
.map_err(DiagDeviceError::DeviceReadFailed)?;
|
||||
}
|
||||
|
||||
info!("Parsing messages container size = {:?} [{:?}]", bytes_read, &self.read_buf[0..bytes_read]);
|
||||
info!(
|
||||
"Parsing messages container size = {:?} [{:?}]",
|
||||
bytes_read,
|
||||
&self.read_buf[0..bytes_read]
|
||||
);
|
||||
|
||||
match MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0)) {
|
||||
Ok((_, container)) => return Ok(container),
|
||||
@@ -129,7 +138,9 @@ impl DiagDevice {
|
||||
use_mdm: self.use_mdm > 0,
|
||||
mdm_field: -1,
|
||||
hdlc_encapsulated_request: hdlc_encapsulate(req_bytes, &CRC_CCITT),
|
||||
}.to_bytes().expect("Failed to serialize RequestContainer");
|
||||
}
|
||||
.to_bytes()
|
||||
.expect("Failed to serialize RequestContainer");
|
||||
if let Err(err) = self.file.write(&buf).await {
|
||||
// For reasons I don't entirely understand, calls to write(2) on
|
||||
// /dev/diag always return 0 bytes written, though the written
|
||||
@@ -164,13 +175,17 @@ impl DiagDevice {
|
||||
for msg in self.read_response().await? {
|
||||
match msg {
|
||||
Ok(Message::Log { .. }) => info!("skipping log response..."),
|
||||
Ok(Message::Response { payload, status, .. }) => match payload {
|
||||
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
|
||||
Ok(Message::Response {
|
||||
payload, status, ..
|
||||
}) => match payload {
|
||||
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges {
|
||||
log_mask_sizes,
|
||||
}) => {
|
||||
if status != 0 {
|
||||
return Err(DiagDeviceError::RequestFailed(status, req));
|
||||
}
|
||||
return Ok(log_mask_sizes);
|
||||
},
|
||||
}
|
||||
_ => info!("skipping non-LogConfigResponse response..."),
|
||||
},
|
||||
Err(e) => error!("error parsing message: {:?}", e),
|
||||
@@ -181,20 +196,26 @@ impl DiagDevice {
|
||||
}
|
||||
|
||||
async fn set_log_mask(&mut self, log_type: u32, log_mask_bitsize: u32) -> DiagResult<()> {
|
||||
let req = build_log_mask_request(log_type, log_mask_bitsize, &LOG_CODES_FOR_RAW_PACKET_LOGGING);
|
||||
let req = build_log_mask_request(
|
||||
log_type,
|
||||
log_mask_bitsize,
|
||||
&LOG_CODES_FOR_RAW_PACKET_LOGGING,
|
||||
);
|
||||
self.write_request(&req).await?;
|
||||
|
||||
for msg in self.read_response().await? {
|
||||
match msg {
|
||||
Ok(Message::Log { .. }) => info!("skipping log response..."),
|
||||
Ok(Message::Response { payload, status, .. }) => {
|
||||
Ok(Message::Response {
|
||||
payload, status, ..
|
||||
}) => {
|
||||
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload {
|
||||
if status != 0 {
|
||||
return Err(DiagDeviceError::RequestFailed(status, req));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => error!("error parsing message: {:?}", e),
|
||||
}
|
||||
}
|
||||
@@ -229,7 +250,7 @@ impl DiagDevice {
|
||||
struct diag_logging_mode_param_t {
|
||||
req_mode: u32,
|
||||
peripheral_mask: u32,
|
||||
mode_param: u8
|
||||
mode_param: u8,
|
||||
}
|
||||
|
||||
// Triggers the diag device's debug logging mode
|
||||
@@ -254,11 +275,18 @@ fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> {
|
||||
fd,
|
||||
DIAG_IOCTL_SWITCH_LOGGING,
|
||||
&mut params as *mut _,
|
||||
std::mem::size_of::<diag_logging_mode_param_t>(), 0, 0, 0, 0
|
||||
std::mem::size_of::<diag_logging_mode_param_t>(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
if ret < 0 {
|
||||
let msg = format!("DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}", ret);
|
||||
return Err(DiagDeviceError::InitializationFailed(msg))
|
||||
let msg = format!(
|
||||
"DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}",
|
||||
ret
|
||||
);
|
||||
return Err(DiagDeviceError::InitializationFailed(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +300,7 @@ fn determine_use_mdm(fd: i32) -> DiagResult<i32> {
|
||||
unsafe {
|
||||
if libc::ioctl(fd, DIAG_IOCTL_REMOTE_DEV, &use_mdm as *const i32) < 0 {
|
||||
let msg = format!("DIAG_IOCTL_REMOTE_DEV ioctl failed with error code {}", 0);
|
||||
return Err(DiagDeviceError::InitializationFailed(msg))
|
||||
return Err(DiagDeviceError::InitializationFailed(msg));
|
||||
}
|
||||
}
|
||||
Ok(use_mdm)
|
||||
|
||||
+15
-17
@@ -6,24 +6,24 @@ use deku::prelude::*;
|
||||
pub enum GsmtapType {
|
||||
Um(UmSubtype),
|
||||
Abis,
|
||||
UmBurst, /* raw burst bits */
|
||||
SIM, /* ISO 7816 smart card interface */
|
||||
TetraI1, /* tetra air interface */
|
||||
UmBurst, /* raw burst bits */
|
||||
SIM, /* ISO 7816 smart card interface */
|
||||
TetraI1, /* tetra air interface */
|
||||
TetraI1Burst, /* tetra air interface */
|
||||
WmxBurst, /* WiMAX burst */
|
||||
GbLlc, /* GPRS Gb interface: LLC */
|
||||
GbSndcp, /* GPRS Gb interface: SNDCP */
|
||||
Gmr1Um, /* GMR-1 L2 packets */
|
||||
WmxBurst, /* WiMAX burst */
|
||||
GbLlc, /* GPRS Gb interface: LLC */
|
||||
GbSndcp, /* GPRS Gb interface: SNDCP */
|
||||
Gmr1Um, /* GMR-1 L2 packets */
|
||||
UmtsRlcMac,
|
||||
UmtsRrc(UmtsRrcSubtype),
|
||||
LteRrc(LteRrcSubtype), /* LTE interface */
|
||||
LteMac, /* LTE MAC interface */
|
||||
LteMacFramed, /* LTE MAC with context hdr */
|
||||
OsmocoreLog, /* libosmocore logging */
|
||||
QcDiag, /* Qualcomm DIAG frame */
|
||||
LteMac, /* LTE MAC interface */
|
||||
LteMacFramed, /* LTE MAC with context hdr */
|
||||
OsmocoreLog, /* libosmocore logging */
|
||||
QcDiag, /* Qualcomm DIAG frame */
|
||||
LteNas(LteNasSubtype), /* LTE Non-Access Stratum */
|
||||
E1T1, /* E1/T1 Lines */
|
||||
GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */
|
||||
E1T1, /* E1/T1 Lines */
|
||||
GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */
|
||||
}
|
||||
|
||||
// based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1337
|
||||
@@ -119,7 +119,7 @@ pub enum UmtsRrcSubtype {
|
||||
SysInfoTypeSB1 = 58,
|
||||
SysInfoTypeSB2 = 59,
|
||||
ToTargetRNCContainer = 60,
|
||||
TargetRNCToSourceRNCContainer = 61
|
||||
TargetRNCToSourceRNCContainer = 61,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
@@ -218,9 +218,7 @@ pub struct GsmtapHeader {
|
||||
}
|
||||
|
||||
impl GsmtapHeader {
|
||||
pub fn new(
|
||||
gsmtap_type: GsmtapType,
|
||||
) -> Self {
|
||||
pub fn new(gsmtap_type: GsmtapType) -> Self {
|
||||
GsmtapHeader {
|
||||
gsmtap_type,
|
||||
version: 2,
|
||||
|
||||
+50
-14
@@ -13,7 +13,10 @@ pub enum GsmtapParserError {
|
||||
}
|
||||
|
||||
pub fn parse(msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapParserError> {
|
||||
if let Message::Log { timestamp, body, .. } = msg {
|
||||
if let Message::Log {
|
||||
timestamp, body, ..
|
||||
} = msg
|
||||
{
|
||||
match log_to_gsmtap(body)? {
|
||||
Some(msg) => Ok(Some((timestamp, msg))),
|
||||
None => Ok(None),
|
||||
@@ -25,9 +28,13 @@ pub fn parse(msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapP
|
||||
|
||||
fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserError> {
|
||||
match value {
|
||||
LogBody::LteRrcOtaMessage { ext_header_version, packet } => {
|
||||
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() {
|
||||
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),
|
||||
@@ -36,7 +43,12 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
6 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||
7 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||
8 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||
pdu => {
|
||||
return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(
|
||||
ext_header_version,
|
||||
pdu,
|
||||
))
|
||||
}
|
||||
},
|
||||
0x09 | 0x0c => match packet.get_pdu_num() {
|
||||
8 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||
@@ -47,7 +59,12 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
13 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||
14 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||
15 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||
pdu => {
|
||||
return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(
|
||||
ext_header_version,
|
||||
pdu,
|
||||
))
|
||||
}
|
||||
},
|
||||
0x0e..=0x10 => match packet.get_pdu_num() {
|
||||
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||
@@ -58,7 +75,12 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||
pdu => {
|
||||
return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(
|
||||
ext_header_version,
|
||||
pdu,
|
||||
))
|
||||
}
|
||||
},
|
||||
0x13 | 0x1a | 0x1b => match packet.get_pdu_num() {
|
||||
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||
@@ -76,8 +98,13 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
49 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
|
||||
50 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
|
||||
52 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
|
||||
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||
}
|
||||
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),
|
||||
@@ -94,9 +121,18 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
58 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
|
||||
59 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
|
||||
61 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
|
||||
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||
pdu => {
|
||||
return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(
|
||||
ext_header_version,
|
||||
pdu,
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(ext_header_version)),
|
||||
_ => {
|
||||
return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(
|
||||
ext_header_version,
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut header = GsmtapHeader::new(gsmtap_type);
|
||||
header.arfcn = packet.get_earfcn().try_into().unwrap_or(0);
|
||||
@@ -106,19 +142,19 @@ fn log_to_gsmtap(value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserEr
|
||||
header,
|
||||
payload: packet.take_payload(),
|
||||
}))
|
||||
},
|
||||
}
|
||||
LogBody::Nas4GMessage { msg, direction, .. } => {
|
||||
// currently we only handle "plain" (i.e. non-secure) NAS messages
|
||||
let mut header = GsmtapHeader::new(GsmtapType::LteNas(LteNasSubtype::Plain));
|
||||
header.uplink = matches!(direction, Nas4GMessageDirection::Uplink);
|
||||
header.uplink = matches!(direction, Nas4GMessageDirection::Uplink);
|
||||
Ok(Some(GsmtapMessage {
|
||||
header,
|
||||
payload: msg,
|
||||
}))
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
error!("gsmtap_sink: ignoring unhandled log type: {:?}", value);
|
||||
Ok(None)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-5
@@ -3,11 +3,14 @@
|
||||
//! here:
|
||||
//! https://github.com/P1sec/QCSuper/blob/master/docs/The%20Diag%20protocol.md#the-diag-protocol-over-usb
|
||||
|
||||
use crc::Crc;
|
||||
use bytes::Buf;
|
||||
use crc::Crc;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::diag::{MESSAGE_ESCAPE_CHAR, MESSAGE_TERMINATOR, ESCAPED_MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR};
|
||||
use crate::diag::{
|
||||
ESCAPED_MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR, MESSAGE_ESCAPE_CHAR,
|
||||
MESSAGE_TERMINATOR,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Error, PartialEq)]
|
||||
pub enum HdlcError {
|
||||
@@ -29,7 +32,9 @@ pub fn hdlc_encapsulate(data: &[u8], crc: &Crc<u16>) -> Vec<u8> {
|
||||
for &b in data {
|
||||
match b {
|
||||
MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]),
|
||||
MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]),
|
||||
MESSAGE_ESCAPE_CHAR => {
|
||||
result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR])
|
||||
}
|
||||
_ => result.push(b),
|
||||
}
|
||||
}
|
||||
@@ -37,7 +42,9 @@ pub fn hdlc_encapsulate(data: &[u8], crc: &Crc<u16>) -> Vec<u8> {
|
||||
for b in crc.checksum(data).to_le_bytes() {
|
||||
match b {
|
||||
MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]),
|
||||
MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]),
|
||||
MESSAGE_ESCAPE_CHAR => {
|
||||
result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR])
|
||||
}
|
||||
_ => result.push(b),
|
||||
}
|
||||
}
|
||||
@@ -77,7 +84,10 @@ pub fn hdlc_decapsulate(data: &[u8], crc: &Crc<u16>) -> Result<Vec<u8>, HdlcErro
|
||||
let checksum_lo = unescaped.pop().ok_or(HdlcError::MissingChecksum)?;
|
||||
let checksum = [checksum_lo, checksum_hi].as_slice().get_u16_le();
|
||||
if checksum != crc.checksum(&unescaped) {
|
||||
return Err(HdlcError::InvalidChecksum(checksum, crc.checksum(&unescaped)));
|
||||
return Err(HdlcError::InvalidChecksum(
|
||||
checksum,
|
||||
crc.checksum(&unescaped),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(unescaped)
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
pub mod hdlc;
|
||||
pub mod analysis;
|
||||
pub mod diag;
|
||||
pub mod qmdl;
|
||||
pub mod log_codes;
|
||||
pub mod gsmtap;
|
||||
pub mod gsmtap_parser;
|
||||
pub mod hdlc;
|
||||
pub mod log_codes;
|
||||
pub mod pcap;
|
||||
pub mod analysis;
|
||||
pub mod qmdl;
|
||||
pub mod util;
|
||||
|
||||
// bin/check.rs may target windows and does not use this mod
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Enumerates some relevant diag log codes. Copied from QCSuper
|
||||
|
||||
|
||||
// These are 2G-related log types.
|
||||
|
||||
pub const LOG_GSM_RR_SIGNALING_MESSAGE_C: u32 = 0x512f;
|
||||
@@ -95,12 +94,10 @@ pub const RRCLOG_SIG_DL_MSCH: u32 = 8;
|
||||
pub const RRCLOG_EXTENSION_SIB: u32 = 9;
|
||||
pub const RRCLOG_SIB_CONTAINER: u32 = 10;
|
||||
|
||||
|
||||
// 3G layer 3 packets:
|
||||
|
||||
pub const WCDMA_SIGNALLING_MESSAGE: u32 = 0x412f;
|
||||
|
||||
|
||||
// Upper layers
|
||||
|
||||
pub const LOG_DATA_PROTOCOL_LOGGING_C: u32 = 0x11eb;
|
||||
|
||||
+25
-10
@@ -1,10 +1,8 @@
|
||||
//! Parse QMDL files and create a pcap file.
|
||||
//! Creates a plausible IP header and [GSMtap](https://osmocom.org/projects/baseband/wiki/GSMTAP) header and then puts the rest of the data under that for wireshark to parse.
|
||||
use crate::gsmtap::GsmtapMessage;
|
||||
//! Parse QMDL files and create a pcap file.
|
||||
//! Creates a plausible IP header and [GSMtap](https://osmocom.org/projects/baseband/wiki/GSMTAP) header and then puts the rest of the data under that for wireshark to parse.
|
||||
use crate::diag::Timestamp;
|
||||
use crate::gsmtap::GsmtapMessage;
|
||||
|
||||
use tokio::io::AsyncWrite;
|
||||
use std::borrow::Cow;
|
||||
use chrono::prelude::*;
|
||||
use deku::prelude::*;
|
||||
use pcap_file_tokio::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
|
||||
@@ -12,7 +10,9 @@ use pcap_file_tokio::pcapng::blocks::interface_description::InterfaceDescription
|
||||
use pcap_file_tokio::pcapng::blocks::section_header::{SectionHeaderBlock, SectionHeaderOption};
|
||||
use pcap_file_tokio::pcapng::PcapNgWriter;
|
||||
use pcap_file_tokio::{Endianness, PcapError};
|
||||
use std::borrow::Cow;
|
||||
use thiserror::Error;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GsmtapPcapError {
|
||||
@@ -26,7 +26,10 @@ pub enum GsmtapPcapError {
|
||||
Deku(#[from] DekuError),
|
||||
}
|
||||
|
||||
pub struct GsmtapPcapWriter<T> where T: AsyncWrite {
|
||||
pub struct GsmtapPcapWriter<T>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
{
|
||||
writer: PcapNgWriter<T>,
|
||||
ip_id: u16,
|
||||
}
|
||||
@@ -59,10 +62,17 @@ struct UdpHeader {
|
||||
checksum: u16,
|
||||
}
|
||||
|
||||
impl<T> GsmtapPcapWriter<T> where T: AsyncWrite + Unpin + Send {
|
||||
impl<T> GsmtapPcapWriter<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin + Send,
|
||||
{
|
||||
pub async fn new(writer: T) -> Result<Self, GsmtapPcapError> {
|
||||
let metadata = crate::util::RuntimeMetadata::new();
|
||||
let package = format!("{} {}", env!("CARGO_PKG_NAME").to_owned(), metadata.rayhunter_version);
|
||||
let package = format!(
|
||||
"{} {}",
|
||||
env!("CARGO_PKG_NAME").to_owned(),
|
||||
metadata.rayhunter_version
|
||||
);
|
||||
let section = SectionHeaderBlock {
|
||||
endianness: Endianness::Big,
|
||||
major_version: 1,
|
||||
@@ -88,8 +98,13 @@ impl<T> GsmtapPcapWriter<T> where T: AsyncWrite + Unpin + Send {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_gsmtap_message(&mut self, msg: GsmtapMessage, timestamp: Timestamp) -> Result<(), GsmtapPcapError> {
|
||||
let duration = timestamp.to_datetime()
|
||||
pub async fn write_gsmtap_message(
|
||||
&mut self,
|
||||
msg: GsmtapMessage,
|
||||
timestamp: Timestamp,
|
||||
) -> Result<(), GsmtapPcapError> {
|
||||
let duration = timestamp
|
||||
.to_datetime()
|
||||
.signed_duration_since(DateTime::UNIX_EPOCH)
|
||||
.to_std()?;
|
||||
|
||||
|
||||
+66
-31
@@ -3,18 +3,24 @@
|
||||
//! QmdlReader and QmdlWriter can read and write MessagesContainers to and from
|
||||
//! QMDL files.
|
||||
|
||||
use crate::diag::{MessagesContainer, MESSAGE_TERMINATOR, HdlcEncapsulatedMessage, DataType};
|
||||
use crate::diag::{DataType, HdlcEncapsulatedMessage, MessagesContainer, MESSAGE_TERMINATOR};
|
||||
|
||||
use futures::TryStream;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, AsyncBufReadExt};
|
||||
use log::error;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
|
||||
|
||||
pub struct QmdlWriter<T> where T: AsyncWrite + Unpin {
|
||||
pub struct QmdlWriter<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
writer: T,
|
||||
pub total_written: usize,
|
||||
}
|
||||
|
||||
impl<T> QmdlWriter<T> where T: AsyncWrite + Unpin {
|
||||
impl<T> QmdlWriter<T>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
pub fn new(writer: T) -> Self {
|
||||
QmdlWriter::new_with_existing_size(writer, 0)
|
||||
}
|
||||
@@ -35,13 +41,19 @@ impl<T> QmdlWriter<T> where T: AsyncWrite + Unpin {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QmdlReader<T> where T: AsyncRead {
|
||||
pub struct QmdlReader<T>
|
||||
where
|
||||
T: AsyncRead,
|
||||
{
|
||||
reader: BufReader<T>,
|
||||
bytes_read: usize,
|
||||
max_bytes: Option<usize>,
|
||||
}
|
||||
|
||||
impl<T> QmdlReader<T> where T: AsyncRead + Unpin {
|
||||
impl<T> QmdlReader<T>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
pub fn new(reader: T, max_bytes: Option<usize>) -> Self {
|
||||
QmdlReader {
|
||||
reader: BufReader::new(reader),
|
||||
@@ -50,21 +62,28 @@ impl<T> QmdlReader<T> where T: AsyncRead + Unpin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_stream(&mut self) -> impl TryStream<Ok = MessagesContainer, Error = std::io::Error> + '_ {
|
||||
pub fn as_stream(
|
||||
&mut self,
|
||||
) -> impl TryStream<Ok = MessagesContainer, Error = std::io::Error> + '_ {
|
||||
futures::stream::try_unfold(self, |reader| async {
|
||||
let maybe_container = reader.get_next_messages_container().await?;
|
||||
match maybe_container {
|
||||
Some(container) => Ok(Some((container, reader))),
|
||||
None => Ok(None)
|
||||
None => Ok(None),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_next_messages_container(&mut self) -> Result<Option<MessagesContainer>, std::io::Error> {
|
||||
pub async fn get_next_messages_container(
|
||||
&mut self,
|
||||
) -> Result<Option<MessagesContainer>, std::io::Error> {
|
||||
if let Some(max_bytes) = self.max_bytes {
|
||||
if self.bytes_read >= max_bytes {
|
||||
if self.bytes_read > max_bytes {
|
||||
error!("warning: {} bytes read, but max_bytes was {}", self.bytes_read, max_bytes);
|
||||
error!(
|
||||
"warning: {} bytes read, but max_bytes was {}",
|
||||
self.bytes_read, max_bytes
|
||||
);
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -82,12 +101,10 @@ impl<T> QmdlReader<T> where T: AsyncRead + Unpin {
|
||||
Ok(Some(MessagesContainer {
|
||||
data_type: DataType::UserSpace,
|
||||
num_messages: 1,
|
||||
messages: vec![
|
||||
HdlcEncapsulatedMessage {
|
||||
len: bytes_read as u32,
|
||||
data: buf,
|
||||
},
|
||||
]
|
||||
messages: vec![HdlcEncapsulatedMessage {
|
||||
len: bytes_read as u32,
|
||||
data: buf,
|
||||
}],
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -96,26 +113,29 @@ impl<T> QmdlReader<T> where T: AsyncRead + Unpin {
|
||||
mod test {
|
||||
use std::io::Cursor;
|
||||
|
||||
use crate::hdlc::hdlc_encapsulate;
|
||||
use crate::diag::CRC_CCITT;
|
||||
use crate::hdlc::hdlc_encapsulate;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn get_test_messages() -> Vec<HdlcEncapsulatedMessage> {
|
||||
let messages: Vec<HdlcEncapsulatedMessage> = (10..20).map(|i| {
|
||||
let data = hdlc_encapsulate(&vec![i as u8; i], &CRC_CCITT);
|
||||
HdlcEncapsulatedMessage {
|
||||
len: data.len() as u32,
|
||||
data,
|
||||
}
|
||||
}).collect();
|
||||
let messages: Vec<HdlcEncapsulatedMessage> = (10..20)
|
||||
.map(|i| {
|
||||
let data = hdlc_encapsulate(&vec![i as u8; i], &CRC_CCITT);
|
||||
HdlcEncapsulatedMessage {
|
||||
len: data.len() as u32,
|
||||
data,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
messages
|
||||
}
|
||||
|
||||
// returns a byte array consisting of concatenated HDLC encapsulated
|
||||
// test messages
|
||||
fn get_test_message_bytes() -> Vec<u8> {
|
||||
get_test_messages().iter()
|
||||
get_test_messages()
|
||||
.iter()
|
||||
.flat_map(|msg| msg.data.clone())
|
||||
.collect()
|
||||
}
|
||||
@@ -132,7 +152,7 @@ mod test {
|
||||
MessagesContainer {
|
||||
data_type: DataType::UserSpace,
|
||||
num_messages: messages2.len() as u32,
|
||||
messages: messages2.to_vec()
|
||||
messages: messages2.to_vec(),
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -148,7 +168,10 @@ mod test {
|
||||
num_messages: 1,
|
||||
messages: vec![message],
|
||||
};
|
||||
assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap());
|
||||
assert_eq!(
|
||||
expected_container,
|
||||
reader.get_next_messages_container().await.unwrap().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +190,15 @@ mod test {
|
||||
num_messages: 1,
|
||||
messages: vec![message],
|
||||
};
|
||||
assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap());
|
||||
assert_eq!(
|
||||
expected_container,
|
||||
reader.get_next_messages_container().await.unwrap().unwrap()
|
||||
);
|
||||
}
|
||||
assert!(matches!(reader.get_next_messages_container().await, Ok(None)));
|
||||
assert!(matches!(
|
||||
reader.get_next_messages_container().await,
|
||||
Ok(None)
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -202,8 +231,14 @@ mod test {
|
||||
num_messages: 1,
|
||||
messages: vec![message],
|
||||
};
|
||||
assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap());
|
||||
assert_eq!(
|
||||
expected_container,
|
||||
reader.get_next_messages_container().await.unwrap().unwrap()
|
||||
);
|
||||
}
|
||||
assert!(matches!(reader.get_next_messages_container().await, Ok(None)));
|
||||
assert!(matches!(
|
||||
reader.get_next_messages_container().await,
|
||||
Ok(None)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+359
-349
@@ -1,42 +1,46 @@
|
||||
use rayhunter::{diag::{
|
||||
LogBody, LteRrcOtaPacket, Message, Timestamp
|
||||
}, gsmtap_parser};
|
||||
use deku::prelude::*;
|
||||
use rayhunter::{
|
||||
diag::{LogBody, LteRrcOtaPacket, Message, Timestamp},
|
||||
gsmtap_parser,
|
||||
};
|
||||
|
||||
// Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py
|
||||
|
||||
#[test]
|
||||
fn test_lte_rrc_ota() {
|
||||
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,
|
||||
0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x10, 0x15
|
||||
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, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0,
|
||||
0x0, 0x2, 0x0, 0x10, 0x15,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 0x23,
|
||||
inner_length: 0x23,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 0xb0c0,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 26,
|
||||
packet: LteRrcOtaPacket::V25 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 64,
|
||||
nr_rrc_rel_maj: 15,
|
||||
nr_rrc_rel_min: 64,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 270,
|
||||
earfcn: 1811,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x10, 0x15],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 0x23,
|
||||
inner_length: 0x23,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 0xb0c0,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 26,
|
||||
packet: LteRrcOtaPacket::V25 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 64,
|
||||
nr_rrc_rel_maj: 15,
|
||||
nr_rrc_rel_min: 64,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 270,
|
||||
earfcn: 1811,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x10, 0x15],
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[0x10, 0x15]);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
@@ -49,41 +53,40 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v26_binary = &[
|
||||
0x10, 0x00, 0x23, 0x00, 0x23, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1a, 0x0f, 0x40, 0x0f, 0x40, 0x01, 0x0e, 0x01,
|
||||
0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x15,
|
||||
0x10, 0x00, 0x23, 0x00, 0x23, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x1a, 0x0f, 0x40, 0x0f, 0x40, 0x01, 0x0e, 0x01, 0x13, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x15,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 35,
|
||||
inner_length: 35,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 26,
|
||||
packet: LteRrcOtaPacket::V25 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 64,
|
||||
nr_rrc_rel_maj: 15,
|
||||
nr_rrc_rel_min: 64,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 270,
|
||||
earfcn: 1811,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x10, 0x15],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 35,
|
||||
inner_length: 35,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 26,
|
||||
packet: LteRrcOtaPacket::V25 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 64,
|
||||
nr_rrc_rel_maj: 15,
|
||||
nr_rrc_rel_min: 64,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 270,
|
||||
earfcn: 1811,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x10, 0x15],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[
|
||||
0x10, 0x15,
|
||||
]);
|
||||
assert_eq!(&gsmtap_msg.payload, &[0x10, 0x15,]);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 1811);
|
||||
@@ -94,44 +97,44 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v24_binary = &[
|
||||
0x10, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x0f, 0x22, 0x00, 0x68, 0x00, 0xe4, 0x0c,
|
||||
0x00, 0x00, 0x09, 0xdc, 0x05, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0d, 0x00, 0x40, 0x85, 0x8e, 0xc4, 0xe5,
|
||||
0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x00,
|
||||
0x10, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x18, 0x0f, 0x22, 0x00, 0x68, 0x00, 0xe4, 0x0c, 0x00, 0x00, 0x09, 0xdc, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0d, 0x00, 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29,
|
||||
0x15, 0x16, 0x00,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v24_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 44,
|
||||
inner_length: 44,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 24,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 34,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 104,
|
||||
earfcn: 3300,
|
||||
sfn_subfn: 56329,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 13,
|
||||
packet: vec![
|
||||
0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29,
|
||||
0x15, 0x16, 0x0
|
||||
],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 44,
|
||||
inner_length: 44,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 24,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 15,
|
||||
rrc_rel_min: 34,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 104,
|
||||
earfcn: 3300,
|
||||
sfn_subfn: 56329,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 13,
|
||||
packet: vec![
|
||||
0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x0
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
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,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x00,]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 3300);
|
||||
@@ -142,48 +145,48 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 9);
|
||||
|
||||
let v20_binary = &[
|
||||
0x10, 0x00, 0x37, 0x00, 0x37, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x14, 0x0e, 0x30, 0x01, 0x09, 0x01, 0x9c, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||
0x00, 0x18, 0x00, 0x08, 0x10, 0xa7, 0x14, 0x53,
|
||||
0x59, 0xa6, 0x05, 0x43, 0x68, 0xc0, 0x3b, 0xda,
|
||||
0x30, 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00,
|
||||
0x9a, 0x68, 0x40,
|
||||
0x10, 0x00, 0x37, 0x00, 0x37, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x14, 0x0e, 0x30, 0x01, 0x09, 0x01, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x00, 0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, 0x43, 0x68,
|
||||
0xc0, 0x3b, 0xda, 0x30, 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v20_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 55,
|
||||
inner_length: 55,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 20,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 48,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 265,
|
||||
earfcn: 6300,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 9,
|
||||
sib_mask: 0,
|
||||
len: 24,
|
||||
packet: vec![
|
||||
0x8, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x5, 0x43, 0x68,
|
||||
0xc0, 0x3b, 0xda, 0x30, 0x4, 0xa6, 0x88, 0x2, 0x8d, 0xa2,
|
||||
0x0, 0x9a, 0x68, 0x40
|
||||
],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 55,
|
||||
inner_length: 55,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 20,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 48,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 265,
|
||||
earfcn: 6300,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 9,
|
||||
sib_mask: 0,
|
||||
len: 24,
|
||||
packet: vec![
|
||||
0x8, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x5, 0x43, 0x68, 0xc0, 0x3b, 0xda,
|
||||
0x30, 0x4, 0xa6, 0x88, 0x2, 0x8d, 0xa2, 0x0, 0x9a, 0x68, 0x40
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
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,
|
||||
0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[
|
||||
0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, 0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30,
|
||||
0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40,
|
||||
]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 6300);
|
||||
@@ -194,41 +197,41 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v19_binary = &[
|
||||
0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x13, 0x0e, 0x22, 0x00, 0x0b, 0x00, 0xfa, 0x09,
|
||||
0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
|
||||
0x00, 0x09, 0x00, 0x28, 0x18, 0x40, 0x16, 0x08,
|
||||
0x08, 0x80, 0x00, 0x00,
|
||||
0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x13, 0x0e, 0x22, 0x00, 0x0b, 0x00, 0xfa, 0x09, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00,
|
||||
0x00, 0x00, 0x00, 0x09, 0x00, 0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, 0x00,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v19_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 40,
|
||||
inner_length: 40,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 19,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 34,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 11,
|
||||
earfcn: 2554,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 50,
|
||||
sib_mask: 0,
|
||||
len: 9,
|
||||
packet: vec![0x28, 0x18, 0x40, 0x16, 0x8, 0x8, 0x80, 0x0, 0x0],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 40,
|
||||
inner_length: 40,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 19,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 14,
|
||||
rrc_rel_min: 34,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 11,
|
||||
earfcn: 2554,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 50,
|
||||
sib_mask: 0,
|
||||
len: 9,
|
||||
packet: vec![0x28, 0x18, 0x40, 0x16, 0x8, 0x8, 0x80, 0x0, 0x0],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[
|
||||
0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00,
|
||||
0x00,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, 0x00,]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 2554);
|
||||
@@ -239,40 +242,41 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v15_binary = &[
|
||||
0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0f, 0x0d, 0x21, 0x00, 0x9e, 0x00, 0x14, 0x05,
|
||||
0x00, 0x00, 0x49, 0x8c, 0x05, 0x00, 0x00, 0x00,
|
||||
0x00, 0x07, 0x00, 0x40, 0x0c, 0x8e, 0xc9, 0x42,
|
||||
0x89, 0xe0,
|
||||
0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0f, 0x0d, 0x21, 0x00, 0x9e, 0x00, 0x14, 0x05, 0x00, 0x00, 0x49, 0x8c, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0x00, 0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 15,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 13,
|
||||
rrc_rel_min: 33,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 158,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 35913,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0xc, 0x8e, 0xc9, 0x42, 0x89, 0xe0],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 15,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 13,
|
||||
rrc_rel_min: 33,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 158,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 35913,
|
||||
pdu_num: 5,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0xc, 0x8e, 0xc9, 0x42, 0x89, 0xe0],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[
|
||||
0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 1300);
|
||||
@@ -283,49 +287,50 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 9);
|
||||
|
||||
let v15_binary = &[
|
||||
0x10, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0f, 0x0d, 0x21, 0x01, 0x9e, 0x00, 0x14, 0x05,
|
||||
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||
0x00, 0x1c, 0x00, 0x08, 0x10, 0xa5, 0x34, 0x61,
|
||||
0x41, 0xa3, 0x1c, 0x31, 0x68, 0x04, 0x40, 0x1a,
|
||||
0x00, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00,
|
||||
0x10, 0x67, 0xc1, 0x06, 0xd9, 0xe0, 0x00,
|
||||
0x10, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0f, 0x0d, 0x21, 0x01, 0x9e, 0x00, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1c, 0x00, 0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68,
|
||||
0x04, 0x40, 0x1a, 0x00, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, 0x06,
|
||||
0xd9, 0xe0, 0x00,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 59,
|
||||
inner_length: 59,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 15,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 13,
|
||||
rrc_rel_min: 33,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 158,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 9,
|
||||
sib_mask: 0,
|
||||
len: 28,
|
||||
packet: vec![
|
||||
0x8, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68,
|
||||
0x4, 0x40, 0x1a, 0x0, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f,
|
||||
0x0, 0x10, 0x67, 0xc1, 0x6, 0xd9, 0xe0, 0x0
|
||||
],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 59,
|
||||
inner_length: 59,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 15,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 13,
|
||||
rrc_rel_min: 33,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 158,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 9,
|
||||
sib_mask: 0,
|
||||
len: 28,
|
||||
packet: vec![
|
||||
0x8, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x4, 0x40, 0x1a,
|
||||
0x0, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x0, 0x10, 0x67, 0xc1, 0x6, 0xd9,
|
||||
0xe0, 0x0
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
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,
|
||||
0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1,
|
||||
0x06, 0xd9, 0xe0, 0x00,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[
|
||||
0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x04, 0x40, 0x1a, 0x00,
|
||||
0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, 0x06, 0xd9, 0xe0, 0x00,
|
||||
]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 1300);
|
||||
@@ -336,35 +341,36 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v13_binary = &[
|
||||
0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0d, 0x0c, 0x74, 0x01, 0x32, 0x00, 0x38, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x2c, 0x00,
|
||||
0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0d, 0x0c, 0x74, 0x01, 0x32, 0x00, 0x38, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x2c, 0x00,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v13_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 33,
|
||||
inner_length: 33,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 13,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 12,
|
||||
rrc_rel_min: 116,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 50,
|
||||
earfcn: 6200,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 8,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x2c, 0x0],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 33,
|
||||
inner_length: 33,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 13,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 12,
|
||||
rrc_rel_min: 116,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 50,
|
||||
earfcn: 6200,
|
||||
sfn_subfn: 0,
|
||||
pdu_num: 8,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x2c, 0x0],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[0x2c, 0x00]);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
@@ -377,40 +383,41 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 0);
|
||||
|
||||
let v9_binary = &[
|
||||
0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x09, 0x0b, 0x70, 0x00, 0x00, 0x01, 0x14, 0x05,
|
||||
0x00, 0x00, 0x09, 0x91, 0x0b, 0x00, 0x00, 0x00,
|
||||
0x00, 0x07, 0x00, 0x40, 0x0b, 0x8e, 0xc1, 0xdd,
|
||||
0x13, 0xb0,
|
||||
0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x09, 0x0b, 0x70, 0x00, 0x00, 0x01, 0x14, 0x05, 0x00, 0x00, 0x09, 0x91, 0x0b, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0x00, 0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v9_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 9,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 11,
|
||||
rrc_rel_min: 112,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 256,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 37129,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0xb, 0x8e, 0xc1, 0xdd, 0x13, 0xb0],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 38,
|
||||
inner_length: 38,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 9,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 11,
|
||||
rrc_rel_min: 112,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 256,
|
||||
earfcn: 1300,
|
||||
sfn_subfn: 37129,
|
||||
pdu_num: 11,
|
||||
sib_mask: 0,
|
||||
len: 7,
|
||||
packet: vec![0x40, 0xb, 0x8e, 0xc1, 0xdd, 0x13, 0xb0],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[
|
||||
0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 1300);
|
||||
@@ -421,35 +428,36 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 9);
|
||||
|
||||
let v8_binary = &[
|
||||
0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x08, 0x0a, 0x72, 0x01, 0x0e, 0x00, 0x9c, 0x18,
|
||||
0x00, 0x00, 0xa9, 0x33, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x2e, 0x02,
|
||||
0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x08, 0x0a, 0x72, 0x01, 0x0e, 0x00, 0x9c, 0x18, 0x00, 0x00, 0xa9, 0x33, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x2e, 0x02,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v8_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 33,
|
||||
inner_length: 33,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 8,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 10,
|
||||
rrc_rel_min: 114,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 14,
|
||||
earfcn: 6300,
|
||||
sfn_subfn: 13225,
|
||||
pdu_num: 6,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x2e, 0x2],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 33,
|
||||
inner_length: 33,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 8,
|
||||
packet: LteRrcOtaPacket::V8 {
|
||||
rrc_rel_maj: 10,
|
||||
rrc_rel_min: 114,
|
||||
bearer_id: 1,
|
||||
phy_cell_id: 14,
|
||||
earfcn: 6300,
|
||||
sfn_subfn: 13225,
|
||||
pdu_num: 6,
|
||||
sib_mask: 0,
|
||||
len: 2,
|
||||
packet: vec![0x2e, 0x2],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap();
|
||||
assert_eq!(&gsmtap_msg.payload, &[0x2e, 0x02]);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
@@ -462,46 +470,48 @@ fn test_lte_rrc_ota() {
|
||||
assert_eq!(gsmtap_msg.header.subslot, 9);
|
||||
|
||||
let v6_binary = &[
|
||||
0x10, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0xc0, 0xb0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x06, 0x09, 0xb1, 0x00, 0x07, 0x01, 0x2c, 0x07,
|
||||
0x25, 0x34, 0x02, 0x02, 0x00, 0x00, 0x00, 0x12,
|
||||
0x00, 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02,
|
||||
0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c,
|
||||
0x43, 0x26, 0xd0,
|
||||
0x10, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x06, 0x09, 0xb1, 0x00, 0x07, 0x01, 0x2c, 0x07, 0x25, 0x34, 0x02, 0x02, 0x00, 0x00,
|
||||
0x00, 0x12, 0x00, 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, 0xb0, 0x98, 0x1c, 0x20,
|
||||
0xa0, 0x81, 0x8c, 0x43, 0x26, 0xd0,
|
||||
];
|
||||
let (_, parsed) = Message::from_bytes((v6_binary, 0)).unwrap();
|
||||
assert_eq!(&parsed, &Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 47,
|
||||
inner_length: 47,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 6,
|
||||
packet: LteRrcOtaPacket::V5 {
|
||||
rrc_rel_maj: 9,
|
||||
rrc_rel_min: 177,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 263,
|
||||
earfcn: 1836,
|
||||
sfn_subfn: 13349,
|
||||
pdu_num: 2,
|
||||
sib_mask: 2,
|
||||
len: 18,
|
||||
packet: vec![
|
||||
0x40, 0x49, 0x88, 0x5, 0xc0, 0x97, 0x2, 0xd3, 0xb0, 0x98,
|
||||
0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43, 0x26, 0xd0
|
||||
],
|
||||
assert_eq!(
|
||||
&parsed,
|
||||
&Message::Log {
|
||||
pending_msgs: 0,
|
||||
outer_length: 47,
|
||||
inner_length: 47,
|
||||
timestamp: Timestamp { ts: 0 },
|
||||
log_type: 45248,
|
||||
body: LogBody::LteRrcOtaMessage {
|
||||
ext_header_version: 6,
|
||||
packet: LteRrcOtaPacket::V5 {
|
||||
rrc_rel_maj: 9,
|
||||
rrc_rel_min: 177,
|
||||
bearer_id: 0,
|
||||
phy_cell_id: 263,
|
||||
earfcn: 1836,
|
||||
sfn_subfn: 13349,
|
||||
pdu_num: 2,
|
||||
sib_mask: 2,
|
||||
len: 18,
|
||||
packet: vec![
|
||||
0x40, 0x49, 0x88, 0x5, 0xc0, 0x97, 0x2, 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0,
|
||||
0x81, 0x8c, 0x43, 0x26, 0xd0
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
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,
|
||||
0x26, 0xd0,
|
||||
]);
|
||||
assert_eq!(
|
||||
&gsmtap_msg.payload,
|
||||
&[
|
||||
0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81,
|
||||
0x8c, 0x43, 0x26, 0xd0,
|
||||
]
|
||||
);
|
||||
assert_eq!(gsmtap_msg.header.packet_type, 13);
|
||||
assert_eq!(gsmtap_msg.header.timeslot, 0);
|
||||
assert_eq!(gsmtap_msg.header.arfcn, 1836);
|
||||
|
||||
+11
-14
@@ -1,10 +1,10 @@
|
||||
//! a simple shell for uploading to the orbic device.
|
||||
//!
|
||||
//!
|
||||
//! It literally just runs bash as UID/GID 0, with special Android GIDs 3003
|
||||
//! (AID_INET) and 3004 (AID_NET_RAW).
|
||||
use std::process::Command;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::env;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
use nix::unistd::Gid;
|
||||
@@ -15,22 +15,19 @@ fn main() {
|
||||
// Android's "paranoid network" feature restricts network access to
|
||||
// processes in specific groups. More info here:
|
||||
// https://www.elinux.org/Android_Security#Paranoid_network-ing
|
||||
#[cfg(target_arch = "arm")] {
|
||||
let gids = &[
|
||||
Gid::from_raw(3003), // AID_INET
|
||||
Gid::from_raw(3004), // AID_NET_RAW
|
||||
];
|
||||
nix::unistd::setgroups(gids).expect("setgroups failed");
|
||||
#[cfg(target_arch = "arm")]
|
||||
{
|
||||
let gids = &[
|
||||
Gid::from_raw(3003), // AID_INET
|
||||
Gid::from_raw(3004), // AID_NET_RAW
|
||||
];
|
||||
nix::unistd::setgroups(gids).expect("setgroups failed");
|
||||
}
|
||||
|
||||
// discard argv[0]
|
||||
let _ = args.next();
|
||||
// This call will only return if there is an error
|
||||
let error = Command::new("/bin/bash")
|
||||
.args(args)
|
||||
.uid(0)
|
||||
.gid(0)
|
||||
.exec();
|
||||
let error = Command::new("/bin/bash").args(args).uid(0).gid(0).exec();
|
||||
eprintln!("Error running command: {error}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ pub enum ParsingError {
|
||||
}
|
||||
|
||||
pub fn decode<T>(data: &[u8]) -> Result<T, ParsingError>
|
||||
where T: UperCodec<Output = T>
|
||||
where
|
||||
T: UperCodec<Output = T>,
|
||||
{
|
||||
let mut asn_data = PerCodecData::from_slice_uper(data);
|
||||
T::uper_decode(&mut asn_data)
|
||||
.map_err(ParsingError::UperDecodeError)
|
||||
T::uper_decode(&mut asn_data).map_err(ParsingError::UperDecodeError)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
This file was autogenerated using hampi (https://github.com/ystero-dev/hampi), do not modify!
|
||||
|
||||
This place is not a place of honor...
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use telcom_parser::lte_rrc::BCCH_DL_SCH_Message;
|
||||
use asn1_codecs::{uper::UperCodec, PerCodecData};
|
||||
use telcom_parser::lte_rrc::BCCH_DL_SCH_Message;
|
||||
|
||||
fn hex_to_bin(hex: &str) -> Vec<u8> {
|
||||
(0..hex.len())
|
||||
.step_by(2)
|
||||
.map(|i| u8::from_str_radix(&hex[i..i+2], 16).unwrap())
|
||||
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user