Skip to main content

netgauze_pcap_decoder/
lib.rs

1// Copyright (C) 2025-present The NetGauze Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12// implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! NetGauze PCAP Decoder Library
17//!
18//! This library provides functionality to decode various network protocols
19//! (BGP, BMP, Flow, UDP-Notif) from PCAP files and serialize them to JSON.
20
21pub mod handlers;
22pub mod protocol_handler;
23
24use crate::protocol_handler::ProtocolHandler;
25use netgauze_pcap_reader::PcapIter;
26use std::collections::HashMap;
27use std::fs::File;
28use std::io::{self, BufWriter, Write};
29use std::net::IpAddr;
30use std::path::PathBuf;
31
32// Define constants
33pub const PCAP_BUFFER_SIZE: usize = 165536;
34
35/// Configuration for PCAP processing
36#[derive(Debug, Clone)]
37pub struct Config {
38    pub dest_ports: Vec<u16>,
39    pub input_size: Option<usize>,
40    pub pcap_path: PathBuf,
41    pub output_path: Option<PathBuf>,
42}
43
44/// Handlers re-export
45pub use handlers::{
46    bgp::BgpProtocolHandler, bmp::BmpProtocolHandler, flow::FlowProtocolHandler,
47    udp_notif::UdpNotifProtocolHandler,
48};
49
50/// Load and process a PCAP file with the given configuration and handler.
51///
52/// This function reads packets from the specified PCAP file, decodes them using
53/// the provided handler, and writes the results to a JSON Lines output file or
54/// standard output.
55///
56/// # Arguments
57/// * `config` - Configuration containing paths and options for processing:
58///   destination ports on which the protocol handler will filter packets, input
59///   size limit, PCAP file path, and optional output path.
60/// * `handler` - Protocol handler that implements the `ProtocolHandler` trait
61/// # Returns
62/// * `Ok(())` if processing was successful
63/// * `Err` if an error occurred during file operations or processing
64pub fn load_pcap_and_process<M, C, E, H>(
65    config: &Config,
66    handler: &H,
67) -> Result<(), Box<dyn std::error::Error>>
68where
69    C: Default,
70    H: ProtocolHandler<M, C, E>,
71{
72    let pcap_file = File::open(config.pcap_path.as_path()).expect("Failed to open pcap file");
73    let pcap_reader =
74        Box::new(pcap_parser::LegacyPcapReader::new(PCAP_BUFFER_SIZE, pcap_file).unwrap());
75
76    let mut exporter_peers: HashMap<(IpAddr, u16, IpAddr, u16), (C, bytes::BytesMut)> =
77        HashMap::new();
78    let mut packet_counter = 0;
79
80    let mut writer: Box<dyn Write> = if let Some(output_path_ref) = &config.output_path {
81        // If an output path is provided, create/truncate the file and use it
82        let output_file = File::create(output_path_ref).map_err(|e| {
83            format!(
84                "Failed to create output file '{}': {}",
85                output_path_ref.display(),
86                e
87            )
88        })?;
89        Box::new(BufWriter::new(output_file))
90    } else {
91        // If no output path is provided, write to standard output
92        Box::new(BufWriter::new(io::stdout()))
93    };
94
95    for (src_ip, src_port, dst_ip, dst_port, protocol, packet_data) in PcapIter::new(pcap_reader) {
96        packet_counter += 1;
97        if let Some(max_packets) = config.input_size
98            && packet_counter > max_packets
99        {
100            break;
101        }
102
103        let flow_key = (src_ip, src_port, dst_ip, dst_port);
104        if let Some(message) = handler.decode(flow_key, protocol, &packet_data, &mut exporter_peers)
105        {
106            for result in message {
107                let serialized_data = handler.serialize(result)?;
108                writer.write_all(serde_json::to_string(&serialized_data)?.as_bytes())?;
109                writer.write_all(b"\n")?;
110            }
111        }
112    }
113
114    writer.flush()?;
115    Ok(())
116}