mod binding;
use libc::{
bind, c_int, c_uint, close, nlattr, nlmsgerr, nlmsghdr, recv, sendto, setsockopt, sockaddr_nl,
socket, sysconf, AF_NETLINK, AF_UNSPEC, EINTR, ENOSPC, MSG_TRUNC, NETLINK_NETFILTER,
NETLINK_NO_ENOBUFS, NLMSG_DONE, NLMSG_ERROR, NLMSG_MIN_TYPE, NLM_F_DUMP_INTR, NLM_F_REQUEST,
NLM_F_ACK, PF_NETLINK, SOCK_RAW, SOL_NETLINK, _SC_PAGE_SIZE,
};
use binding::*;
use std::sync::Arc;
use std::io::Result;
use std::time::{Duration, SystemTime};
use std::collections::VecDeque;
const NLMSG_HDRLEN: usize = (std::mem::size_of::<nlmsghdr>() + 3) &! 3;
fn be16_to_cpu(x: u16) -> u16 {
u16::from_ne_bytes(x.to_be_bytes())
}
fn be32_to_cpu(x: u32) -> u32 {
u32::from_ne_bytes(x.to_be_bytes())
}
fn be64_to_cpu(x: u64) -> u64 {
u64::from_ne_bytes(x.to_be_bytes())
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
Drop,
Accept,
Queue(u16),
Repeat,
Stop,
}
unsafe fn nfq_hdr_put(nlmsg: &mut Nlmsg, typ: u16, queue_num: u16, ack: bool) {
let nlh = nlmsg.as_hdr();
(*nlh).nlmsg_type = ((NFNL_SUBSYS_QUEUE as u16) << 8) | typ;
(*nlh).nlmsg_flags = (NLM_F_REQUEST | if ack { NLM_F_ACK } else { 0 }) as u16;
let nfg: *mut nfgenmsg = nlmsg.extra_header();
(*nfg).nfgen_family = AF_UNSPEC as u8;
(*nfg).version = NFNETLINK_V0 as u8;
(*nfg).res_id = be16_to_cpu(queue_num);
}
mod nla {
#[inline]
pub unsafe fn get_type(attr: *const libc::nlattr) -> u16 { (*attr).nla_type & (libc::NLA_TYPE_MASK as u16) }
#[inline]
pub unsafe fn get_payload_len(attr: *const libc::nlattr) -> usize {
(*attr).nla_len as usize - std::mem::size_of::<libc::nlattr>()
}
#[inline]
pub unsafe fn get_payload<T>(attr: *const libc::nlattr) -> *const T {
(attr as usize + std::mem::size_of::<libc::nlattr>()) as _
}
#[inline]
pub unsafe fn get_u32(attr: *const libc::nlattr) -> u32 {
super::be32_to_cpu(*get_payload(attr))
}
}
struct AttrStream<'a> {
buf: &'a [u32],
}
impl<'a> Iterator for AttrStream<'a> {
type Item = *const nlattr;
fn next(&mut self) -> Option<Self::Item> {
let buf_left = self.buf.len() * 4;
if buf_left < std::mem::size_of::<libc::nlattr>() { return None }
let attr = self.buf.as_ptr() as *const nlattr;
let nla_len = unsafe { (*attr).nla_len } as usize;
assert!(buf_left >= nla_len, "truncated attribute {} < {}", buf_left, nla_len);
self.buf = &self.buf[(nla_len + 3) / 4 ..];
Some(attr)
}
}
struct Nlmsg<'a> {
buf: &'a mut [u32],
len: usize,
}
impl<'a> Nlmsg<'a> {
unsafe fn new(buf: &'a mut [u32]) -> Self {
let hdrlen = (std::mem::size_of::<nlmsghdr>() + 3) / 4;
std::ptr::write_bytes(buf.as_mut_ptr(), 0, hdrlen);
Self {
buf,
len: hdrlen,
}
}
fn extra_header<T>(&mut self) -> *mut T {
let len = (std::mem::size_of::<T>() + 3) / 4;
let ptr = unsafe { self.buf.as_mut_ptr().offset(self.len as isize) };
self.len += len;
assert!(self.len <= self.buf.len());
unsafe { std::ptr::write_bytes(ptr, 0, len) };
ptr as _
}
fn put_raw(&mut self, typ: u16, len: usize) -> *mut u32 {
let nla_len = len + std::mem::size_of::<nlattr>();
let attr: *mut nlattr = unsafe { self.buf.as_mut_ptr().offset(self.len as isize) as _ };
self.len += (nla_len + 3) / 4;
assert!(self.len <= self.buf.len());
unsafe {
(*attr).nla_type = typ;
(*attr).nla_len = nla_len as _;
nla::get_payload::<u32>(attr) as *mut u32
}
}
fn put_slice<T>(&mut self, typ: u16, value: &[T]) {
let ptr = self.put_raw(typ, value.len() * std::mem::size_of::<T>());
unsafe { std::ptr::copy_nonoverlapping(value.as_ptr(), ptr as _, value.len()) };
}
fn put_u32(&mut self, typ: u16, data: u32) {
let ptr = self.put_raw(typ, 4);
unsafe { *ptr = be32_to_cpu(data) };
}
fn as_hdr(&mut self) -> *mut nlmsghdr {
self.buf.as_mut_ptr() as _
}
unsafe fn adjust_len(&mut self) {
let hdr = self.as_hdr();
(*hdr).nlmsg_len = (self.len * 4) as _;
}
}
enum PayloadState {
Unmodified,
Modified,
Owned(Vec<u8>),
}
pub struct Message {
#[allow(dead_code)]
buffer: Arc<Vec<u32>>,
id: u16,
nfmark: u32,
nfmark_dirty: bool,
indev: u32,
outdev: u32,
physindev: u32,
physoutdev: u32,
orig_len: u32,
skbinfo: u32,
secctx: *const libc::c_char,
uid: Option<u32>,
gid: Option<u32>,
timestamp: *const nfqnl_msg_packet_timestamp,
hwaddr: *const nfqnl_msg_packet_hw,
hdr: *const nfqnl_msg_packet_hdr,
ct: Option<Conntrack>,
payload: &'static mut [u8],
payload_state: PayloadState,
verdict: Verdict,
}
unsafe impl Send for Message {}
impl Message {
#[inline]
pub fn get_nfmark(&self) -> u32 { self.nfmark }
#[inline]
pub fn set_nfmark(&mut self, mark: u32) {
self.nfmark = mark;
self.nfmark_dirty = true;
}
#[inline]
pub fn get_indev(&self) -> u32 { self.indev }
#[inline]
pub fn get_physindev(&self) -> u32 { self.physindev }
#[inline]
pub fn get_outdev(&self) -> u32 { self.outdev }
#[inline]
pub fn get_physoutdev(&self) -> u32 { self.physoutdev }
#[inline]
pub fn get_original_len(&self) -> usize {
if self.orig_len == 0 { self.payload.len() } else { self.orig_len as usize }
}
#[inline]
pub fn is_seg_offloaded(&self) -> bool {
self.skbinfo & NFQA_SKB_GSO != 0
}
#[inline]
pub fn is_checksum_ready(&self) -> bool {
self.skbinfo & NFQA_SKB_CSUMNOTREADY == 0
}
pub fn get_security_context(&self) -> Option<&str> {
if self.secctx.is_null() { return None }
unsafe { std::ffi::CStr::from_ptr(self.secctx).to_str().ok() }
}
#[inline]
pub fn get_uid(&self) -> Option<u32> { self.uid }
#[inline]
pub fn get_gid(&self) -> Option<u32> { self.gid }
pub fn get_timestamp(&self) -> Option<SystemTime> {
if self.timestamp.is_null() { return None }
unsafe {
let duration = Duration::from_secs(be64_to_cpu((*self.timestamp).sec)) +
Duration::from_micros(be64_to_cpu((*self.timestamp).usec));
Some(SystemTime::UNIX_EPOCH + duration)
}
}
pub fn get_hw_addr(&self) -> Option<&[u8]> {
if self.hwaddr.is_null() { return None }
unsafe {
let len = be16_to_cpu((*self.hwaddr).hw_addrlen) as usize;
Some(&(*self.hwaddr).hw_addr[..len])
}
}
#[inline]
pub fn get_hw_protocol(&self) -> u16 {
be16_to_cpu(unsafe { (*self.hdr).hw_protocol })
}
#[inline]
pub fn get_hook(&self) -> u8 {
unsafe { (*self.hdr).hook }
}
#[inline]
pub fn get_payload(&self) -> &[u8] {
match self.payload_state {
PayloadState::Unmodified |
PayloadState::Modified => self.payload,
PayloadState::Owned(ref vec) => &vec,
}
}
#[inline]
pub fn get_payload_mut(&mut self) -> &mut [u8] {
match self.payload_state {
PayloadState::Unmodified => {
self.payload_state = PayloadState::Modified;
self.payload
}
PayloadState::Modified => self.payload,
PayloadState::Owned(ref mut vec) => vec,
}
}
#[inline]
pub fn set_payload(&mut self, payload: impl Into<Vec<u8>>) {
self.payload_state = PayloadState::Owned(payload.into());
}
#[inline]
pub fn get_verdict(&self) -> Verdict {
self.verdict
}
#[inline]
pub fn set_verdict(&mut self, verdict: Verdict) {
self.verdict = verdict;
}
#[inline]
#[cfg_attr(not(feature = "ct"), doc(hidden))]
pub fn get_conntrack(&self) -> Option<&Conntrack> {
self.ct.as_ref()
}
}
#[cfg_attr(not(feature = "ct"), doc(hidden))]
pub struct Conntrack {
state: u32,
id: u32,
}
#[cfg_attr(not(feature = "ct"), doc(hidden))]
pub mod conntrack {
#[derive(Debug)]
pub enum State {
Established,
Related,
New,
EstablishedReply,
RelatedReply,
NewReply,
#[doc(hidden)]
Invalid,
}
}
impl Conntrack {
#[inline]
pub fn get_id(&self) -> u32 {
self.id
}
#[inline]
pub fn get_state(&self) -> conntrack::State {
use conntrack::State;
match self.state {
IP_CT_ESTABLISHED => State::Established,
IP_CT_RELATED => State::Related,
IP_CT_NEW => State::New,
IP_CT_ESTABLISHED_REPLY => State::EstablishedReply,
IP_CT_RELATED_REPLY => State::RelatedReply,
IP_CT_NEW_REPLY => State::NewReply,
_ => State::Invalid,
}
}
}
unsafe fn parse_ct_attr(attr: *const nlattr, ct: &mut Conntrack) {
let typ = nla::get_type(attr) as c_uint;
match typ {
CTA_ID => ct.id = nla::get_u32(attr),
_ => (),
}
}
unsafe fn parse_attr(attr: *const nlattr, message: &mut Message) {
let typ = nla::get_type(attr) as c_uint;
match typ {
NFQA_MARK => message.nfmark = nla::get_u32(attr),
NFQA_IFINDEX_INDEV => message.indev = nla::get_u32(attr),
NFQA_IFINDEX_OUTDEV => message.outdev = nla::get_u32(attr),
NFQA_IFINDEX_PHYSINDEV => message.physindev = nla::get_u32(attr),
NFQA_IFINDEX_PHYSOUTDEV => message.physoutdev = nla::get_u32(attr),
NFQA_HWADDR => {
assert!(nla::get_payload_len(attr) >= std::mem::size_of::<nfqnl_msg_packet_hw>());
message.hwaddr = nla::get_payload(attr);
}
NFQA_CAP_LEN => message.orig_len = nla::get_u32(attr),
NFQA_SKB_INFO => message.skbinfo = nla::get_u32(attr),
NFQA_SECCTX => message.secctx = nla::get_payload(attr),
NFQA_UID => message.uid = Some(nla::get_u32(attr)),
NFQA_GID => message.gid = Some(nla::get_u32(attr)),
NFQA_TIMESTAMP => {
assert!(nla::get_payload_len(attr) >= std::mem::size_of::<nfqnl_msg_packet_timestamp>());
message.timestamp = nla::get_payload(attr);
}
NFQA_PACKET_HDR => {
assert!(nla::get_payload_len(attr) >= std::mem::size_of::<nfqnl_msg_packet_hdr>());
message.hdr = nla::get_payload(attr);
}
NFQA_PAYLOAD => {
let len = nla::get_payload_len(attr);
let payload = nla::get_payload::<u8>(attr) as *mut u8;
message.payload = std::slice::from_raw_parts_mut(payload as *const u8 as *mut u8, len as usize);
}
NFQA_CT => {
if message.ct.is_none() { message.ct = Some(std::mem::zeroed()) }
let ct = message.ct.as_mut().unwrap();
for attr in (AttrStream { buf: std::slice::from_raw_parts(nla::get_payload(attr), (nla::get_payload_len(attr) + 3) / 4) }) {
parse_ct_attr(attr, ct);
}
}
NFQA_CT_INFO => {
if message.ct.is_none() { message.ct = Some(std::mem::zeroed()) }
message.ct.as_mut().unwrap().state = nla::get_u32(attr);
}
_ => (),
}
}
unsafe fn parse_msg(nlh: *const nlmsghdr, queue: &mut Queue) {
const NFGEN_HDRLEN: usize = (std::mem::size_of::<nfgenmsg>() + 3) &! 3;
let nfgenmsg = (nlh as usize + NLMSG_HDRLEN) as *const nfgenmsg;
let attr_start = (nfgenmsg as usize + NFGEN_HDRLEN) as *const u32;
let attr_len = (*nlh).nlmsg_len as usize - NLMSG_HDRLEN - NFGEN_HDRLEN;
let mut message = Message {
buffer: Arc::clone(&queue.buffer),
id: (*nfgenmsg).res_id,
hdr: std::ptr::null(),
nfmark: 0,
nfmark_dirty: false,
indev: 0,
outdev: 0,
physindev: 0,
physoutdev: 0,
orig_len: 0,
skbinfo: 0,
uid: None,
gid: None,
secctx: std::ptr::null(),
timestamp: std::ptr::null(),
hwaddr: std::ptr::null(),
ct: None,
payload: &mut [],
payload_state: PayloadState::Unmodified,
verdict: Verdict::Accept,
};
for attr in (AttrStream { buf: std::slice::from_raw_parts(attr_start, (attr_len + 3) / 4) }) {
parse_attr(attr, &mut message);
}
assert!(!message.hdr.is_null());
queue.queue.push_back(message);
}
pub struct Queue {
fd: libc::c_int,
bufsize: usize,
buffer: Arc<Vec<u32>>,
queue: VecDeque<Message>,
verdict_buffer: Option<Box<[u32; (8192 + 0x10000) / 4]>>,
}
unsafe impl Send for Queue {}
impl Queue {
pub fn open() -> std::io::Result<Queue> {
let fd = unsafe { socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER) };
if fd == -1 {
return Err(std::io::Error::last_os_error());
}
let metadata_size = std::cmp::min(unsafe { sysconf(_SC_PAGE_SIZE) as usize }, 8192);
let mut queue = Queue {
fd,
bufsize: metadata_size,
buffer: Arc::new(Vec::with_capacity(metadata_size)),
queue: VecDeque::new(),
verdict_buffer: Some(Box::new(unsafe { std::mem::zeroed() })),
};
let mut addr: sockaddr_nl = unsafe { std::mem::zeroed() };
addr.nl_family = AF_NETLINK as _;
if unsafe { bind(fd, &addr as *const sockaddr_nl as _, std::mem::size_of_val(&addr) as _) } < 0 {
return Err(std::io::Error::last_os_error());
}
queue.set_recv_enobufs(false)?;
Ok(queue)
}
pub fn set_recv_enobufs(&mut self, enable: bool) -> std::io::Result<()> {
let val = (!enable) as c_int;
if unsafe { setsockopt(
self.fd, SOL_NETLINK, NETLINK_NO_ENOBUFS,
&val as *const c_int as _, std::mem::size_of_val(&val) as _
) } < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
unsafe fn send_nlmsg(&self, mut nlh: Nlmsg) -> std::io::Result<()> {
nlh.adjust_len();
let nlh = nlh.as_hdr();
let mut addr: sockaddr_nl = std::mem::zeroed();
addr.nl_family = AF_NETLINK as _;
if sendto(
self.fd,
nlh as _, (*nlh).nlmsg_len as _, 0,
&addr as *const sockaddr_nl as _, std::mem::size_of_val(&addr) as _
) < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
pub fn bind(&mut self, queue_num: u16) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
let command = nfqnl_msg_config_cmd {
command: NFQNL_CFG_CMD_BIND as u8,
pf: 0,
_pad: 0,
};
nlmsg.put_slice(NFQA_CFG_CMD as u16, std::slice::from_ref(&command));
self.send_nlmsg(nlmsg)?;
self.recv_error()?;
}
self.set_copy_range(queue_num, 65535)
}
pub fn set_fail_open(&mut self, queue_num: u16, enabled: bool) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_FLAGS as u16, if enabled { NFQA_CFG_F_FAIL_OPEN } else { 0 });
nlmsg.put_u32(NFQA_CFG_MASK as u16, NFQA_CFG_F_FAIL_OPEN);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
pub fn set_recv_gso(&mut self, queue_num: u16, enabled: bool) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_FLAGS as u16, if enabled { NFQA_CFG_F_GSO } else { 0 });
nlmsg.put_u32(NFQA_CFG_MASK as u16, NFQA_CFG_F_GSO);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
pub fn set_recv_uid_gid(&mut self, queue_num: u16, enabled: bool) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_FLAGS as u16, if enabled { NFQA_CFG_F_UID_GID } else { 0 });
nlmsg.put_u32(NFQA_CFG_MASK as u16, NFQA_CFG_F_UID_GID);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
pub fn set_recv_security_context(&mut self, queue_num: u16, enabled: bool) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_FLAGS as u16, if enabled { NFQA_CFG_F_SECCTX } else { 0 });
nlmsg.put_u32(NFQA_CFG_MASK as u16, NFQA_CFG_F_SECCTX);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
#[cfg_attr(not(feature = "ct"), doc(hidden))]
pub fn set_recv_conntrack(&mut self, queue_num: u16, enabled: bool) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_FLAGS as u16, if enabled { NFQA_CFG_F_CONNTRACK } else { 0 });
nlmsg.put_u32(NFQA_CFG_MASK as u16, NFQA_CFG_F_CONNTRACK);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
pub fn set_copy_range(&mut self, queue_num: u16, range: u16) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
let params = nfqnl_msg_config_params {
copy_range: be32_to_cpu(range as u32),
copy_mode: if range == 0 { NFQNL_COPY_META } else { NFQNL_COPY_PACKET } as u8,
};
nlmsg.put_slice(NFQA_CFG_PARAMS as u16, std::slice::from_ref(¶ms));
self.send_nlmsg(nlmsg)?;
self.recv_error()?;
}
let metadata_size = std::cmp::min(unsafe { sysconf(_SC_PAGE_SIZE) as usize }, 8192);
self.bufsize = (metadata_size + range as usize + 3) / 4;
self.buffer = Arc::new(Vec::with_capacity(self.bufsize));
Ok(())
}
pub fn set_queue_max_len(&mut self, queue_num: u16, len: u32) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
nlmsg.put_u32(NFQA_CFG_QUEUE_MAXLEN as u16, len);
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
pub fn unbind(&mut self, queue_num: u16) -> Result<()> {
unsafe {
let mut buf = [0u32; 8192 / 4];
let mut nlmsg = Nlmsg::new(&mut buf);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_CONFIG as u16, queue_num, true);
let command = nfqnl_msg_config_cmd {
command: NFQNL_CFG_CMD_UNBIND as u8,
pf: 0,
_pad: 0,
};
nlmsg.put_slice(NFQA_CFG_CMD as u16, std::slice::from_ref(&command));
self.send_nlmsg(nlmsg)?;
self.recv_error()
}
}
fn recv_nlmsg(&mut self, mut callback: impl FnMut(&mut Self, *const nlmsghdr)) -> Result<bool> {
let buf = match Arc::get_mut(&mut self.buffer) {
Some(v) => v,
None => {
self.buffer = Arc::new(Vec::with_capacity(self.bufsize));
Arc::get_mut(&mut self.buffer).unwrap()
}
};
let buf_size = buf.capacity();
unsafe { buf.set_len(buf_size) }
let size = unsafe { recv(self.fd, buf.as_mut_ptr() as _, buf_size * 4, MSG_TRUNC) };
if size < 0 {
return Err(std::io::Error::last_os_error());
}
let mut size = size as usize;
if size > buf_size * 4 {
return Err(std::io::Error::from_raw_os_error(ENOSPC));
}
let mut nlh = buf.as_ptr() as *const nlmsghdr;
loop {
if size < NLMSG_HDRLEN { break }
let nlmsg_len = unsafe { (*nlh).nlmsg_len } as usize;
if size < nlmsg_len { break }
if unsafe { (*nlh).nlmsg_flags } & NLM_F_DUMP_INTR as u16 != 0 {
return Err(std::io::Error::from_raw_os_error(EINTR));
}
let nlmsg_type = unsafe { (*nlh).nlmsg_type } as c_int;
match nlmsg_type {
NLMSG_ERROR => {
assert!(nlmsg_len >= NLMSG_HDRLEN + std::mem::size_of::<nlmsgerr>());
let err = (nlh as usize + NLMSG_HDRLEN) as *const nlmsgerr;
let errno = unsafe { (*err).error }.abs();
if errno == 0 { return Ok(true) }
return Err(std::io::Error::from_raw_os_error(errno));
}
NLMSG_DONE => return Ok(true),
v if v < NLMSG_MIN_TYPE => (),
_ => callback(self, nlh),
}
let aligned_len = (nlmsg_len + 3) &! 3;
nlh = (nlh as usize + aligned_len) as *const nlmsghdr;
size = match size.checked_sub(aligned_len) {
Some(v) => v,
None => break,
}
}
Ok(false)
}
fn recv_error(&mut self) -> Result<()> {
while !self.recv_nlmsg(|_, _| ())? {}
Ok(())
}
pub fn recv(&mut self) -> Result<Message> {
while self.queue.is_empty() {
self.recv_nlmsg(|this, nlh| {
unsafe { parse_msg(nlh, this) };
})?;
}
let msg = self.queue.pop_front().unwrap();
Ok(msg)
}
pub fn verdict(&mut self, msg: Message) -> Result<()> {
unsafe {
let mut buffer = self.verdict_buffer.take().unwrap();
let mut nlmsg = Nlmsg::new(&mut buffer[..]);
nfq_hdr_put(&mut nlmsg, NFQNL_MSG_VERDICT as u16, be16_to_cpu(msg.id), false);
let vh = nfqnl_msg_verdict_hdr {
verdict: be32_to_cpu(match msg.verdict {
Verdict::Drop => 0,
Verdict::Accept => 1,
Verdict::Queue(num) => (num as u32) << 16 | 3,
Verdict::Repeat => 4,
Verdict::Stop => 5,
}),
id: (*msg.hdr).packet_id,
};
nlmsg.put_slice(NFQA_VERDICT_HDR as u16, std::slice::from_ref(&vh));
if msg.nfmark_dirty {
nlmsg.put_u32(NFQA_MARK as u16, msg.nfmark);
}
if let PayloadState::Unmodified = msg.payload_state {} else {
if msg.verdict != Verdict::Drop {
let payload = msg.get_payload();
nlmsg.put_slice(NFQA_PAYLOAD as u16, payload);
}
}
let ret = self.send_nlmsg(nlmsg);
self.verdict_buffer = Some(buffer);
ret
}
}
}
impl Drop for Queue {
fn drop(&mut self) {
unsafe { close(self.fd) };
}
}