nf 0.1.0

A port of the netfilter framework written entirely in rust.
Documentation
use crate::mnl::nlmsg::{Header, Message};
use libc::{EINTR, EPROTO, ESRCH};
use std::ptr::null_mut;

/// Callback function type for netlink message processing
///
/// Function pointer type for callbacks that process netlink messages.
/// 
/// # Parameters
/// * `nlh` - Pointer to the netlink message header
/// * `data` - Pointer to user-provided data passed to the callback
///
/// # Returns
/// Integer status code indicating processing result
pub type callback_t = *mut fn(nlh: *const Header, data: *mut u8) -> i32;

/// Provides callback handling functions for netlink message processing
pub struct Callback {}

impl Callback {
    /// Return value indicating successful message processing
    pub const OK: i32 = 1;
    
    /// Return value indicating message processing should stop
    pub const STOP: i32 = 2;
    
    /// Return value indicating an error occurred during processing
    pub const ERROR: i32 = 3;
    
    /// No-operation callback that always returns OK
    ///
    /// # Parameters
    /// * `_nlh` - Pointer to the netlink message header (unused)
    /// * `_data` - Pointer to user-provided data (unused)
    ///
    /// # Returns
    /// Always returns OK status
    pub const fn noop(_nlh: *const Header, _data: *mut u8) -> i32 {
        Self::OK
    }

    /// Error handling callback for processing error messages
    ///
    /// # Parameters
    /// * `nlh` - Pointer to the netlink message header
    /// * `_data` - Pointer to user-provided data (unused)
    ///
    /// # Returns
    /// Error code or STOP if no error
    pub fn error(nlh: *const Header, _data: *mut u8) -> i32 {
        unsafe {
            let err = *(nlh.add(1) as *const i32);

            /* Netlink subsystems returns the errno value with different signess */
            if err < 0 {
                return -err;
            } else {
                if (err as u32) & Message::DUMP_INTR != 0 {
                    return -EINTR;
                }
            }

            if err == 0 { Self::STOP } else { -err }
        }
    }

    /// Stop callback that always returns STOP
    ///
    /// # Parameters
    /// * `_nlh` - Pointer to the netlink message header (unused)
    /// * `_data` - Pointer to user-provided data (unused)
    ///
    /// # Returns
    /// Always returns STOP status
    pub const fn stop(_nlh: *const Header, _data: *mut u8) -> i32 {
        Self::STOP
    }

    /// Default array of callbacks for standard netlink message types
    pub const default_array: [Option<callback_t>; Message::MIN_TYPE as usize] = {
        let mut array: [Option<callback_t>; Message::MIN_TYPE as usize] =
            [None; Message::MIN_TYPE as usize];
        array[Message::NOOP as usize] = Some(Self::noop as callback_t);
        array[Message::ERROR as usize] = Some(Self::error as callback_t);
        array[Message::DONE as usize] = Some(Self::stop as callback_t);
        array[Message::OVERRUN as usize] = Some(Self::noop as callback_t);
        array
    };

