11use crate::conf::{IndexConfig, StorageBackend, StorageConfig, StorageFormat};
22use crate::layer::{ParsedPacket, TransportInfo};
33use std::fs::{create_dir_all, File, OpenOptions};
4- use std::io::{self, BufWriter, Write};
4+ use std::io::{self, BufWriter, Seek, SeekFrom, Write};
55use std::net::IpAddr;
66use std::path::PathBuf;
77use 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
4563trait PacketSink: Send {
@@ -51,16 +69,17 @@ struct PcapSink {
5169}
5270
5371impl 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
85104impl 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
147169impl 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