1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
//!  Netfilter NFLOG high-level bindings
//!
//! libnetfilter_log is a userspace library providing interface to packets that
//! have been logged by the kernel packet filter. It is is part of a system that
//! deprecates the old syslog/dmesg based packet logging.
//!
//! libnetfilter_log homepage is: http://netfilter.org/projects/libnetfilter_log/
//!
//! **Using NFLOG requires root privileges, or the `CAP_NET_ADMIN` capability**
//!
//! The code is available on [Github](https://github.com/chifflier/nflog-rs)
//!
//! # Example
//!
//! ```rust,ignore
//! extern crate libc;
//! extern crate nflog;
//! use std::fmt::Write;
//!
//! fn callback(msg: &nflog::Message) {
//!     println!(" -> msg: {}", msg);
//!     // this will send an error if there is no uid (for ex. incoming packets)
//!     println!(" -> uid: {}, gid: {}", msg.get_uid().unwrap(), msg.get_gid().unwrap());
//!     println!(" -> prefix: {}", msg.get_prefix().unwrap());
//!     println!(" -> seq: {}", msg.get_seq().unwrap_or(0xffff));
//!
//!     let payload_data = msg.get_payload();
//!     let mut s = String::new();
//!     for &byte in payload_data {
//!         write!(&mut s, "{:X} ", byte).unwrap();
//!     }
//!     println!("{}", s);
//!
//!     println!("XML\n{}", msg.as_xml_str(&[nflog::XMLFormatFlags::XmlAll]).unwrap());
//!
//! }
//!
//! fn main() {
//!     let mut q = nflog::Queue::new();
//!
//!     q.open();
//!
//!     let rc = q.bind(libc::AF_INET);
//!     assert!(rc == 0);
//!
//!     q.bind_group(0);
//!
//!     q.set_mode(nflog::CopyMode::CopyPacket, 0xffff);
//!     q.set_flags(nflog::CfgFlags::CfgFlagsSeq);
//!
//!     q.set_callback(callback);
//!     q.run_loop();
//!
//!     q.close();
//! }
//! ```


extern crate libc;

pub use hwaddr::*;
mod hwaddr;

pub use message::*;
mod message;

type NflogHandle = *const libc::c_void;
type NflogGroupHandle = *const libc::c_void;

/// Prototype for the callback function, triggered when a packet is received
pub type NflogCallback = fn (&Message) -> ();

type NflogCCallback = extern "C" fn (*const libc::c_void, *const libc::c_void, *const libc::c_void, *const libc::c_void );

#[link(name = "netfilter_log")]
extern {
    // library setup
    fn nflog_open() -> NflogHandle;
    fn nflog_close(qh: NflogHandle);
    fn nflog_bind_pf (qh: NflogHandle, pf: libc::c_int) -> libc::c_int;
    fn nflog_unbind_pf (qh: NflogHandle, pf: libc::c_int) -> libc::c_int;

    // group handling
    fn nflog_fd (h: NflogHandle) -> libc::c_int;
    fn nflog_bind_group (qh: NflogHandle, num: u16) -> NflogGroupHandle;
    fn nflog_unbind_group (gh: NflogGroupHandle) -> libc::c_int;
    fn nflog_set_mode (gh: NflogGroupHandle, mode: u8, range: u32) -> libc::c_int;
    fn nflog_set_timeout (gh: NflogGroupHandle, timeout: u32) -> libc::c_int;
    fn nflog_set_qthresh (gh: NflogGroupHandle, qthresh: u32) -> libc::c_int;
    fn nflog_set_nlbufsiz (gh: NflogGroupHandle, nlbufsiz: u32) -> libc::c_int;
    fn nflog_set_flags (gh: NflogGroupHandle, flags: u16) -> libc::c_int;

    // callback-related functions
    fn nflog_callback_register(gh: NflogGroupHandle, cb: NflogCCallback, data: *mut libc::c_void);
    fn nflog_handle_packet(qh: NflogHandle, buf: *mut libc::c_void, rc: libc::c_int) -> libc::c_int;
}




/// Copy modes
pub enum CopyMode {
    /// Do not copy packet contents nor metadata
    CopyNone,
    /// Copy only packet metadata, not payload
    CopyMeta,
    /// Copy packet metadata and not payload
    CopyPacket,
}
const NFULNL_COPY_NONE : u8   = 0x00;
const NFULNL_COPY_META : u8   = 0x01;
const NFULNL_COPY_PACKET : u8 = 0x02;

