mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-06-08 06:01:53 -07:00
+14
-7
@@ -1,21 +1,28 @@
|
|||||||
use wavehunter::diag_device::{DiagDevice, DiagResult};
|
use wavehunter::diag_device::{DiagDevice, DiagResult};
|
||||||
use wavehunter::diag_reader::DiagReader;
|
use wavehunter::diag_reader::DiagReader;
|
||||||
|
use wavehunter::gsmtap_parser::GsmtapParser;
|
||||||
|
use wavehunter::pcap::PcapFile;
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
fn main() -> DiagResult<()> {
|
fn main() -> DiagResult<()> {
|
||||||
// this should eventually be removed for prod
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let file = std::fs::File::options()
|
let mut dev = DiagDevice::new()?;
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.open("/dev/diag")?;
|
|
||||||
let mut dev = DiagDevice::new(&file)?;
|
|
||||||
dev.enable_debug_mode("/data/wavehunter-debug")?;
|
dev.enable_debug_mode("/data/wavehunter-debug")?;
|
||||||
dev.config_logs()?;
|
dev.config_logs()?;
|
||||||
|
|
||||||
|
let mut gsmtap_parser = GsmtapParser::new();
|
||||||
|
let mut pcap_file = PcapFile::new("/data/wavehunter.pcap").unwrap();
|
||||||
|
pcap_file.write_iface_header().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for msg in dev.read_response()? {
|
for msg in dev.read_response()? {
|
||||||
println!("msg: {:?}", msg);
|
debug!("msg: {:?}", msg);
|
||||||
|
if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).unwrap() {
|
||||||
|
debug!("gsmtap_msg: {:?}", gsmtap_msg);
|
||||||
|
pcap_file.write_gsmtap_message(gsmtap_msg, timestamp).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
use wavehunter::debug_file::DebugFileReader;
|
use wavehunter::debug_file::DebugFileReader;
|
||||||
use wavehunter::diag_reader::DiagReader;
|
use wavehunter::diag_reader::DiagReader;
|
||||||
use wavehunter::diag_device::DiagResult;
|
use wavehunter::diag_device::DiagResult;
|
||||||
|
use wavehunter::gsmtap_parser::GsmtapParser;
|
||||||
|
use wavehunter::pcap::PcapFile;
|
||||||
|
|
||||||
|
use log::{debug, error};
|
||||||
|
|
||||||
fn main() -> DiagResult<()> {
|
fn main() -> DiagResult<()> {
|
||||||
// this should eventually be removed for prod
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
println!("Usage: {} /path/to/debug/file", args[0]);
|
error!("Usage: {} /path/to/debug/file", args[0]);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let mut debug_reader = DebugFileReader::new(&args[1])?;
|
let mut debug_reader = DebugFileReader::new(&args[1])?;
|
||||||
|
|
||||||
|
let mut gsmtap_parser = GsmtapParser::new();
|
||||||
|
let mut pcap_file = PcapFile::new("./wavehunter.pcap").unwrap();
|
||||||
|
pcap_file.write_iface_header().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for msg in debug_reader.read_response()? {
|
for msg in debug_reader.read_response()? {
|
||||||
println!("msg: {:?}", msg);
|
debug!("msg: {:?}", msg);
|
||||||
|
if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).unwrap() {
|
||||||
|
debug!("gsmtap_msg: {:?}", gsmtap_msg);
|
||||||
|
pcap_file.write_gsmtap_message(gsmtap_msg, timestamp).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -5,6 +5,7 @@ use crate::diag::*;
|
|||||||
use deku::prelude::*;
|
use deku::prelude::*;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use log::{warn, info};
|
||||||
|
|
||||||
#[derive(Debug, DekuRead, DekuWrite)]
|
#[derive(Debug, DekuRead, DekuWrite)]
|
||||||
#[deku(endian = "little")]
|
#[deku(endian = "little")]
|
||||||
@@ -27,13 +28,12 @@ impl DebugFileReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl DiagReader for DebugFileReader {
|
impl DiagReader for DebugFileReader {
|
||||||
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
||||||
let mut bytes_read_buf = [0; 4];
|
let mut bytes_read_buf = [0; 4];
|
||||||
if let Err(e) = self.file.read_exact(&mut bytes_read_buf) {
|
if let Err(e) = self.file.read_exact(&mut bytes_read_buf) {
|
||||||
if e.kind() == std::io::ErrorKind::UnexpectedEof {
|
if e.kind() == std::io::ErrorKind::UnexpectedEof {
|
||||||
println!("reached end of debug file, exiting...");
|
info!("reached end of debug file, exiting...");
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
@@ -43,7 +43,7 @@ impl DiagReader for DebugFileReader {
|
|||||||
self.file.read_exact(&mut data)?;
|
self.file.read_exact(&mut data)?;
|
||||||
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&data, 0))?;
|
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&data, 0))?;
|
||||||
if leftover_bytes.len() > 0 {
|
if leftover_bytes.len() > 0 {
|
||||||
println!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
||||||
}
|
}
|
||||||
Ok(container)
|
Ok(container)
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-6
@@ -68,7 +68,6 @@ pub enum Message {
|
|||||||
inner_length: u16,
|
inner_length: u16,
|
||||||
log_type: u16,
|
log_type: u16,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
//#[deku(count = "inner_length - 12")]
|
|
||||||
#[deku(ctx = "*log_type, *inner_length - 12")]
|
#[deku(ctx = "*log_type, *inner_length - 12")]
|
||||||
body: LogBody,
|
body: LogBody,
|
||||||
},
|
},
|
||||||
@@ -126,11 +125,13 @@ pub enum LogBody {
|
|||||||
rrc_rel: u8,
|
rrc_rel: u8,
|
||||||
rrc_version_minor: u8,
|
rrc_version_minor: u8,
|
||||||
rrc_version_major: u8,
|
rrc_version_major: u8,
|
||||||
#[deku(count = "hdr_len - 4")] // is this right??
|
// is this right?? based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1327
|
||||||
|
#[deku(count = "hdr_len - 4")]
|
||||||
msg: Vec<u8>,
|
msg: Vec<u8>,
|
||||||
},
|
},
|
||||||
#[deku(id = "0x11eb")]
|
#[deku(id = "0x11eb")]
|
||||||
IpTraffic {
|
IpTraffic {
|
||||||
|
// is this right?? based on https://github.com/P1sec/QCSuper/blob/81dbaeee15ec7747e899daa8e3495e27cdcc1264/src/modules/pcap_dump.py#L378
|
||||||
#[deku(count = "hdr_len - 8")] // is this right???
|
#[deku(count = "hdr_len - 8")] // is this right???
|
||||||
msg: Vec<u8>,
|
msg: Vec<u8>,
|
||||||
},
|
},
|
||||||
@@ -219,12 +220,39 @@ impl LteRrcOtaPacket {
|
|||||||
LteRrcOtaPacket::V25 { sfn_subfn, .. } => *sfn_subfn,
|
LteRrcOtaPacket::V25 { sfn_subfn, .. } => *sfn_subfn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_sfn(&self) -> u16 {
|
pub fn get_sfn(&self) -> u32 {
|
||||||
self.get_sfn_subfn() >> 4
|
self.get_sfn_subfn() as u32 >> 4
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_subfn(&self) -> u16 {
|
pub fn get_subfn(&self) -> u8 {
|
||||||
self.get_sfn_subfn() & 0xf
|
(self.get_sfn_subfn() & 0xf) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pdu_num(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
LteRrcOtaPacket::V0 { pdu_num, .. } => *pdu_num,
|
||||||
|
LteRrcOtaPacket::V5 { pdu_num, .. } => *pdu_num,
|
||||||
|
LteRrcOtaPacket::V8 { pdu_num, .. } => *pdu_num,
|
||||||
|
LteRrcOtaPacket::V25 { pdu_num, .. } => *pdu_num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_earfcn(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
LteRrcOtaPacket::V0 { earfcn, .. } => *earfcn as u32,
|
||||||
|
LteRrcOtaPacket::V5 { earfcn, .. } => *earfcn as u32,
|
||||||
|
LteRrcOtaPacket::V8 { earfcn, .. } => *earfcn,
|
||||||
|
LteRrcOtaPacket::V25 { earfcn, .. } => *earfcn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_payload(self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
LteRrcOtaPacket::V0 { packet, .. } => packet,
|
||||||
|
LteRrcOtaPacket::V5 { packet, .. } => packet,
|
||||||
|
LteRrcOtaPacket::V8 { packet, .. } => packet,
|
||||||
|
LteRrcOtaPacket::V25 { packet, .. } => packet,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-18
@@ -8,6 +8,7 @@ use std::fs::File;
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use log::{info, warn, error};
|
||||||
use deku::prelude::*;
|
use deku::prelude::*;
|
||||||
|
|
||||||
pub type DiagResult<T> = Result<T, DiagDeviceError>;
|
pub type DiagResult<T> = Result<T, DiagDeviceError>;
|
||||||
@@ -56,16 +57,14 @@ const MEMORY_DEVICE_MODE: i32 = 2;
|
|||||||
const DIAG_IOCTL_REMOTE_DEV: u32 = 32;
|
const DIAG_IOCTL_REMOTE_DEV: u32 = 32;
|
||||||
const DIAG_IOCTL_SWITCH_LOGGING: u32 = 7;
|
const DIAG_IOCTL_SWITCH_LOGGING: u32 = 7;
|
||||||
|
|
||||||
pub struct DiagDevice<'a> {
|
pub struct DiagDevice {
|
||||||
file: &'a File,
|
file: File,
|
||||||
debug_file: Option<File>,
|
debug_file: Option<File>,
|
||||||
read_buf: Vec<u8>,
|
read_buf: Vec<u8>,
|
||||||
use_mdm: i32,
|
use_mdm: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DiagReader for DiagDevice {
|
||||||
|
|
||||||
impl<'a> DiagReader for DiagDevice<'a> {
|
|
||||||
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
||||||
let mut bytes_read;
|
let mut bytes_read;
|
||||||
loop {
|
loop {
|
||||||
@@ -81,19 +80,23 @@ impl<'a> DiagReader for DiagDevice<'a> {
|
|||||||
size: bytes_read as u32,
|
size: bytes_read as u32,
|
||||||
data: &self.read_buf[0..bytes_read],
|
data: &self.read_buf[0..bytes_read],
|
||||||
};
|
};
|
||||||
let debug_block_bytes = debug_block.to_bytes().unwrap();
|
let debug_block_bytes = debug_block.to_bytes()?;
|
||||||
debug_file.write_all(&debug_block_bytes).unwrap();
|
debug_file.write_all(&debug_block_bytes)?;
|
||||||
}
|
}
|
||||||
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0))?;
|
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0))?;
|
||||||
if leftover_bytes.len() > 0 {
|
if leftover_bytes.len() > 0 {
|
||||||
println!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
||||||
}
|
}
|
||||||
Ok(container)
|
Ok(container)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DiagDevice<'a> {
|
impl DiagDevice {
|
||||||
pub fn new(file: &'a File) -> DiagResult<Self> {
|
pub fn new() -> DiagResult<Self> {
|
||||||
|
let file = std::fs::File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open("/dev/diag")?;
|
||||||
let fd = file.as_raw_fd();
|
let fd = file.as_raw_fd();
|
||||||
|
|
||||||
enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?;
|
enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?;
|
||||||
@@ -114,7 +117,7 @@ impl<'a> DiagDevice<'a> {
|
|||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(path)?;
|
.open(path)?;
|
||||||
println!("enabling debug mode, writing debug output to {:?}", debug_file);
|
info!("enabling debug mode, writing debug output to {:?}", debug_file);
|
||||||
self.debug_file = Some(debug_file);
|
self.debug_file = Some(debug_file);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -124,8 +127,8 @@ impl<'a> DiagDevice<'a> {
|
|||||||
data_type: DataType::UserSpace,
|
data_type: DataType::UserSpace,
|
||||||
use_mdm: self.use_mdm > 0,
|
use_mdm: self.use_mdm > 0,
|
||||||
mdm_field: -1,
|
mdm_field: -1,
|
||||||
hdlc_encapsulated_request: hdlc_encapsulate(&req.to_bytes().unwrap(), &CRC_CCITT),
|
hdlc_encapsulated_request: hdlc_encapsulate(&req.to_bytes()?, &CRC_CCITT),
|
||||||
}.to_bytes().unwrap();
|
}.to_bytes()?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = self.file.as_raw_fd();
|
let fd = self.file.as_raw_fd();
|
||||||
let buf_ptr = buf.as_ptr() as *const libc::c_void;
|
let buf_ptr = buf.as_ptr() as *const libc::c_void;
|
||||||
@@ -144,7 +147,7 @@ impl<'a> DiagDevice<'a> {
|
|||||||
|
|
||||||
for msg in self.read_response()? {
|
for msg in self.read_response()? {
|
||||||
match msg {
|
match msg {
|
||||||
Message::Log { .. } => println!("skipping log response..."),
|
Message::Log { .. } => info!("skipping log response..."),
|
||||||
Message::Response { payload, status, .. } => match payload {
|
Message::Response { payload, status, .. } => match payload {
|
||||||
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
|
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
@@ -152,7 +155,7 @@ impl<'a> DiagDevice<'a> {
|
|||||||
}
|
}
|
||||||
return Ok(log_mask_sizes);
|
return Ok(log_mask_sizes);
|
||||||
},
|
},
|
||||||
_ => println!("skipping non-LogConfigResponse response..."),
|
_ => info!("skipping non-LogConfigResponse response..."),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +169,7 @@ impl<'a> DiagDevice<'a> {
|
|||||||
|
|
||||||
for msg in self.read_response()? {
|
for msg in self.read_response()? {
|
||||||
match msg {
|
match msg {
|
||||||
Message::Log { .. } => println!("skipping log response..."),
|
Message::Log { .. } => info!("skipping log response..."),
|
||||||
Message::Response { payload, status, .. } => {
|
Message::Response { payload, status, .. } => {
|
||||||
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload {
|
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload {
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
@@ -182,13 +185,13 @@ impl<'a> DiagDevice<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn config_logs(&mut self) -> DiagResult<()> {
|
pub fn config_logs(&mut self) -> DiagResult<()> {
|
||||||
println!("retrieving diag logging capabilities...");
|
info!("retrieving diag logging capabilities...");
|
||||||
let log_mask_sizes = self.retrieve_id_ranges()?;
|
let log_mask_sizes = self.retrieve_id_ranges()?;
|
||||||
|
|
||||||
for (log_type, &log_mask_bitsize) in log_mask_sizes.iter().enumerate() {
|
for (log_type, &log_mask_bitsize) in log_mask_sizes.iter().enumerate() {
|
||||||
if log_mask_bitsize > 0 {
|
if log_mask_bitsize > 0 {
|
||||||
self.set_log_mask(log_type as u32, log_mask_bitsize)?;
|
self.set_log_mask(log_type as u32, log_mask_bitsize)?;
|
||||||
println!("enabled logging for log type {}", log_type);
|
info!("enabled logging for log type {}", log_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -3,6 +3,7 @@ use crate::diag_device::DiagResult;
|
|||||||
|
|
||||||
use crc::{Crc, Algorithm};
|
use crc::{Crc, Algorithm};
|
||||||
use deku::prelude::*;
|
use deku::prelude::*;
|
||||||
|
use log::{debug, info, warn, error};
|
||||||
|
|
||||||
// this is sorta based on the params qcsuper uses, plus what seems to be used in
|
// this is sorta based on the params qcsuper uses, plus what seems to be used in
|
||||||
// https://github.com/fgsect/scat/blob/f1538b397721df3ab8ba12acd26716abcf21f78b/util.py#L47
|
// https://github.com/fgsect/scat/blob/f1538b397721df3ab8ba12acd26716abcf21f78b/util.py#L47
|
||||||
@@ -27,7 +28,7 @@ pub trait DiagReader {
|
|||||||
if container.data_type == DataType::UserSpace {
|
if container.data_type == DataType::UserSpace {
|
||||||
return self.parse_response_container(container);
|
return self.parse_response_container(container);
|
||||||
} else {
|
} else {
|
||||||
println!("skipping non-userspace message...")
|
info!("skipping non-userspace message...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,18 +41,18 @@ pub trait DiagReader {
|
|||||||
Ok(data) => match Message::from_bytes((&data, 0)) {
|
Ok(data) => match Message::from_bytes((&data, 0)) {
|
||||||
Ok(((leftover_bytes, _), res)) => {
|
Ok(((leftover_bytes, _), res)) => {
|
||||||
if leftover_bytes.len() > 0 {
|
if leftover_bytes.len() > 0 {
|
||||||
println!("warning: {} leftover bytes when parsing Message", leftover_bytes.len());
|
warn!("warning: {} leftover bytes when parsing Message", leftover_bytes.len());
|
||||||
}
|
}
|
||||||
result.push(res);
|
result.push(res);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("error parsing response: {:?}", e);
|
error!("error parsing response: {:?}", e);
|
||||||
println!("{:?}", data);
|
debug!("{:?}", data);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("error decapsulating response: {:?}", err);
|
error!("error decapsulating response: {:?}", err);
|
||||||
println!("{:?}", &sub_msg);
|
debug!("{:?}", &sub_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+232
@@ -0,0 +1,232 @@
|
|||||||
|
//! The spec for GSMTAP is here: https://github.com/osmocom/libosmocore/blob/master/include/osmocom/core/gsmtap.h
|
||||||
|
|
||||||
|
use deku::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum GsmtapType {
|
||||||
|
Um(UmSubtype),
|
||||||
|
Abis,
|
||||||
|
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 */
|
||||||
|
UmtsRlcMac,
|
||||||
|
UmtsRrc(UmtsRrcSubtype),
|
||||||
|
LteRrc(LteRrcSubtype), /* LTE interface */
|
||||||
|
LteMac, /* LTE MAC interface */
|
||||||
|
LteMacFramed, /* LTE MAC with context hdr */
|
||||||
|
OsmocoreLog, /* libosmocore logging */
|
||||||
|
QcDiag, /* Qualcomm DIAG frame */
|
||||||
|
LteNas, /* LTE Non-Access Stratum */
|
||||||
|
E1T1, /* E1/T1 Lines */
|
||||||
|
GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum UmSubtype {
|
||||||
|
Unknown = 0x00,
|
||||||
|
Bcch = 0x01,
|
||||||
|
Ccch = 0x02,
|
||||||
|
Rach = 0x03,
|
||||||
|
Agch = 0x04,
|
||||||
|
Pch = 0x05,
|
||||||
|
Sdcch = 0x06,
|
||||||
|
Sdcch4 = 0x07,
|
||||||
|
Sdcch8 = 0x08,
|
||||||
|
TchF = 0x09,
|
||||||
|
TchH = 0x0a,
|
||||||
|
Pacch = 0x0b,
|
||||||
|
Cbch52 = 0x0c,
|
||||||
|
Pdch = 0x0d,
|
||||||
|
Ptcch = 0x0e,
|
||||||
|
Cbch51 = 0x0f,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum UmtsRrcSubtype {
|
||||||
|
DlDcch = 0,
|
||||||
|
UlDcch = 1,
|
||||||
|
DlCcch = 2,
|
||||||
|
UlCcch = 3,
|
||||||
|
Pcch = 4,
|
||||||
|
DlShcch = 5,
|
||||||
|
UlShcch = 6,
|
||||||
|
BcchFach = 7,
|
||||||
|
BcchBch = 8,
|
||||||
|
Mcch = 9,
|
||||||
|
Msch = 10,
|
||||||
|
HandoverToUTRANCommand = 11,
|
||||||
|
InterRATHandoverInfo = 12,
|
||||||
|
SystemInformationBCH = 13,
|
||||||
|
SystemInformationContainer = 14,
|
||||||
|
UERadioAccessCapabilityInfo = 15,
|
||||||
|
MasterInformationBlock = 16,
|
||||||
|
SysInfoType1 = 17,
|
||||||
|
SysInfoType2 = 18,
|
||||||
|
SysInfoType3 = 19,
|
||||||
|
SysInfoType4 = 20,
|
||||||
|
SysInfoType5 = 21,
|
||||||
|
SysInfoType5bis = 22,
|
||||||
|
SysInfoType6 = 23,
|
||||||
|
SysInfoType7 = 24,
|
||||||
|
SysInfoType8 = 25,
|
||||||
|
SysInfoType9 = 26,
|
||||||
|
SysInfoType10 = 27,
|
||||||
|
SysInfoType11 = 28,
|
||||||
|
SysInfoType11bis = 29,
|
||||||
|
SysInfoType12 = 30,
|
||||||
|
SysInfoType13 = 31,
|
||||||
|
SysInfoType13_1 = 32,
|
||||||
|
SysInfoType13_2 = 33,
|
||||||
|
SysInfoType13_3 = 34,
|
||||||
|
SysInfoType13_4 = 35,
|
||||||
|
SysInfoType14 = 36,
|
||||||
|
SysInfoType15 = 37,
|
||||||
|
SysInfoType15bis = 38,
|
||||||
|
SysInfoType15_1 = 39,
|
||||||
|
SysInfoType15_1bis = 40,
|
||||||
|
SysInfoType15_2 = 41,
|
||||||
|
SysInfoType15_2bis = 42,
|
||||||
|
SysInfoType15_2ter = 43,
|
||||||
|
SysInfoType15_3 = 44,
|
||||||
|
SysInfoType15_3bis = 45,
|
||||||
|
SysInfoType15_4 = 46,
|
||||||
|
SysInfoType15_5 = 47,
|
||||||
|
SysInfoType15_6 = 48,
|
||||||
|
SysInfoType15_7 = 49,
|
||||||
|
SysInfoType15_8 = 50,
|
||||||
|
SysInfoType16 = 51,
|
||||||
|
SysInfoType17 = 52,
|
||||||
|
SysInfoType18 = 53,
|
||||||
|
SysInfoType19 = 54,
|
||||||
|
SysInfoType20 = 55,
|
||||||
|
SysInfoType21 = 56,
|
||||||
|
SysInfoType22 = 57,
|
||||||
|
SysInfoTypeSB1 = 58,
|
||||||
|
SysInfoTypeSB2 = 59,
|
||||||
|
ToTargetRNCContainer = 60,
|
||||||
|
TargetRNCToSourceRNCContainer = 61
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum LteRrcSubtype {
|
||||||
|
DlCcch = 0,
|
||||||
|
DlDcch = 1,
|
||||||
|
UlCcch = 2,
|
||||||
|
UlDcch = 3,
|
||||||
|
BcchBch = 4,
|
||||||
|
BcchDlSch = 5,
|
||||||
|
PCCH = 6,
|
||||||
|
MCCH = 7,
|
||||||
|
BcchBchMbms = 8,
|
||||||
|
BcchDlSchBr = 9,
|
||||||
|
BcchDlSchMbms = 10,
|
||||||
|
ScMcch = 11,
|
||||||
|
SbcchSlBch = 12,
|
||||||
|
SbcchSlBchV2x = 13,
|
||||||
|
DlCcchNb = 14,
|
||||||
|
DlDcchNb = 15,
|
||||||
|
UlCcchNb = 16,
|
||||||
|
UlDcchNb = 17,
|
||||||
|
BcchBchNb = 18,
|
||||||
|
BcchBchTddNb = 19,
|
||||||
|
BcchDlSchNb = 20,
|
||||||
|
PcchNb = 21,
|
||||||
|
ScMcchNb = 22,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GsmtapType {
|
||||||
|
pub fn get_type(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
GsmtapType::Um(_) => 0x01,
|
||||||
|
GsmtapType::Abis => 0x02,
|
||||||
|
GsmtapType::UmBurst => 0x03,
|
||||||
|
GsmtapType::SIM => 0x04,
|
||||||
|
GsmtapType::TetraI1 => 0x05,
|
||||||
|
GsmtapType::TetraI1Burst => 0x06,
|
||||||
|
GsmtapType::WmxBurst => 0x07,
|
||||||
|
GsmtapType::GbLlc => 0x08,
|
||||||
|
GsmtapType::GbSndcp => 0x09,
|
||||||
|
GsmtapType::Gmr1Um => 0x0a,
|
||||||
|
GsmtapType::UmtsRlcMac => 0x0b,
|
||||||
|
GsmtapType::UmtsRrc(_) => 0x0c,
|
||||||
|
GsmtapType::LteRrc(_) => 0x0d,
|
||||||
|
GsmtapType::LteMac => 0x0e,
|
||||||
|
GsmtapType::LteMacFramed => 0x0f,
|
||||||
|
GsmtapType::OsmocoreLog => 0x10,
|
||||||
|
GsmtapType::QcDiag => 0x11,
|
||||||
|
GsmtapType::LteNas => 0x12,
|
||||||
|
GsmtapType::E1T1 => 0x13,
|
||||||
|
GsmtapType::GsmRlp => 0x14,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_subtype(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
GsmtapType::Um(subtype) => *subtype as u8,
|
||||||
|
GsmtapType::UmtsRrc(subtype) => *subtype as u8,
|
||||||
|
GsmtapType::LteRrc(subtype) => *subtype as u8,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, DekuWrite)]
|
||||||
|
#[deku(endian = "big")]
|
||||||
|
pub struct GsmtapHeader {
|
||||||
|
#[deku(skip)]
|
||||||
|
pub gsmtap_type: GsmtapType,
|
||||||
|
|
||||||
|
#[deku(assert_eq = "2")]
|
||||||
|
pub version: u8,
|
||||||
|
#[deku(assert_eq = "4")]
|
||||||
|
pub header_len: u8, // length in 4-byte words
|
||||||
|
#[deku(update = "self.gsmtap_type.get_type()")]
|
||||||
|
packet_type: u8,
|
||||||
|
pub timeslot: u8,
|
||||||
|
pub arfcn: u16,
|
||||||
|
pub signal_dbm: i8,
|
||||||
|
pub signal_noise_ratio_db: u8,
|
||||||
|
pub frame_number: u32,
|
||||||
|
#[deku(update = "self.gsmtap_type.get_subtype()")]
|
||||||
|
subtype: u8,
|
||||||
|
pub antenna_number: u8,
|
||||||
|
pub subslot: u8,
|
||||||
|
#[deku(assert_eq = "0")]
|
||||||
|
pub reserved: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GsmtapHeader {
|
||||||
|
pub fn new(
|
||||||
|
gsmtap_type: GsmtapType,
|
||||||
|
) -> Self {
|
||||||
|
GsmtapHeader {
|
||||||
|
gsmtap_type,
|
||||||
|
version: 2,
|
||||||
|
header_len: 4,
|
||||||
|
packet_type: gsmtap_type.get_type(),
|
||||||
|
timeslot: 0,
|
||||||
|
arfcn: 0,
|
||||||
|
signal_dbm: 0,
|
||||||
|
signal_noise_ratio_db: 0,
|
||||||
|
frame_number: 0,
|
||||||
|
subtype: gsmtap_type.get_subtype(),
|
||||||
|
antenna_number: 0,
|
||||||
|
subslot: 0,
|
||||||
|
reserved: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, DekuWrite)]
|
||||||
|
pub struct GsmtapMessage {
|
||||||
|
pub header: GsmtapHeader,
|
||||||
|
pub payload: Vec<u8>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
use crate::diag::*;
|
||||||
|
use crate::gsmtap::*;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub struct GsmtapParser {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum GsmtapParserError {
|
||||||
|
#[error("Invalid LteRrcOtaMessage ext header version {0}")]
|
||||||
|
InvalidLteRrcOtaExtHeaderVersion(u8),
|
||||||
|
#[error("Invalid LteRrcOtaMessage header/PDU number combination: {0}/{1}")]
|
||||||
|
InvalidLteRrcOtaHeaderPduNum(u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GsmtapParser {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GsmtapParser {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv_message(&mut self, msg: Message) -> Result<Option<(Timestamp, GsmtapMessage)>, GsmtapParserError> {
|
||||||
|
if let Message::Log { timestamp, body, .. } = msg {
|
||||||
|
match self.log_to_gsmtap(body)? {
|
||||||
|
Some(msg) => Ok(Some((timestamp, msg))),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_to_gsmtap(&self, value: LogBody) -> Result<Option<GsmtapMessage>, GsmtapParserError> {
|
||||||
|
match value {
|
||||||
|
LogBody::LteRrcOtaMessage { ext_header_version, packet } => {
|
||||||
|
let gsmtap_type = match ext_header_version {
|
||||||
|
0x02 | 0x03 | 0x04 | 0x06 | 0x07 | 0x08 | 0x0d | 0x16 => match packet.get_pdu_num() {
|
||||||
|
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||||
|
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
|
||||||
|
3 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
|
||||||
|
4 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
|
||||||
|
5 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
|
||||||
|
6 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||||
|
7 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||||
|
8 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||||
|
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||||
|
},
|
||||||
|
0x09 | 0x0c => match packet.get_pdu_num() {
|
||||||
|
8 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||||
|
9 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
|
||||||
|
10 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
|
||||||
|
11 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
|
||||||
|
12 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
|
||||||
|
13 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||||
|
14 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||||
|
15 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||||
|
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||||
|
},
|
||||||
|
0x0e | 0x0f | 0x10 => match packet.get_pdu_num() {
|
||||||
|
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||||
|
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
|
||||||
|
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
|
||||||
|
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
|
||||||
|
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
|
||||||
|
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||||
|
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||||
|
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||||
|
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||||
|
},
|
||||||
|
0x13 | 0x1a | 0x1b => match packet.get_pdu_num() {
|
||||||
|
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||||
|
3 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
|
||||||
|
6 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
|
||||||
|
7 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
|
||||||
|
8 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
|
||||||
|
9 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||||
|
10 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||||
|
11 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||||
|
45 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
|
||||||
|
46 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
|
||||||
|
47 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
|
||||||
|
48 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
|
||||||
|
49 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
|
||||||
|
50 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
|
||||||
|
52 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
|
||||||
|
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||||
|
}
|
||||||
|
0x14 | 0x18 | 0x19 => match packet.get_pdu_num() {
|
||||||
|
1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch),
|
||||||
|
2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch),
|
||||||
|
4 => GsmtapType::LteRrc(LteRrcSubtype::MCCH),
|
||||||
|
5 => GsmtapType::LteRrc(LteRrcSubtype::PCCH),
|
||||||
|
6 => GsmtapType::LteRrc(LteRrcSubtype::DlCcch),
|
||||||
|
7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch),
|
||||||
|
8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch),
|
||||||
|
9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch),
|
||||||
|
54 => GsmtapType::LteRrc(LteRrcSubtype::BcchBchNb),
|
||||||
|
55 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSchNb),
|
||||||
|
56 => GsmtapType::LteRrc(LteRrcSubtype::PcchNb),
|
||||||
|
57 => GsmtapType::LteRrc(LteRrcSubtype::DlCcchNb),
|
||||||
|
58 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb),
|
||||||
|
59 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb),
|
||||||
|
61 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb),
|
||||||
|
pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)),
|
||||||
|
},
|
||||||
|
_ => return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(ext_header_version)),
|
||||||
|
};
|
||||||
|
let mut header = GsmtapHeader::new(gsmtap_type);
|
||||||
|
// Wireshark GSMTAP only accepts 14 bits of ARFCN
|
||||||
|
header.arfcn = packet.get_earfcn().try_into().unwrap_or(0);
|
||||||
|
header.frame_number = packet.get_sfn();
|
||||||
|
header.subslot = packet.get_subfn();
|
||||||
|
Ok(Some(GsmtapMessage {
|
||||||
|
header,
|
||||||
|
payload: packet.take_payload(),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
error!("gsmtap_sink: ignoring unhandled log type: {:?}", value);
|
||||||
|
Ok(None)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,6 @@ pub mod diag_device;
|
|||||||
pub mod diag_reader;
|
pub mod diag_reader;
|
||||||
pub mod debug_file;
|
pub mod debug_file;
|
||||||
pub mod log_codes;
|
pub mod log_codes;
|
||||||
|
pub mod gsmtap;
|
||||||
|
pub mod gsmtap_parser;
|
||||||
|
pub mod pcap;
|
||||||
|
|||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
use crate::gsmtap::GsmtapMessage;
|
||||||
|
use crate::diag::Timestamp;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::path::Path;
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use deku::prelude::*;
|
||||||
|
use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
|
||||||
|
use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock;
|
||||||
|
use pcap_file::pcapng::PcapNgWriter;
|
||||||
|
use pcap_file::PcapError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PcapFileError {
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[error("Pcap error: {0}")]
|
||||||
|
Pcap(#[from] PcapError),
|
||||||
|
#[error("Deku error: {0}")]
|
||||||
|
Deku(#[from] DekuError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PcapFile {
|
||||||
|
writer: PcapNgWriter<File>,
|
||||||
|
ip_id: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
const IP_HEADER_LEN: u16 = 20;
|
||||||
|
#[derive(DekuWrite)]
|
||||||
|
#[deku(endian = "big")]
|
||||||
|
struct IpHeader {
|
||||||
|
version_and_ihl: u8,
|
||||||
|
dscp: u8,
|
||||||
|
total_len: u16,
|
||||||
|
identification: u16,
|
||||||
|
flags_and_frag_offset: u8,
|
||||||
|
idk: u8,
|
||||||
|
ttl: u8,
|
||||||
|
protocol: u8,
|
||||||
|
checksum: u16,
|
||||||
|
src_addr: u32,
|
||||||
|
dst_addr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const UDP_HEADER_LEN: u16 = 8;
|
||||||
|
const GSMTAP_PORT: u16 = 4729;
|
||||||
|
#[derive(DekuWrite)]
|
||||||
|
#[deku(endian = "big")]
|
||||||
|
struct UdpHeader {
|
||||||
|
src_port: u16,
|
||||||
|
dst_port: u16,
|
||||||
|
length: u16,
|
||||||
|
checksum: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PcapFile {
|
||||||
|
pub fn new<P>(path: P) -> Result<Self, PcapFileError> where P: AsRef<Path> {
|
||||||
|
let file = std::fs::File::options()
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)?;
|
||||||
|
let writer = PcapNgWriter::new(file)?;
|
||||||
|
Ok(PcapFile { writer, ip_id: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_iface_header(&mut self) -> Result<(), PcapFileError> {
|
||||||
|
let interface = InterfaceDescriptionBlock {
|
||||||
|
linktype: pcap_file::DataLink::IPV4,
|
||||||
|
snaplen: 0xffff,
|
||||||
|
options: vec![],
|
||||||
|
};
|
||||||
|
self.writer.write_pcapng_block(interface)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_gsmtap_message(&mut self, msg: GsmtapMessage, timestamp: Timestamp) -> Result<(), PcapFileError> {
|
||||||
|
let time_since_epoch = timestamp.to_datetime().signed_duration_since(DateTime::UNIX_EPOCH);
|
||||||
|
let secs_since_epoch = time_since_epoch.num_seconds() as u64;
|
||||||
|
let nsecs_since_epoch = time_since_epoch.num_nanoseconds().unwrap_or(0) as u32;
|
||||||
|
// FIXME: although the duration value is correct here, when it shows up in
|
||||||
|
// the pcap it's WAY off, like in the year 55920
|
||||||
|
let duration = std::time::Duration::new(secs_since_epoch, nsecs_since_epoch);
|
||||||
|
let msg_bytes = msg.to_bytes()?;
|
||||||
|
let ip_header = IpHeader {
|
||||||
|
version_and_ihl: 0x45,
|
||||||
|
dscp: 0,
|
||||||
|
total_len: msg_bytes.len() as u16 + IP_HEADER_LEN + UDP_HEADER_LEN,
|
||||||
|
identification: self.ip_id,
|
||||||
|
flags_and_frag_offset: 0x40,
|
||||||
|
idk: 0,
|
||||||
|
ttl: 64,
|
||||||
|
protocol: 0x11, // UDP
|
||||||
|
checksum: 0xffff,
|
||||||
|
src_addr: 0x7f000001,
|
||||||
|
dst_addr: 0x7f000001, // TODO increment by radio_id
|
||||||
|
};
|
||||||
|
let udp_header = UdpHeader {
|
||||||
|
src_port: 13337,
|
||||||
|
dst_port: GSMTAP_PORT,
|
||||||
|
length: msg_bytes.len() as u16 + UDP_HEADER_LEN,
|
||||||
|
checksum: 0xffff,
|
||||||
|
};
|
||||||
|
let mut data: Vec<u8> = Vec::new();
|
||||||
|
data.extend(&ip_header.to_bytes()?);
|
||||||
|
data.extend(&udp_header.to_bytes()?);
|
||||||
|
data.extend(&msg_bytes);
|
||||||
|
let packet = EnhancedPacketBlock {
|
||||||
|
interface_id: 0,
|
||||||
|
timestamp: duration,
|
||||||
|
original_len: data.len() as u32,
|
||||||
|
data: Cow::Owned(data),
|
||||||
|
options: vec![],
|
||||||
|
};
|
||||||
|
self.writer.write_pcapng_block(packet)?;
|
||||||
|
self.ip_id = self.ip_id.wrapping_add(1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user