mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-06-11 15:23:30 -07:00
Add GSMTAP parsing and PcapNg file writing
This commit is contained in:
+31
-4
@@ -219,12 +219,39 @@ impl LteRrcOtaPacket {
|
||||
LteRrcOtaPacket::V25 { sfn_subfn, .. } => *sfn_subfn,
|
||||
}
|
||||
}
|
||||
pub fn get_sfn(&self) -> u16 {
|
||||
self.get_sfn_subfn() >> 4
|
||||
pub fn get_sfn(&self) -> u32 {
|
||||
self.get_sfn_subfn() as u32 >> 4
|
||||
}
|
||||
|
||||
pub fn get_subfn(&self) -> u16 {
|
||||
self.get_sfn_subfn() & 0xf
|
||||
pub fn get_subfn(&self) -> u8 {
|
||||
(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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 debug_file;
|
||||
pub mod log_codes;
|
||||
pub mod gsmtap;
|
||||
pub mod gsmtap_parser;
|
||||
pub mod pcap;
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
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,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[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 duration = std::time::Duration::new(time_since_epoch.num_seconds() as u64, time_since_epoch.num_nanoseconds().unwrap() as u32);
|
||||
let msg_bytes = msg.to_bytes()?;
|
||||
let ip_header = IpHeader {
|
||||
version_and_ihl: 0x45,
|
||||
dscp: 0,
|
||||
total_len: msg_bytes.len() as u16 + 20 + 8,
|
||||
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: 4729,
|
||||
length: msg_bytes.len() as u16 + 8,
|
||||
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