/// Configuration Flags
pub enum CfgFlags {
    CfgFlagsSeq,
    CfgFlagsSeqGlobal,
}
const NFULNL_CFG_F_SEQ : u16         = 0x0001;
const NFULNL_CFG_F_SEQ_GLOBAL : u16  = 0x0001;


/// Opaque struct `Queue`: abstracts an NFLOG queue
pub struct Queue {
    qh  : NflogHandle,
    gh  : NflogGroupHandle,
    cb : Option<NflogCallback>,
}


impl Queue {
    /// Creates a new, uninitialized, `Queue`.
    pub fn new() -> Queue {
        return Queue {
            qh : std::ptr::null_mut(),
            gh : std::ptr::null_mut(),
            cb: None,
        };
    }

    /// Opens a NFLOG handler
    ///
    /// This function obtains a netfilter log connection handle. When you are
    /// finished with the handle returned by this function, you should destroy it
    /// by calling `close()`.
    /// A new netlink connection is obtained internally
    /// and associated with the log connection handle returned.
    pub fn open(&mut self) {
        self.qh = unsafe { nflog_open() };
    }

    /// Closes a NFLOG handler
    ///
    /// This function closes the nflog handler and free associated resources.
    pub fn close(&mut self) {
        assert!(!self.qh.is_null());
        unsafe { nflog_close(self.qh) };
        self.qh = std::ptr::null_mut();
    }

    /// Bind a nflog handler to a given protocol family
    ///
    /// Binds the given log connection handle to process packets belonging to
    /// the given protocol family (ie. `PF_INET`, `PF_INET6`, etc).
    ///
    /// Arguments
    ///
    /// * `pf` - Protocol family (usually `AF_INET` or `AF_INET6`)
    ///
    /// Remarks:
    ///
    /// **Requires root privileges**
    pub fn bind(&self, pf: libc::c_int) -> i32 {
        assert!(!self.qh.is_null());
        return unsafe { nflog_bind_pf(self.qh,pf) };
    }

    /// Unbinds the nflog handler from a protocol family
    ///
    /// Unbinds the given nflog handle from processing packets belonging to the
    /// given protocol family.
    ///
    /// Arguments
    ///
    /// * `pf` - Protocol family (usually `AF_INET` or `AF_INET6`)
    ///
    /// Remarks:
    ///
    /// **Requires root privileges**
    pub fn unbind(&self, pf: libc::c_int) -> i32 {
        assert!(!self.qh.is_null());
        return unsafe { nflog_unbind_pf(self.qh,pf) }
    }

    /// Returns the C file descriptor associated with the nflog handler
    ///
    /// This function returns a file descriptor that can be used for
    /// communication over the netlink connection associated with the given log
    /// connection handle.
    pub fn fd(&self) -> i32 {
        assert!(!self.qh.is_null());
        return unsafe { nflog_fd(self.qh) }
    }

    ///  Binds a new handle to a specific group number.
    ///
    /// Arguments:
    ///
    /// * `num` - The number of the group to bind to
    pub fn bind_group(&mut self, num: u16) {
        assert!(!self.qh.is_null());
        self.gh = unsafe { nflog_bind_group(self.qh,num) }
    }

    /// Unbinds a group handle
    ///
    /// Arguments:
    ///
    /// * `num` - The number of the group to unbind to
    pub fn unbind_group(&mut self) {
        assert!(!self.gh.is_null());
        unsafe { nflog_unbind_group(self.gh); }
        self.gh = std::ptr::null_mut();
    }

    /// Set the amount of packet data that nflog copies to userspace
    ///
    /// Arguments:
    ///
    /// * `mode` - The part of the packet that we are interested in
    /// * `range` - Size of the packet that we want to get
    ///
    /// `mode` can be one of:
    ///
    /// * `NFULNL_COPY_NONE` - do not copy any data
    /// * `NFULNL_COPY_META` - copy only packet metadata
    /// * `NFULNL_COPY_PACKET` - copy entire packet
    pub fn set_mode(&self, mode: CopyMode, range: u32) {
        assert!(!self.gh.is_null());
        let c_mode = match mode {
            CopyMode::CopyNone => NFULNL_COPY_NONE,
            CopyMode::CopyMeta => NFULNL_COPY_META,
            CopyMode::CopyPacket => NFULNL_COPY_PACKET,
        };
        unsafe { nflog_set_mode(self.gh, c_mode, range); }
    }

