Skip to content

Commit 4acb3e8

Browse files
committed
fix: resolve pcap writer merge issues and extend metadata queries
1 parent a51237e commit 4acb3e8

File tree

1 file changed

+75
-35
lines changed

1 file changed

+75
-35
lines changed

src/pcap/mod.rs

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::conf::{IndexConfig, StorageBackend, StorageConfig, StorageFormat};
22
use crate::layer::{ParsedPacket, TransportInfo};
33
use std::fs::{create_dir_all, File, OpenOptions};
4-
use std::io::{self, BufWriter, Write};
4+
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
55
use std::net::IpAddr;
66
use std::path::PathBuf;
77
use std::time::{SystemTime, UNIX_EPOCH};
@@ -40,6 +40,24 @@ impl MetadataIndex {
4040
.filter(|m| m.src_ip == Some(ip) || m.dst_ip == Some(ip))
4141
.collect()
4242
}
43+
44+
pub fn search_by_port(&self, port: u16) -> Vec<&PacketMetadata> {
45+
self.entries
46+
.iter()
47+
.filter(|m| m.src_port == Some(port) || m.dst_port == Some(port))
48+
.collect()
49+
}
50+
51+
pub fn search_by_vlan(&self, vlan_id: u16) -> Vec<&PacketMetadata> {
52+
self.entries
53+
.iter()
54+
.filter(|m| m.vlan_id == Some(vlan_id))
55+
.collect()
56+
}
57+
58+
pub fn len(&self) -> usize {
59+
self.entries.len()
60+
}
4361
}
4462