    /// Internal implementation for processing netlink messages
    ///
    /// # Parameters
    /// * `buf` - Buffer containing netlink messages
    /// * `numbytes` - Number of bytes in the buffer
    /// * `seq` - Expected sequence number
    /// * `portid` - Expected port ID
    /// * `cb_data` - Callback for data messages
    /// * `data` - User data pointer passed to callbacks
    /// * `cb_ctl_array` - Array of control message callbacks
    /// * `cb_ctl_array_len` - Length of control callback array
    ///
    /// # Returns
    /// * `MNL_CB_ERROR` (<=-1): An error occurred
    /// * `MNL_CB_STOP` (=0): Stop processing
    /// * `MNL_CB_OK` (>=1): Processing completed successfully
    pub fn __run(
        buf: *const u8,
        numbytes: usize,
        seq: u32,
        portid: u32,
        cb_data: callback_t,
        data: *mut u8,
        cb_ctl_array: *const callback_t,
        cb_ctl_array_len: u32,
    ) -> i32 {
        unsafe {
            let mut ret: i32 = Self::OK;
            let mut len: i32 = numbytes as i32;
            let mut nlh = buf as *mut Header;

            while (*nlh).is_ok(len) {
                /* check message source */
                if !(*nlh).portid_ok(portid) {
                    return -ESRCH;
                }
                /* perform sequence tracking */
                if !(*nlh).seq_ok(seq) {
                    return -EPROTO;
                }

                /* dump was interrupted */
                if ((*nlh).flags as u32 & Message::DUMP_INTR) != 0 {
                    return -EINTR;
                }
                /* netlink data message handling */
                if (*nlh).type_ >= Message::MIN_TYPE {
                    if !cb_data.is_null() {
                        ret = (*cb_data)(nlh, data);
                        if ret <= Self::STOP {
                            return ret;
                        }
                    }
                } else if (*nlh).type_ < cb_ctl_array_len as u16 {
                    if !cb_ctl_array.is_null() {
                        let cb = *cb_ctl_array.add((*nlh).type_ as usize);
                        if !cb.is_null() {
                            ret = (*cb)(nlh, data);
                            if ret <= Self::STOP {
                                return ret;
                            }
                        }
                    }
                } else if let Some(cb) = Self::default_array[(*nlh).type_ as usize] {
                    ret = (*cb)(nlh, data);
                    if ret <= Self::STOP {
                        return ret;
                    }
                }
                nlh = (*nlh).next(&mut len);
            }
            ret
        }
    }

    /// Processes a batch of netlink messages with custom control callbacks
    ///
    /// # Parameters
    /// * `buf` - Buffer containing netlink messages
    /// * `numbytes` - Number of bytes in the buffer
    /// * `seq` - Expected sequence number
    /// * `portid` - Expected port ID
    /// * `cb_data` - Callback for data messages
    /// * `data` - User data pointer passed to callbacks
    /// * `cb_ctl_array` - Array of custom control message callbacks
    /// * `cb_ctl_array_len` - Length of control callback array
    ///
    /// # Returns
    /// * Negative value: An error occurred
    /// * `0`: Processing was stopped
    /// * Positive value: Processing completed successfully
    ///
    /// # Notes
    /// - Returns -ESRCH if the port ID doesn't match
    /// - Returns -EPROTO if the sequence number doesn't match
    /// - Returns -EINTR if the dump was interrupted
    pub fn run2(
        buf: *const u8,
        numbytes: usize,
        seq: u32,
        portid: u32,
        cb_data: callback_t,
        data: *mut u8,
        cb_ctl_array: *const callback_t,
        cb_ctl_array_len: u32,
    ) -> i32 {
        Self::__run(
            buf,
            numbytes,
            seq,
            portid,
            cb_data,
            data,
            cb_ctl_array,
            cb_ctl_array_len,
        )
    }


    /// Processes a batch of netlink messages with default control callbacks
    ///
    /// Simplified version of run2() that uses default control callbacks.
    ///
    /// # Parameters
    /// * `buf` - Buffer containing netlink messages
    /// * `numbytes` - Number of bytes in the buffer
    /// * `seq` - Expected sequence number
    /// * `portid` - Expected port ID
    /// * `cb_data` - Callback for data messages
    /// * `data` - User data pointer passed to callbacks
    ///
    /// # Returns
    /// * Negative value: An error occurred
    /// * `0`: Processing was stopped
    /// * Positive value: Processing completed successfully
    pub fn run(
        buf: *const u8,
        numbytes: usize,
        seq: u32,
        portid: u32,
        cb_data: callback_t,
        data: *mut u8,
    ) -> i32 {
        Self::__run(buf, numbytes, seq, portid, cb_data, data, null_mut(), 0)
    }
}