rscap 0.3.2

Cross-platform packet capture and transmission utilities
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2024 Nathaniel Bennett <me[at]nathanielbennett[dotcom]>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Cross-platform packet filtering utilities.
//!
//!

/// Information on packets generally received/dropped over a sniffing interface.
#[derive(Clone, Debug)]
pub struct PacketStatistics {
    pub(crate) received: u32,
    pub(crate) dropped: u32,
}

impl PacketStatistics {
    /// The total number of packets received over the interface.
    #[inline]
    pub fn received(&self) -> u32 {
        self.received
    }

    /// The total number of packets dropped by the sniffing device (e.g. due to full buffers).
    #[inline]
    pub fn dropped(&self) -> u32 {
        self.dropped
    }
}

/// A BPF filter program.
#[repr(C)]
pub struct BpfProgram {
    #[cfg(target_os = "windows")]
    bf_len: u32,
    #[cfg(not(target_os = "windows"))]
    bf_len: u16,
    bf_insns: *mut BpfInstruction,
}

/// Represents a single machine instruction for a BPF program.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct BpfInstruction {
    pub code: libc::c_ushort,
    pub jt: libc::c_uchar,
    pub jf: libc::c_uchar,
    pub k: libc::c_uint,
}

/// A Berkeley Packet Filter (BPF) rule.
#[derive(Clone)]
pub struct PacketFilter {
    filter: Vec<BpfInstruction>,
}

impl PacketFilter {
    #[inline]
    pub fn from_vec(filter: Vec<BpfInstruction>) -> Self {
        Self { filter }
    }

    /// Returns a Linux-specific representation of the BPF rule.
    ///
    /// # Safety
    ///
    /// The lifetime of the returned `sock_fprog` struct must not exceed that of the `Bpf`.
    ///
    /// `sock_fprog` contains a mutable pointer to the contents of `Bpf`; as such, care should
    /// be taken to ensure that mutable aliasing rules are not violated. Using the struct for
    /// `SO_ATTACH_FILTER` on a socket is a safe use case.
    #[inline]
    pub unsafe fn as_bpf_program(&mut self) -> BpfProgram {
        #[cfg(target_os = "windows")]
        let bf_len = self.filter.len() as u32;
        #[cfg(not(target_os = "windows"))]
        let bf_len = self.filter.len() as u16;

        BpfProgram {
            bf_len,
            bf_insns: self.filter.as_mut_ptr(),
        }
    }

    /// A BPF rule that accepts all prospective packets.
    #[inline]
    pub fn accept_all() -> Self {
        Self {
            filter: vec![BpfInstruction {
                code: 0x06,
                jt: 0,
                jf: 0,
                k: 0xffffffff,
            }],
        }
    }

    /// Indicates whether the given BPF rule accepts all prospective packets.
    ///
    /// There are technically many millions of potential BPF rules that accept all prospective
    /// packets. This function only matches on the packet generated by
    /// [`PacketFilter::accept_all()`].
    pub fn is_accept_all(&self) -> bool {
        match self.filter.first() {
            None => false,
            Some(inst) => {
                self.filter.len() == 1
                    && inst.code == 0x06
                    && inst.jt == 0
                    && inst.jf == 0
                    && inst.k == 0xffffffff
            }
        }
    }

    /// A BPF rule that rejects all prospective packets.
    #[inline]
    pub fn reject_all() -> Self {
        Self {
            filter: vec![BpfInstruction {
                code: 0x06,
                jt: 0,
                jf: 0,
                k: 0,
            }],
        }
    }

    /*
    pub fn test_match(&self, buf: &[u8]) -> bool {
        // Tests whether the given packet matches the filter.
    }
    */
}