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    pub show_frame_number: bool,
43}
44
45/// Handlers re-export
46pub use handlers::{
47    bgp::BgpProtocolHandler, bmp::BmpProtocolHandler, flow::FlowProtocolHandler,
48    udp_notif::UdpNotifProtocolHandler,
49};
50
51/// Load and process a PCAP file with the given configuration and handler.
52///
53/// This function reads packets from the specified PCAP file, decodes them using
54/// the provided handler, and writes the results to a JSON Lines output file or
55/// standard output.
56///
57/// # Arguments
58/// * `config` - Configuration containing paths and options for processing:
59///   destination ports on which the protocol handler will filter packets, input
60///   size limit, PCAP file path, and optional output path.
61/// * `handler` - Protocol handler that implements the `ProtocolHandler` trait
62/// # Returns
63/// * `Ok(())` if processing was successful
64/// * `Err` if an error occurred during file operations or processing
65pub fn load_pcap_and_process<M, C, E, H>(
66    config: &Config,
67    handler: &H,
68) -> Result<(), Box<dyn std::error::Error>>
69where
70    C: Default,
71    H: ProtocolHandler<M, C, E>,
72{
73    let pcap_file = File::open(config.pcap_path.as_path()).expect("Failed to open pcap file");
74    let pcap_reader = Box::new(pcap_parser::LegacyPcapReader::new(
75        PCAP_BUFFER_SIZE,
76        pcap_file,
77    )?);
78
79    let mut exporter_peers: HashMap<(IpAddr, u16, IpAddr, u16), (C, bytes::BytesMut)> =
80        HashMap::new();
81
82    let mut writer: Box<dyn Write> = if let Some(output_path_ref) = &config.output_path {
83        // If an output path is provided, create/truncate the file and use it
84        let output_file = File::create(output_path_ref).map_err(|e| {
85            format!(
86                "Failed to create output file '{}': {}",
87                output_path_ref.display(),
88                e
89            )
90        })?;
91        Box::new(BufWriter::new(output_file))
92    } else {
93        // If no output path is provided, write to standard output
94        Box::new(BufWriter::new(io::stdout()))
95    };
96
97    let mut iter = PcapIter::new(pcap_reader);
98    while let Some((src_ip, src_port, dst_ip, dst_port, protocol, packet_data)) = iter.next() {
99        let frame_counter = iter.frame_counter();
100        if let Some(max_packets) = config.input_size
101            && frame_counter > max_packets
102        {
103            break;
104        }
105
106        let flow_key = (src_ip, src_port, dst_ip, dst_port);
107        if let Some(message) = handler.decode(flow_key, protocol, &packet_data, &mut exporter_peers)
108        {
109            for result in message {
110                let serialized_data = handler.serialize(result)?;
111                let output = if config.show_frame_number {
112                    serde_json::json!({
113                        "frame_number": frame_counter,
114                        "data": serialized_data,
115                    })
116                } else {
117                    serialized_data
118                };
119                writer.write_all(serde_json::to_string(&output)?.as_bytes())?;
120                writer.write_all(b"\n")?;
121            }
122        }
123    }
124
125    writer.flush()?;
126    Ok(())
127}