extern crate libc;
pub use hwaddr::*;
mod hwaddr;
pub use message::*;
mod message;
type NfqueueHandle = *const libc::c_void;
type NfqueueQueueHandle = *const libc::c_void;
pub type NfqueueCallback = fn (&Message) -> ();
type NfqueueCCallback = extern "C" fn (*const libc::c_void, *const libc::c_void, *const libc::c_void, *const libc::c_void );
#[link(name = "netfilter_queue")]
extern {
fn nfq_open() -> NfqueueHandle;
fn nfq_close(qh: NfqueueHandle);
fn nfq_bind_pf (qh: NfqueueHandle, pf: libc::c_int) -> libc::c_int;
fn nfq_unbind_pf (qh: NfqueueHandle, pf: libc::c_int) -> libc::c_int;
fn nfq_fd (h: NfqueueHandle) -> libc::c_int;
fn nfq_create_queue(qh: NfqueueHandle, num: u16, cb: NfqueueCCallback, data: *mut libc::c_void) -> NfqueueQueueHandle;
fn nfq_destroy_queue(qh: NfqueueHandle) -> libc::c_int;
fn nfq_handle_packet(qh: NfqueueHandle, buf: *mut libc::c_void, rc: libc::c_int) -> libc::c_int;
fn nfq_set_mode (gh: NfqueueQueueHandle, mode: u8, range: u32) -> libc::c_int;
fn nfq_set_queuelen (gh: NfqueueQueueHandle, queuelen: u32) -> libc::c_int;
}
pub enum CopyMode {
CopyNone,
CopyMeta,
CopyPacket,
}
const NFQNL_COPY_NONE : u8 = 0x00;
const NFQNL_COPY_META : u8 = 0x01;
const NFQNL_COPY_PACKET : u8 = 0x02;
pub struct Queue<T> {
qh : NfqueueHandle,
qqh : NfqueueQueueHandle,
cb : Option<fn (&Message, &mut T) -> ()>,
data: T,
}
impl <T: Send> Queue<T> {
pub fn new(data: T) -> Queue<T> {
return Queue {
qh : std::ptr::null_mut(),
qqh : std::ptr::null_mut(),
cb: None,
data: data,
};
}
pub fn open(&mut self) {
self.qh = unsafe { nfq_open() };
}
pub fn close(&mut self) {
assert!(!self.qh.is_null());
unsafe { nfq_close(self.qh) };
self.qh = std::ptr::null_mut();
}
pub fn bind(&self, pf: libc::c_int) -> i32 {
assert!(!self.qh.is_null());
return unsafe { nfq_bind_pf(self.qh,pf) };
}
pub fn unbind(&self, pf: libc::c_int) -> i32 {
assert!(!self.qh.is_null());
return unsafe { nfq_unbind_pf(self.qh,pf) }
}
pub fn fd(&self) -> i32 {
assert!(!self.qh.is_null());
return unsafe { nfq_fd(self.qh) }
}
pub fn create_queue(&mut self, num: u16, cb: fn(&Message, &mut T)) {
assert!(!self.qh.is_null());
assert!(self.qqh.is_null());
let self_ptr = unsafe { std::mem::transmute(&*self) };
self.cb = Some(cb);
self.qqh = unsafe { nfq_create_queue(self.qh, num, real_callback::<T>, self_ptr) };
}
pub fn destroy_queue(&mut self) {
assert!(!self.qqh.is_null());
unsafe { nfq_destroy_queue(self.qqh); }
self.qqh = std::ptr::null_mut();
}
pub fn set_mode(&self, mode: CopyMode, range: u32) {
assert!(!self.qqh.is_null());
let c_mode = match mode {
CopyMode::CopyNone => NFQNL_COPY_NONE,
CopyMode::CopyMeta => NFQNL_COPY_META,
CopyMode::CopyPacket => NFQNL_COPY_PACKET,
};
unsafe { nfq_set_mode(self.qqh, c_mode, range); }
}
pub fn set_queuelen(&self, queuelen: u32) {
assert!(!self.qqh.is_null());
unsafe { nfq_set_queuelen(self.qqh, queuelen); }
}
pub fn run_loop(&self) {
assert!(!self.qh.is_null());
assert!(!self.qqh.is_null());
assert!(!self.cb.is_none());
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 { nfq_handle_packet(self.qh, buf_ptr, rc as libc::c_int) };
if rv < 0 { println!("error in nfq_handle_packet()"); }; }
}
}
#[doc(hidden)]
extern "C" fn real_callback<T>(qqh: *const libc::c_void, _nfmsg: *const libc::c_void, nfad: *const libc::c_void, data: *const libc::c_void ) {
let raw : *mut Queue<T> = unsafe { std::mem::transmute(data) };
let ref mut q = unsafe { &mut *raw };
let mut msg = Message::new (qqh, nfad);
match q.cb {
None => panic!("no callback registered"),
Some(callback) => {
callback(&mut msg, &mut q.data);
},
}
}
#[cfg(test)]
mod tests {
extern crate libc;
#[test]
fn nfqueue_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 nfqueue_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();
}
}