    /// Sets the maximum time to push log buffer for this group
    ///
    /// Arguments:
    ///
    /// * `timeout` - Time to wait until the log buffer is pushed to userspace
    ///
    /// This function allows to set the maximum time that nflog waits until it
    /// pushes the log buffer to userspace if no new logged packets have occured.
    ///
    /// Basically, nflog implements a buffer to reduce the computational cost of
    /// delivering the log message to userspace.
    pub fn set_timeout(&self, timeout: u32) {
        assert!(!self.gh.is_null());
        unsafe { nflog_set_timeout(self.gh, timeout); }
    }

    /// Sets the maximum amount of logs in buffer for this group
    ///
    /// Arguments:
    ///
    /// * `qthresh` - Maximum number of log entries
    ///
    /// This function determines the maximum number of log entries in the
    /// buffer until it is pushed to userspace.
    pub fn set_qthresh(&self, qthresh: u32) {
        assert!(!self.gh.is_null());
        unsafe { nflog_set_qthresh(self.gh, qthresh); }
    }

    /// Sets the size of the nflog buffer for this group
    ///
    /// Arguments:
    ///
    /// * `nlbufsiz` - Size of the nflog buffer
    ///
    /// This function sets the size (in bytes) of the buffer that is used to
    /// stack log messages in nflog.
    pub fn set_nlbufsiz(&self, nlbufsiz: u32) {
        assert!(!self.gh.is_null());
        unsafe { nflog_set_nlbufsiz(self.gh, nlbufsiz); }
    }

    /// Sets the nflog flags for this group
    ///
    /// Arguments:
    ///
    /// * `flags` - Flags that you want to set
    ///
    /// There are two existing flags:
    ///
    /// * `NFULNL_CFG_F_SEQ`: This enables local nflog sequence numbering.
    /// * `NFULNL_CFG_F_SEQ_GLOBAL`: This enables global nflog sequence numbering.
    pub fn set_flags(&self, flags: CfgFlags) {
        assert!(!self.gh.is_null());
        let c_flags : u16 = match flags {
            CfgFlags::CfgFlagsSeq => NFULNL_CFG_F_SEQ,
            CfgFlags::CfgFlagsSeqGlobal => NFULNL_CFG_F_SEQ_GLOBAL,
        };
        unsafe { nflog_set_flags(self.gh, c_flags); }
    }


    /// Registers the callback triggered when a packet is received
    pub fn set_callback(&mut self, cb: NflogCallback) {
        self.cb = Some(cb);
        let self_ptr = unsafe { std::mem::transmute(&*self) };
        unsafe { nflog_callback_register(self.gh, real_callback, self_ptr); };
    }

    /// Runs an infinite loop, waiting for packets and triggering the callback.
    pub fn run_loop(&self) {
        assert!(!self.gh.is_null());

        let fd = self.fd();
        let mut buf : [u8;65536] = [0;65536];
        let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;
        let buf_len = buf.len() as libc::size_t;

        loop {
            let rc = unsafe { libc::recv(fd,buf_ptr,buf_len,0) };
            if rc < 0 { panic!("error in recv()"); };

            let rv = unsafe { nflog_handle_packet(self.qh, buf_ptr, rc as libc::c_int) };
            if rv < 0 { println!("error in nflog_handle_packet()"); }; // not critical
        }
    }
}

#[doc(hidden)]
#[no_mangle]
pub 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 ) {
    let raw : *mut Queue = unsafe { std::mem::transmute(data) };

    let ref mut q = unsafe { &*raw };
    let mut msg = Message::new (nfad);

    match q.cb {
        None => panic!("no callback registered"),
        Some(callback) => {
            callback(&mut msg);
            },
    }
}



#[cfg(test)]
mod tests {

    extern crate libc;

    #[test]
    fn nflog_open() {
        let mut q = ::Queue::new();

        q.open();

        let raw = q.qh as *const i32;
        println!("nfq_open: 0x{:x}", unsafe{*raw});

        assert!(!q.qh.is_null());

        q.close();
    }

    #[test]
    #[ignore]
    fn nflog_bind() {
        let mut q = ::Queue::new();

        q.open();

        let raw = q.qh as *const i32;
        println!("nfq_open: 0x{:x}", unsafe{*raw});

        assert!(!q.qh.is_null());

        let rc = q.bind(libc::AF_INET);
        println!("q.bind: {}", rc);
        assert!(q.bind(libc::AF_INET) == 0);

        q.close();
    }
}