4563
trait PacketSink: Send {
@@ -51,16 +69,17 @@ struct PcapSink {
5169
}
5270

5371
impl PcapSink {
54-
fn new(file: File) -> io::Result<Self> {
72+
fn new(file: File, is_new_file: bool) -> io::Result<Self> {
5573
let mut writer = BufWriter::new(file);
56-
// PCAP global header (little-endian)
57-
writer.write_all(&0xa1b2c3d4u32.to_le_bytes())?;
58-
writer.write_all(&2u16.to_le_bytes())?;
59-
writer.write_all(&4u16.to_le_bytes())?;
60-
writer.write_all(&0i32.to_le_bytes())?;
61-
writer.write_all(&0u32.to_le_bytes())?;
62-
writer.write_all(&65535u32.to_le_bytes())?;
63-
writer.write_all(&1u32.to_le_bytes())?; // LINKTYPE_ETHERNET
74+
if is_new_file {
75+
writer.write_all(&0xa1b2c3d4u32.to_le_bytes())?;
76+
writer.write_all(&2u16.to_le_bytes())?;
77+
writer.write_all(&4u16.to_le_bytes())?;
78+
writer.write_all(&0i32.to_le_bytes())?;
79+
writer.write_all(&0u32.to_le_bytes())?;
80+
writer.write_all(&65535u32.to_le_bytes())?;
81+
writer.write_all(&1u32.to_le_bytes())?;
82+
}
6483
Ok(Self { writer })
6584
}
6685
}
@@ -83,30 +102,30 @@ struct PcapNgSink {
83102
}
84103

85104
impl PcapNgSink {
86-
fn new(file: File) -> io::Result<Self> {
105+
fn new(file: File, is_new_file: bool) -> io::Result<Self> {
87106
let mut writer = BufWriter::new(file);
88-
// Section Header Block
89-
writer.write_all(&0x0A0D0D0Au32.to_le_bytes())?;
90-
writer.write_all(&28u32.to_le_bytes())?;
91-
writer.write_all(&0x1A2B3C4Du32.to_le_bytes())?;
92-
writer.write_all(&1u16.to_le_bytes())?;
93-
writer.write_all(&0u16.to_le_bytes())?;
94-
writer.write_all(&(-1i64).to_le_bytes())?;
95-
writer.write_all(&28u32.to_le_bytes())?;
107+
if is_new_file {
108+
writer.write_all(&0x0A0D0D0Au32.to_le_bytes())?;
109+
writer.write_all(&28u32.to_le_bytes())?;
110+
writer.write_all(&0x1A2B3C4Du32.to_le_bytes())?;
111+
writer.write_all(&1u16.to_le_bytes())?;
112+
writer.write_all(&0u16.to_le_bytes())?;
113+
writer.write_all(&(-1i64).to_le_bytes())?;
114+
writer.write_all(&28u32.to_le_bytes())?;
115+
}
96116
Ok(Self {
97117
writer,
98-
wrote_if_block: false,
118+
wrote_if_block: !is_new_file,
99119
})
100120
}
101121

102122
fn ensure_interface_block(&mut self) -> io::Result<()> {
103123
if self.wrote_if_block {
104124
return Ok(());
105125
}
106-
// Interface Description Block
107126
self.writer.write_all(&1u32.to_le_bytes())?;
108127
self.writer.write_all(&20u32.to_le_bytes())?;
109-
self.writer.write_all(&1u16.to_le_bytes())?; // LINKTYPE_ETHERNET
128+
self.writer.write_all(&1u16.to_le_bytes())?;
110129
self.writer.write_all(&0u16.to_le_bytes())?;
111130
self.writer.write_all(&65535u32.to_le_bytes())?;
112131
self.writer.write_all(&20u32.to_le_bytes())?;
@@ -121,12 +140,15 @@ impl PacketSink for PcapNgSink {
121140
let packet_len = frame.len() as u32;
122141
let padded_len = (packet_len + 3) & !3;
123142
let block_len = 32 + padded_len;
143+
let ts_ns: u64 = (ts_sec as u64) * 1_000_000_000 + (ts_usec as u64) * 1_000;
144+
let ts_high = (ts_ns >> 32) as u32;
145+
let ts_low = ts_ns as u32;
124146

125-
self.writer.write_all(&6u32.to_le_bytes())?; // EPB
147+
self.writer.write_all(&6u32.to_le_bytes())?;
126148
self.writer.write_all(&block_len.to_le_bytes())?;
127-
self.writer.write_all(&0u32.to_le_bytes())?; // interface id
128-
self.writer.write_all(&ts_sec.to_le_bytes())?;
129-
self.writer.write_all(&(ts_usec * 1000).to_le_bytes())?; // ns approximation
149+
self.writer.write_all(&0u32.to_le_bytes())?;
150+
self.writer.write_all(&ts_high.to_le_bytes())?;
151+
self.writer.write_all(&ts_low.to_le_bytes())?;
130152
self.writer.write_all(&packet_len.to_le_bytes())?;
131153
self.writer.write_all(&packet_len.to_le_bytes())?;
132154
self.writer.write_all(frame)?;
@@ -146,10 +168,10 @@ pub struct PacketRecorder {
146168

147169
impl PacketRecorder {
148170
pub fn new(storage: &StorageConfig, index_cfg: &IndexConfig) -> io::Result<Self> {
149-
let file = open_output_file(storage)?;
171+
let (file, is_new_file) = open_output_file(storage)?;
150172
let sink: Box<dyn PacketSink> = match storage.format {
151-
StorageFormat::Pcap => Box::new(PcapSink::new(file)?),
152-
StorageFormat::PcapNg => Box::new(PcapNgSink::new(file)?),
173+
StorageFormat::Pcap => Box::new(PcapSink::new(file, is_new_file)?),
174+
StorageFormat::PcapNg => Box::new(PcapNgSink::new(file, is_new_file)?),
153175
};
154176

155177
Ok(Self {
@@ -201,9 +223,21 @@ impl PacketRecorder {
201223
pub fn search_by_ip(&self, ip: IpAddr) -> Vec<&PacketMetadata> {
202224
self.index.search_by_ip(ip)
203225
}
226+
227+
pub fn search_by_port(&self, port: u16) -> Vec<&PacketMetadata> {
228+
self.index.search_by_port(port)
229+
}
230+
231+
pub fn search_by_vlan(&self, vlan_id: u16) -> Vec<&PacketMetadata> {
232+
self.index.search_by_vlan(vlan_id)
233+
}
234+
235+
pub fn indexed_count(&self) -> usize {
236+
self.index.len()
237+
}
204238
}
205239

206-
fn open_output_file(storage: &StorageConfig) -> io::Result<File> {
240+
fn open_output_file(storage: &StorageConfig) -> io::Result<(File, bool)> {
207241
let base = PathBuf::from(&storage.local_path);
208242
create_dir_all(&base)?;
209243

@@ -218,10 +252,14 @@ fn open_output_file(storage: &StorageConfig) -> io::Result<File> {
218252
StorageFormat::PcapNg => "pcapng",
219253
};
220254

221-
OpenOptions::new()
255+
let path = base.join(format!("{}.{}", file_name, ext));
256+
let mut file = OpenOptions::new()
222257
.create(true)
258+
.read(true)
223259
.append(true)
224-
.open(base.join(format!("{}.{}", file_name, ext)))
260+
.open(path)?;
261+
let is_new_file = file.seek(SeekFrom::End(0))? == 0;
262+
Ok((file, is_new_file))
225263
}
226264

227265
#[cfg(test)]
@@ -245,11 +283,11 @@ mod tests {
245283
iface: "eth0".to_string(),
246284
capture_len: 64,
247285
wire_len: 64,
248-
vlan_id: None,
286+
vlan_id: Some(100),
249287
src_ip: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
250288
dst_ip: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))),
251-
src_port: Some(1),
252-
dst_port: Some(2),
289+
src_port: Some(12345),
290+
dst_port: Some(443),
253291
},
254292
&cfg,
255293
);
@@ -259,6 +297,8 @@ mod tests {
259297
.len(),
260298
1
261299
);
300+
assert_eq!(idx.search_by_port(443).len(), 1);
301+
assert_eq!(idx.search_by_vlan(100).len(), 1);
262302
assert_eq!(
263303
idx.search_by_ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
264304
.len(),

0 commit comments

Comments
 (0)