nflog/
lib.rs

1//!  Netfilter NFLOG high-level bindings
2//!
3//! libnetfilter_log is a userspace library providing interface to packets that
4//! have been logged by the kernel packet filter. It is is part of a system that
5//! deprecates the old syslog/dmesg based packet logging.
6//!
7//! libnetfilter_log homepage is: http://netfilter.org/projects/libnetfilter_log/
8//!
9//! **Using NFLOG requires root privileges, or the `CAP_NET_ADMIN` capability**
10//!
11//! The code is available on [Github](https://github.com/chifflier/nflog-rs)
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! extern crate libc;
17//! extern crate nflog;
18//! use std::fmt::Write;
19//!
20//! fn callback(msg: &nflog::Message) {
21//!     println!(" -> msg: {}", msg);
22//!     // this will send an error if there is no uid (for ex. incoming packets)
23//!     println!(" -> uid: {}, gid: {}", msg.get_uid().unwrap(), msg.get_gid().unwrap());
24//!     println!(" -> prefix: {}", msg.get_prefix().unwrap());
25//!     println!(" -> seq: {}", msg.get_seq().unwrap_or(0xffff));
26//!
27//!     let payload_data = msg.get_payload();
28//!     let mut s = String::new();
29//!     for &byte in payload_data {
30//!         write!(&mut s, "{:X} ", byte).unwrap();
31//!     }
32//!     println!("{}", s);
33//!
34//!     println!("XML\n{}", msg.as_xml_str(&[nflog::XMLFormatFlags::XmlAll]).unwrap());
35//!
36//! }
37//!
38//! fn main() {
39//!     let mut q = nflog::Queue::new();
40//!
41//!     q.open();
42//!
43//!     let rc = q.bind(libc::AF_INET);
44//!     assert!(rc == 0);
45//!
46//!     q.bind_group(0);
47//!
48//!     q.set_mode(nflog::CopyMode::CopyPacket, 0xffff);
49//!     q.set_flags(nflog::CfgFlags::CfgFlagsSeq);
50//!
51//!     q.set_callback(callback);
52//!     q.run_loop();
53//!
54//!     q.close();
55//! }
56//! ```
57
58
59extern crate libc;
60
61pub use hwaddr::*;
62mod hwaddr;
63
64pub use message::*;
65mod message;
66
67type NflogHandle = *const libc::c_void;
68type NflogGroupHandle = *const libc::c_void;
69
70/// Prototype for the callback function, triggered when a packet is received
71pub type NflogCallback = fn (&Message) -> ();
72
73type NflogCCallback = extern "C" fn (*const libc::c_void, *const libc::c_void, *const libc::c_void, *const libc::c_void );
74
75#[link(name = "netfilter_log")]
76extern {
77    // library setup
78    fn nflog_open() -> NflogHandle;
79    fn nflog_close(qh: NflogHandle);
80    fn nflog_bind_pf (qh: NflogHandle, pf: libc::c_int) -> libc::c_int;
81    fn nflog_unbind_pf (qh: NflogHandle, pf: libc::c_int) -> libc::c_int;
82
83    // group handling
84    fn nflog_fd (h: NflogHandle) -> libc::c_int;
85    fn nflog_bind_group (qh: NflogHandle, num: u16) -> NflogGroupHandle;
86    fn nflog_unbind_group (gh: NflogGroupHandle) -> libc::c_int;
87    fn nflog_set_mode (gh: NflogGroupHandle, mode: u8, range: u32) -> libc::c_int;
88    fn nflog_set_timeout (gh: NflogGroupHandle, timeout: u32) -> libc::c_int;
89    fn nflog_set_qthresh (gh: NflogGroupHandle, qthresh: u32) -> libc::c_int;
90    fn nflog_set_nlbufsiz (gh: NflogGroupHandle, nlbufsiz: u32) -> libc::c_int;
91    fn nflog_set_flags (gh: NflogGroupHandle, flags: u16) -> libc::c_int;
92
93    // callback-related functions
94    fn nflog_callback_register(gh: NflogGroupHandle, cb: NflogCCallback, data: *mut libc::c_void);
95    fn nflog_handle_packet(qh: NflogHandle, buf: *mut libc::c_void, rc: libc::c_int) -> libc::c_int;
96}
97
98
99
100
101/// Copy modes
102pub enum CopyMode {
103    /// Do not copy packet contents nor metadata
104    CopyNone,
105    /// Copy only packet metadata, not payload
106    CopyMeta,
107    /// Copy packet metadata and not payload
108    CopyPacket,
109}
110const NFULNL_COPY_NONE : u8   = 0x00;
111const NFULNL_COPY_META : u8   = 0x01;
112const NFULNL_COPY_PACKET : u8 = 0x02;
113
114/// Configuration Flags
115pub enum CfgFlags {
116    CfgFlagsSeq,
117    CfgFlagsSeqGlobal,
118}
119const NFULNL_CFG_F_SEQ : u16         = 0x0001;
120const NFULNL_CFG_F_SEQ_GLOBAL : u16  = 0x0001;
121
122
123/// Opaque struct `Queue`: abstracts an NFLOG queue
124pub struct Queue {
125    qh  : NflogHandle,
126    gh  : NflogGroupHandle,
127    cb : Option<NflogCallback>,
128}
129
130
131impl Queue {
132    /// Creates a new, uninitialized, `Queue`.
133    pub fn new() -> Queue {
134        return Queue {
135            qh : std::ptr::null_mut(),
136            gh : std::ptr::null_mut(),
137            cb: None,
138        };
139    }
140
141    /// Opens a NFLOG handler
142    ///
143    /// This function obtains a netfilter log connection handle. When you are
144    /// finished with the handle returned by this function, you should destroy it
145    /// by calling `close()`.
146    /// A new netlink connection is obtained internally
147    /// and associated with the log connection handle returned.
148    pub fn open(&mut self) {
149        self.qh = unsafe { nflog_open() };
150    }
151
152    /// Closes a NFLOG handler
153    ///
154    /// This function closes the nflog handler and free associated resources.
155    pub fn close(&mut self) {
156        assert!(!self.qh.is_null());
157        unsafe { nflog_close(self.qh) };
158        self.qh = std::ptr::null_mut();
159    }
160
161    /// Bind a nflog handler to a given protocol family
162    ///
163    /// Binds the given log connection handle to process packets belonging to
164    /// the given protocol family (ie. `PF_INET`, `PF_INET6`, etc).
165    ///
166    /// Arguments
167    ///
168    /// * `pf` - Protocol family (usually `AF_INET` or `AF_INET6`)
169    ///
170    /// Remarks:
171    ///
172    /// **Requires root privileges**
173    pub fn bind(&self, pf: libc::c_int) -> i32 {
174        assert!(!self.qh.is_null());
175        return unsafe { nflog_bind_pf(self.qh,pf) };
176    }
177
178    /// Unbinds the nflog handler from a protocol family
179    ///
180    /// Unbinds the given nflog handle from processing packets belonging to the
181    /// given protocol family.
182    ///
183    /// Arguments
184    ///
185    /// * `pf` - Protocol family (usually `AF_INET` or `AF_INET6`)
186    ///
187    /// Remarks:
188    ///
189    /// **Requires root privileges**
190    pub fn unbind(&self, pf: libc::c_int) -> i32 {
191        assert!(!self.qh.is_null());
192        return unsafe { nflog_unbind_pf(self.qh,pf) }
193    }
194
195    /// Returns the C file descriptor associated with the nflog handler
196    ///
197    /// This function returns a file descriptor that can be used for
198    /// communication over the netlink connection associated with the given log
199    /// connection handle.
200    pub fn fd(&self) -> i32 {
201        assert!(!self.qh.is_null());
202        return unsafe { nflog_fd(self.qh) }
203    }
204
205    ///  Binds a new handle to a specific group number.
206    ///
207    /// Arguments:
208    ///
209    /// * `num` - The number of the group to bind to
210    pub fn bind_group(&mut self, num: u16) {
211        assert!(!self.qh.is_null());
212        self.gh = unsafe { nflog_bind_group(self.qh,num) }
213    }
214
215    /// Unbinds a group handle
216    ///
217    /// Arguments:
218    ///
219    /// * `num` - The number of the group to unbind to
220    pub fn unbind_group(&mut self) {
221        assert!(!self.gh.is_null());
222        unsafe { nflog_unbind_group(self.gh); }
223        self.gh = std::ptr::null_mut();
224    }
225
226    /// Set the amount of packet data that nflog copies to userspace
227    ///
228    /// Arguments:
229    ///
230    /// * `mode` - The part of the packet that we are interested in
231    /// * `range` - Size of the packet that we want to get
232    ///
233    /// `mode` can be one of:
234    ///
235    /// * `NFULNL_COPY_NONE` - do not copy any data
236    /// * `NFULNL_COPY_META` - copy only packet metadata
237    /// * `NFULNL_COPY_PACKET` - copy entire packet
238    pub fn set_mode(&self, mode: CopyMode, range: u32) {
239        assert!(!self.gh.is_null());
240        let c_mode = match mode {
241            CopyMode::CopyNone => NFULNL_COPY_NONE,
242            CopyMode::CopyMeta => NFULNL_COPY_META,
243            CopyMode::CopyPacket => NFULNL_COPY_PACKET,
244        };
245        unsafe { nflog_set_mode(self.gh, c_mode, range); }
246    }
247
248    /// Sets the maximum time to push log buffer for this group
249    ///
250    /// Arguments:
251    ///
252    /// * `timeout` - Time to wait until the log buffer is pushed to userspace
253    ///
254    /// This function allows to set the maximum time that nflog waits until it
255    /// pushes the log buffer to userspace if no new logged packets have occured.
256    ///
257    /// Basically, nflog implements a buffer to reduce the computational cost of
258    /// delivering the log message to userspace.
259    pub fn set_timeout(&self, timeout: u32) {
260        assert!(!self.gh.is_null());
261        unsafe { nflog_set_timeout(self.gh, timeout); }
262    }
263
264    /// Sets the maximum amount of logs in buffer for this group
265    ///
266    /// Arguments:
267    ///
268    /// * `qthresh` - Maximum number of log entries
269    ///
270    /// This function determines the maximum number of log entries in the
271    /// buffer until it is pushed to userspace.
272    pub fn set_qthresh(&self, qthresh: u32) {
273        assert!(!self.gh.is_null());
274        unsafe { nflog_set_qthresh(self.gh, qthresh); }
275    }
276
277    /// Sets the size of the nflog buffer for this group
278    ///
279    /// Arguments:
280    ///
281    /// * `nlbufsiz` - Size of the nflog buffer
282    ///
283    /// This function sets the size (in bytes) of the buffer that is used to
284    /// stack log messages in nflog.
285    pub fn set_nlbufsiz(&self, nlbufsiz: u32) {
286        assert!(!self.gh.is_null());
287        unsafe { nflog_set_nlbufsiz(self.gh, nlbufsiz); }
288    }
289
290    /// Sets the nflog flags for this group
291    ///
292    /// Arguments:
293    ///
294    /// * `flags` - Flags that you want to set
295    ///
296    /// There are two existing flags:
297    ///
298    /// * `NFULNL_CFG_F_SEQ`: This enables local nflog sequence numbering.
299    /// * `NFULNL_CFG_F_SEQ_GLOBAL`: This enables global nflog sequence numbering.
300    pub fn set_flags(&self, flags: CfgFlags) {
301        assert!(!self.gh.is_null());
302        let c_flags : u16 = match flags {
303            CfgFlags::CfgFlagsSeq => NFULNL_CFG_F_SEQ,
304            CfgFlags::CfgFlagsSeqGlobal => NFULNL_CFG_F_SEQ_GLOBAL,
305        };
306        unsafe { nflog_set_flags(self.gh, c_flags); }
307    }
308
309
310    /// Registers the callback triggered when a packet is received
311    pub fn set_callback(&mut self, cb: NflogCallback) {
312        self.cb = Some(cb);
313        let self_ptr = unsafe { std::mem::transmute(&*self) };
314        unsafe { nflog_callback_register(self.gh, real_callback, self_ptr); };
315    }
316
317    /// Runs an infinite loop, waiting for packets and triggering the callback.
318    pub fn run_loop(&self) {
319        assert!(!self.gh.is_null());
320
321        let fd = self.fd();
322        let mut buf : [u8;65536] = [0;65536];
323        let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;
324        let buf_len = buf.len() as libc::size_t;
325
326        loop {
327            let rc = unsafe { libc::recv(fd,buf_ptr,buf_len,0) };
328            if rc < 0 { panic!("error in recv()"); };
329
330            let rv = unsafe { nflog_handle_packet(self.qh, buf_ptr, rc as libc::c_int) };
331            if rv < 0 { println!("error in nflog_handle_packet()"); }; // not critical
332        }
333    }
334}
335
336#[doc(hidden)]
337#[no_mangle]
338pub extern "C" fn real_callback(_g: *const libc::c_void, _nfmsg: *const libc::c_void, nfad: *const libc::c_void, data: *const libc::c_void ) {
339    let raw : *mut Queue = unsafe { std::mem::transmute(data) };
340
341    let ref mut q = unsafe { &*raw };
342    let mut msg = Message::new (nfad);
343
344    match q.cb {
345        None => panic!("no callback registered"),
346        Some(callback) => {
347            callback(&mut msg);
348            },
349    }
350}
351
352
353
354#[cfg(test)]
355mod tests {
356
357    extern crate libc;
358
359    #[test]
360    fn nflog_open() {
361        let mut q = ::Queue::new();
362
363        q.open();
364
365        let raw = q.qh as *const i32;
366        println!("nfq_open: 0x{:x}", unsafe{*raw});
367
368        assert!(!q.qh.is_null());
369
370        q.close();
371    }
372
373    #[test]
374    #[ignore]
375    fn nflog_bind() {
376        let mut q = ::Queue::new();
377
378        q.open();
379
380        let raw = q.qh as *const i32;
381        println!("nfq_open: 0x{:x}", unsafe{*raw});
382
383        assert!(!q.qh.is_null());
384
385        let rc = q.bind(libc::AF_INET);
386        println!("q.bind: {}", rc);
387        assert!(q.bind(libc::AF_INET) == 0);
388
389        q.close();
390    }
391}