pub mod bindings;
use crate::bindings::*;
use core::ffi::{c_int, c_uint, c_void};
use std::fmt::{Debug, Formatter};
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::ops::BitOr;
use std::os::fd::{AsRawFd, RawFd};
trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i8 i16 i32 i64 isize }
fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
if t.is_minus_one() {
Err(io::Error::last_os_error())
} else {
Ok(t)
}
}
#[derive(Copy, Clone)]
pub struct Msg<'a>(&'a nlmsghdr);
impl<'a> Msg<'a> {
unsafe fn from(nlh: *const nlmsghdr, len: c_int) -> Option<Self> {
unsafe {
if !mnl_nlmsg_ok(nlh, len) {
return None;
}
Some(Self(&*nlh))
}
}
fn next(&self, len: &mut c_int) -> Option<Self> {
unsafe {
let next = mnl_nlmsg_next(self.0, len as _);
Self::from(next, *len)
}
}
pub fn as_packet_msg(&self) -> Option<PacketMsg<'a>> {
if self.0.nlmsg_flags as u8 != NFQNL_MSG_PACKET as u8 {
return None;
}
let mut tb = [None; NFQA_MAX as usize + 1];
if unsafe {
mnl_attr_parse(
self.0 as _,
size_of::<nfgenmsg>() as c_uint,
Some(parse_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(PacketMsg(tb))
}
pub fn as_bytes(&self) -> &'a [u8] {
unsafe {
std::slice::from_raw_parts(self.0 as *const _ as *const u8, self.0.nlmsg_len as usize)
}
}
}
extern "C" fn parse_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, NFQA_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
NFQA_MARK
| NFQA_IFINDEX_INDEV
| NFQA_IFINDEX_OUTDEV
| NFQA_IFINDEX_PHYSINDEV
| NFQA_IFINDEX_PHYSOUTDEV
| NFQA_CT_INFO
| NFQA_CAP_LEN
| NFQA_SKB_INFO
| NFQA_UID
| NFQA_GID
| NFQA_PRIORITY
| NFQA_CGROUP_CLASSID => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
return MNL_CB_ERROR;
}
}
NFQA_PACKET_HDR => {
if unsafe {
mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, size_of::<nfqnl_msg_packet_hdr>())
} < 0
{
return MNL_CB_ERROR;
}
}
NFQA_TIMESTAMP => {
if unsafe {
mnl_attr_validate2(
attr,
MNL_TYPE_UNSPEC,
size_of::<nfqnl_msg_packet_timestamp>(),
)
} < 0
{
return MNL_CB_ERROR;
}
}
NFQA_HWADDR => {
if unsafe {
mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, size_of::<nfqnl_msg_packet_hw>())
} < 0
{
return MNL_CB_ERROR;
}
}
NFQA_CT | NFQA_VLAN => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
return MNL_CB_ERROR;
}
}
NFQA_PAYLOAD | NFQA_SECCTX | NFQA_L2HDR => {}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
#[derive(Copy, Clone)]
pub struct Messages<'a>(c_int, Option<Msg<'a>>);
impl Messages<'_> {
pub fn new(buf: &[u8]) -> Self {
let len = buf.len() as c_int;
Self(len, unsafe { Msg::from(buf.as_ptr() as _, len) })
}
}
impl<'a> Iterator for Messages<'a> {
type Item = Msg<'a>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.1.as_ref().and_then(|msg| msg.next(&mut self.0));
let msg = self.1.take();
self.1 = next;
msg
}
}
pub const SOCKET_BUFFER_SIZE: usize = 8192;
#[derive(Copy, Clone)]
pub struct Socket<'a>(&'a mnl_socket);
impl Socket<'_> {
pub fn new(flags: i32) -> io::Result<Self> {
unsafe {
let socket = mnl_socket_open2(NETLINK_NETFILTER as c_int, flags);
if socket.is_null() {
return Err(io::Error::last_os_error());
}
let mut ret: c_int = 1;
cvt(mnl_socket_setsockopt(
socket as _,
NETLINK_NO_ENOBUFS as c_int,
&mut ret as *mut _ as _,
size_of::<c_int>() as _,
))?;
cvt(mnl_socket_bind(socket, 0, MNL_SOCKET_AUTOPID as _))?;
Ok(Self(&*socket))
}
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<Messages<'_>> {
unsafe {
let ret = cvt(mnl_socket_recvfrom(
self.0,
buf.as_mut_ptr() as _,
buf.len(),
))? as usize;
if mnl_cb_run(
buf.as_ptr() as _,
ret,
0,
mnl_socket_get_portid(self.0),
None,
std::ptr::null_mut(),
) < 0
{
return Err(io::Error::last_os_error());
}
Ok(Messages::new(&buf[..ret]))
}
}
pub fn send(&self, msg: Msg) -> io::Result<()> {
cvt(unsafe {
mnl_socket_sendto(self.0, msg.as_bytes().as_ptr() as _, msg.as_bytes().len())
})?;
Ok(())
}
}
impl AsRawFd for Socket<'_> {
fn as_raw_fd(&self) -> RawFd {
unsafe { mnl_socket_get_fd(self.0) }
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CopyMode {
Meta = NFQNL_COPY_META as u8,
Packet = NFQNL_COPY_PACKET as u8,
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CmdType {
None = NFQNL_CFG_CMD_NONE as u8,
Bind = NFQNL_CFG_CMD_BIND as u8,
Unbind = NFQNL_CFG_CMD_UNBIND as u8,
}
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum CfgFlag {
FailOpen = NFQA_CFG_F_FAIL_OPEN,
ConnTrack = NFQA_CFG_F_CONNTRACK,
Gso = NFQA_CFG_F_GSO,
UidGid = NFQA_CFG_F_UID_GID,
SecCtx = NFQA_CFG_F_SECCTX,
}
impl BitOr for CfgFlag {
type Output = u32;
fn bitor(self, rhs: Self) -> Self::Output {
(self as u32) | (rhs as u32)
}
}
impl BitOr<CfgFlag> for u32 {
type Output = u32;
fn bitor(self, rhs: CfgFlag) -> Self::Output {
self | (rhs as u32)
}
}
pub enum VerdictType {
Drop,
Accept,
Queue(u32),
Repeat,
}
pub struct MsgBuilder<'a>(&'a mut nlmsghdr);
impl<'a> MsgBuilder<'a> {
pub fn new(buf: &'a mut [u8], queue_num: u16) -> Self {
unsafe {
let nlh = mnl_nlmsg_put_header(buf.as_mut_ptr() as _);
(*nlh).nlmsg_type = (NFNL_SUBSYS_QUEUE as u16) << 8;
(*nlh).nlmsg_flags = NLM_F_REQUEST as u16;
let nfg = mnl_nlmsg_put_extra_header(nlh, size_of::<nfgenmsg>()) as *mut nfgenmsg;
(*nfg).nfgen_family = 0;
(*nfg).version = NFNETLINK_V0 as u8;
(*nfg).res_id = queue_num.to_be();
Self(&mut *nlh)
}
}
pub fn ack(self) -> Self {
self.0.nlmsg_flags |= NLM_F_ACK as u16;
self
}
pub fn cfg(self) -> CfgMsgBuilder<'a> {
self.0.nlmsg_type |= NFQNL_MSG_CONFIG as u16;
CfgMsgBuilder(self)
}
pub fn verdict(self) -> VerdictMsgBuilder<'a> {
self.0.nlmsg_type |= NFQNL_MSG_VERDICT as u16;
VerdictMsgBuilder(self)
}
pub fn build(self) -> Msg<'a> {
Msg(self.0)
}
}
pub struct CfgMsgBuilder<'a>(MsgBuilder<'a>);
impl<'a> CfgMsgBuilder<'a> {
pub fn cmd(self, cmd: CmdType) -> Self {
unsafe {
mnl_attr_put(
self.0.0,
NFQA_CFG_CMD as u16,
size_of::<nfqnl_msg_config_cmd>(),
&nfqnl_msg_config_cmd {
command: cmd as u8,
_pad: 0,
pf: 0,
} as *const _ as _,
);
}
self
}
pub fn params(self, copy_range: u32, copy_mode: CopyMode) -> Self {
unsafe {
mnl_attr_put(
self.0.0,
NFQA_CFG_PARAMS as u16,
size_of::<nfqnl_msg_config_params>(),
&nfqnl_msg_config_params {
copy_range: copy_range.to_be(),
copy_mode: copy_mode as u8,
} as *const _ as _,
);
}
self
}
pub fn queue_maxlen(self, queue_maxlen: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, NFQA_CFG_QUEUE_MAXLEN as u16, queue_maxlen.to_be());
}
self
}
pub fn flags(self, flags: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, NFQA_CFG_FLAGS as u16, flags.to_be());
mnl_attr_put_u32(self.0.0, NFQA_CFG_MASK as u16, flags.to_be());
}
self
}
pub fn build(self) -> Msg<'a> {
self.0.build()
}
}
pub struct VerdictMsgBuilder<'a>(MsgBuilder<'a>);
impl<'a> VerdictMsgBuilder<'a> {
pub fn verdict_hdr(self, id: u32, verdict: VerdictType) -> Self {
unsafe {
mnl_attr_put(
self.0.0,
NFQA_VERDICT_HDR as u16,
size_of::<nfqnl_msg_verdict_hdr>(),
&nfqnl_msg_verdict_hdr {
verdict: match verdict {
VerdictType::Drop => NF_DROP,
VerdictType::Accept => NF_ACCEPT,
VerdictType::Queue(num) => ((num << 16) & NF_VERDICT_QMASK) | NF_QUEUE,
VerdictType::Repeat => NF_REPEAT,
}
.to_be(),
id: id.to_be(),
} as *const _ as _,
);
}
self
}
pub fn ct(self) -> VerdictCtBuilder<'a> {
unsafe {
let start = mnl_attr_nest_start(self.0.0, NFQA_CT as u16);
VerdictCtBuilder(self.0, start)
}
}
pub fn payload(self, payload: &[u8]) -> Self {
unsafe {
mnl_attr_put(
self.0.0,
NFQA_PAYLOAD as u16,
payload.len(),
&payload as *const _ as _,
)
}
self
}
pub fn mark(self, mark: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, NFQA_MARK as u16, mark.to_be());
}
self
}
pub fn priority(self, priority: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, NFQA_PRIORITY as u16, priority.to_be());
}
self
}
pub fn build(self) -> Msg<'a> {
self.0.build()
}
}
pub struct VerdictCtBuilder<'a>(MsgBuilder<'a>, *mut nlattr);
impl<'a> VerdictCtBuilder<'a> {
pub fn timeout(self, timeout: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, CTA_TIMEOUT as u16, timeout.to_be());
}
self
}
pub fn status(self, status: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, CTA_STATUS as u16, status.to_be());
}
self
}
pub fn mark(self, mark: u32) -> Self {
unsafe {
mnl_attr_put_u32(self.0.0, CTA_MARK as u16, mark.to_be());
}
self
}
pub fn build(self) -> Msg<'a> {
unsafe { mnl_attr_nest_end(self.0.0, self.1) }
self.0.build()
}
}
#[derive(Debug, Copy, Clone)]
pub struct PacketHdr {
pub packet_id: u32,
pub hw_protocol: u16,
pub hook: u8,
}
#[derive(Debug, Copy, Clone)]
pub struct Timestamp {
pub sec: u64,
pub usec: u64,
}
#[derive(Debug, Copy, Clone)]
pub struct HwAddr {
pub hw_addrlen: u16,
pub hw_addr: [u8; 8],
}
#[derive(Copy, Clone)]
pub struct PacketMsg<'a>([Option<&'a nlattr>; NFQA_MAX as usize + 1]);
impl<'a> PacketMsg<'a> {
pub fn packet_hdr(&self) -> Option<PacketHdr> {
self.0[NFQA_PACKET_HDR as usize].map(|nla| unsafe {
let packet_hdr = mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_hdr;
PacketHdr {
packet_id: u32::from_be((*packet_hdr).packet_id),
hw_protocol: u16::from_be((*packet_hdr).hw_protocol),
hook: (*packet_hdr).hook,
}
})
}
pub fn mark(&self) -> Option<u32> {
self.0[NFQA_MARK as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn timestamp(&self) -> Option<Timestamp> {
self.0[NFQA_TIMESTAMP as usize].map(|nla| unsafe {
let timestamp = std::ptr::read_unaligned(
mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_timestamp
);
Timestamp {
sec: u64::from_be(timestamp.sec),
usec: u64::from_be(timestamp.usec),
}
})
}
pub fn indev(&self) -> Option<u32> {
self.0[NFQA_IFINDEX_INDEV as usize]
.map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn outdev(&self) -> Option<u32> {
self.0[NFQA_IFINDEX_OUTDEV as usize]
.map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn phys_indev(&self) -> Option<u32> {
self.0[NFQA_IFINDEX_PHYSINDEV as usize]
.map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn phys_outdev(&self) -> Option<u32> {
self.0[NFQA_IFINDEX_PHYSOUTDEV as usize]
.map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn hw_addr(&self) -> Option<HwAddr> {
self.0[NFQA_HWADDR as usize].map(|nla| unsafe {
let hw_addr = mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_hw;
HwAddr {
hw_addrlen: u16::from_be((*hw_addr).hw_addrlen),
hw_addr: (*hw_addr).hw_addr,
}
})
}
pub fn payload(&self) -> Option<&'a [u8]> {
self.0[NFQA_PAYLOAD as usize].map(|nla| unsafe {
std::slice::from_raw_parts(
mnl_attr_get_payload(nla) as *const u8,
mnl_attr_get_payload_len(nla) as usize,
)
})
}
pub fn ct(&self) -> Option<Ct<'a>> {
self.0[NFQA_CT as usize].map(|nla| {
let mut tb = [None; CTA_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(nla, Some(parse_ct_attr_cb), tb.as_mut_ptr() as *mut c_void)
} < 0
{
return None;
}
Some(Ct(tb))
})?
}
pub fn ct_info(&self) -> Option<u32> {
self.0[NFQA_CT_INFO as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn cap_len(&self) -> Option<u32> {
self.0[NFQA_CAP_LEN as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn skb_info(&self) -> Option<u32> {
self.0[NFQA_SKB_INFO as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn uid(&self) -> Option<u32> {
self.0[NFQA_UID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn gid(&self) -> Option<u32> {
self.0[NFQA_GID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn secctx(&self) -> Option<&'a [u8]> {
self.0[NFQA_SECCTX as usize].map(|nla| unsafe {
std::slice::from_raw_parts(
mnl_attr_get_payload(nla) as *const u8,
mnl_attr_get_payload_len(nla) as usize,
)
})
}
pub fn vlan(&self) -> Option<Vlan<'a>> {
self.0[NFQA_VLAN as usize].map(|nla| {
let mut tb = [None; NFQA_VLAN_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_vlan_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(Vlan(tb))
})?
}
pub fn l2hdr(&self) -> Option<&'a [u8]> {
self.0[NFQA_L2HDR as usize].map(|nla| unsafe {
std::slice::from_raw_parts(
mnl_attr_get_payload(nla) as *const u8,
mnl_attr_get_payload_len(nla) as usize,
)
})
}
pub fn priority(&self) -> Option<u32> {
self.0[NFQA_PRIORITY as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn cgroup_classid(&self) -> Option<u32> {
self.0[NFQA_CGROUP_CLASSID as usize]
.map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
}
impl Debug for PacketMsg<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PacketMsg")
.field("packet_hdr", &self.packet_hdr())
.field("mark", &self.mark())
.field("timestamp", &self.timestamp())
.field("indev", &self.indev())
.field("outdev", &self.outdev())
.field("phys_indev", &self.phys_indev())
.field("phys_outdev", &self.phys_outdev())
.field("hw_addr", &self.hw_addr())
.field("payload", &self.payload())
.field("ct", &self.ct())
.field("ct_info", &self.ct_info())
.field("cap_len", &self.cap_len())
.field("skb_info", &self.skb_info())
.field("uid", &self.uid())
.field("gid", &self.gid())
.field("secctx", &self.secctx())
.field("vlan", &self.vlan())
.field("l2hdr", &self.l2hdr())
.field("priority", &self.priority())
.field("cgroup_classid", &self.cgroup_classid())
.finish()
}
}
extern "C" fn parse_ct_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_ZONE => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_STATUS | CTA_TIMEOUT | CTA_MARK | CTA_USE | CTA_ID => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_TUPLE_ORIG | CTA_TUPLE_REPLY | CTA_TUPLE_MASTER | CTA_PROTOINFO | CTA_COUNTERS_ORIG
| CTA_COUNTERS_REPLY | CTA_TIMESTAMP => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
extern "C" fn parse_vlan_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, NFQA_VLAN_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
NFQA_VLAN_PROTO | NFQA_VLAN_TCI => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
#[derive(Copy, Clone)]
pub struct Vlan<'a>([Option<&'a nlattr>; NFQA_VLAN_MAX as usize + 1]);
impl Vlan<'_> {
pub fn proto(&self) -> Option<u16> {
self.0[NFQA_VLAN_PROTO as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn tci(&self) -> Option<u16> {
self.0[NFQA_VLAN_TCI as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
}
impl Debug for Vlan<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Vlan")
.field("proto", &self.proto())
.field("tci", &self.tci())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct Ct<'a>([Option<&'a nlattr>; CTA_MAX as usize + 1]);
impl<'a> Ct<'a> {
fn tuple(&self, tuple: usize) -> Option<CtTuple<'a>> {
self.0[tuple].map(|nla| {
let mut tb = [None; CTA_TUPLE_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_tuple_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtTuple(tb))
})?
}
pub fn tuple_orig(&self) -> Option<CtTuple<'a>> {
self.tuple(CTA_TUPLE_ORIG as usize)
}
pub fn tuple_reply(&self) -> Option<CtTuple<'a>> {
self.tuple(CTA_TUPLE_REPLY as usize)
}
pub fn tuple_master(&self) -> Option<CtTuple<'a>> {
self.tuple(CTA_TUPLE_MASTER as usize)
}
pub fn status(&self) -> Option<u32> {
self.0[CTA_STATUS as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn protoinfo(&self) -> Option<CtProtoInfo<'a>> {
self.0[CTA_PROTOINFO as usize].map(|nla| {
let mut tb = [None; CTA_PROTOINFO_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_protoinfo_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtProtoInfo(tb))
})?
}
fn counters(&self, counters: usize) -> Option<CtCounters<'a>> {
self.0[counters].map(|nla| {
let mut tb = [None; CTA_COUNTERS_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_counters_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtCounters(tb))
})?
}
pub fn counters_orig(&self) -> Option<CtCounters<'a>> {
self.counters(CTA_COUNTERS_ORIG as usize)
}
pub fn counters_reply(&self) -> Option<CtCounters<'a>> {
self.counters(CTA_COUNTERS_REPLY as usize)
}
pub fn timeout(&self) -> Option<u32> {
self.0[CTA_TIMEOUT as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn mark(&self) -> Option<u32> {
self.0[CTA_MARK as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn r#use(&self) -> Option<u32> {
self.0[CTA_USE as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn id(&self) -> Option<u32> {
self.0[CTA_ID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
}
pub fn zone(&self) -> Option<u16> {
self.0[CTA_ZONE as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn timestamp(&self) -> Option<CtTimestamp<'a>> {
self.0[CTA_TIMESTAMP as usize].map(|nla| {
let mut tb = [None; CTA_TIMESTAMP_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_timestamp_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtTimestamp(tb))
})?
}
}
impl Debug for Ct<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ct")
.field("tuple_orig", &self.tuple_orig())
.field("tuple_reply", &self.tuple_reply())
.field("tuple_master", &self.tuple_master())
.field("status", &self.status())
.field("protoinfo", &self.protoinfo())
.field("counters_orig", &self.counters_orig())
.field("counters_reply", &self.counters_reply())
.field("timeout", &self.timeout())
.field("mark", &self.mark())
.field("use", &self.r#use())
.field("id", &self.id())
.field("zone", &self.zone())
.field("timestamp", &self.timestamp())
.finish()
}
}
extern "C" fn parse_tuple_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_TUPLE_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_TUPLE_IP | CTA_TUPLE_PROTO => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_TUPLE_ZONE => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
extern "C" fn parse_protoinfo_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_PROTOINFO_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_PROTOINFO_TCP => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_PROTOINFO_DCCP | CTA_PROTOINFO_SCTP => {}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
extern "C" fn parse_counters_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_COUNTERS_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_COUNTERS_PACKETS | CTA_COUNTERS_BYTES => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U64) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
extern "C" fn parse_timestamp_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_TIMESTAMP_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_TIMESTAMP_START | CTA_TIMESTAMP_STOP => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U64) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
#[derive(Copy, Clone)]
pub struct CtTuple<'a>([Option<&'a nlattr>; CTA_TUPLE_MAX as usize + 1]);
impl<'a> CtTuple<'a> {
pub fn ip(&self) -> Option<CtTupleIp<'a>> {
self.0[CTA_TUPLE_IP as usize].map(|nla| {
let mut tb = [None; CTA_IP_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(nla, Some(parse_ip_attr_cb), tb.as_mut_ptr() as *mut c_void)
} < 0
{
return None;
}
Some(CtTupleIp(tb))
})?
}
pub fn proto(&self) -> Option<CtTupleProto<'a>> {
self.0[CTA_TUPLE_PROTO as usize].map(|nla| {
let mut tb = [None; CTA_PROTO_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_proto_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtTupleProto(tb))
})?
}
pub fn zone(&self) -> Option<u16> {
self.0[CTA_TUPLE_ZONE as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
}
impl Debug for CtTuple<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtTuple")
.field("ip", &self.ip())
.field("proto", &self.proto())
.field("zone", &self.zone())
.finish()
}
}
extern "C" fn parse_ip_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_IP_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_IP_V4_SRC | CTA_IP_V4_DST => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_IP_V6_SRC | CTA_IP_V6_DST => {
if unsafe { mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, 16) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
extern "C" fn parse_proto_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_PROTO_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_PROTO_NUM
| CTA_PROTO_ICMP_TYPE
| CTA_PROTO_ICMP_CODE
| CTA_PROTO_ICMPV6_TYPE
| CTA_PROTO_ICMPV6_CODE => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U8) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_PROTO_SRC_PORT | CTA_PROTO_DST_PORT | CTA_PROTO_ICMP_ID | CTA_PROTO_ICMPV6_ID => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
#[derive(Copy, Clone)]
pub struct CtTupleIp<'a>([Option<&'a nlattr>; CTA_IP_MAX as usize + 1]);
impl CtTupleIp<'_> {
pub fn ipv4_src(&self) -> Option<Ipv4Addr> {
self.0[CTA_IP_V4_SRC as usize]
.map(|nla| Ipv4Addr::from(u32::from_be(unsafe { mnl_attr_get_u32(nla) })))
}
pub fn ipv4_dst(&self) -> Option<Ipv4Addr> {
self.0[CTA_IP_V4_DST as usize]
.map(|nla| Ipv4Addr::from(u32::from_be(unsafe { mnl_attr_get_u32(nla) })))
}
fn ipv6(&self, r#type: usize) -> Option<Ipv6Addr> {
self.0[r#type].map(|nla| {
let mut bytes = [0u8; 16];
unsafe {
std::ptr::copy_nonoverlapping(
mnl_attr_get_payload(nla) as *const u8,
bytes.as_mut_ptr(),
16,
);
}
Ipv6Addr::from(bytes)
})
}
pub fn ipv6_src(&self) -> Option<Ipv6Addr> {
self.ipv6(CTA_IP_V6_SRC as usize)
}
pub fn ipv6_dst(&self) -> Option<Ipv6Addr> {
self.ipv6(CTA_IP_V6_DST as usize)
}
}
impl Debug for CtTupleIp<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtTupleIp")
.field("ipv4_src", &self.ipv4_src())
.field("ipv4_dst", &self.ipv4_dst())
.field("ipv6_src", &self.ipv6_src())
.field("ipv6_dst", &self.ipv6_dst())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CtTupleProto<'a>([Option<&'a nlattr>; CTA_PROTO_MAX as usize + 1]);
impl CtTupleProto<'_> {
pub fn num(&self) -> Option<u8> {
self.0[CTA_PROTO_NUM as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn src_port(&self) -> Option<u16> {
self.0[CTA_PROTO_SRC_PORT as usize]
.map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn dst_port(&self) -> Option<u16> {
self.0[CTA_PROTO_DST_PORT as usize]
.map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn icmp_id(&self) -> Option<u16> {
self.0[CTA_PROTO_ICMP_ID as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn icmp_type(&self) -> Option<u8> {
self.0[CTA_PROTO_ICMP_TYPE as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn icmp_code(&self) -> Option<u8> {
self.0[CTA_PROTO_ICMP_CODE as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn icmp6_id(&self) -> Option<u16> {
self.0[CTA_PROTO_ICMPV6_ID as usize]
.map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
}
pub fn icmp6_type(&self) -> Option<u8> {
self.0[CTA_PROTO_ICMPV6_TYPE as usize]
.map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn icmp6_code(&self) -> Option<u8> {
self.0[CTA_PROTO_ICMPV6_CODE as usize]
.map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
}
impl Debug for CtTupleProto<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtTupleProto")
.field("num", &self.num())
.field("src_port", &self.src_port())
.field("dst_port", &self.dst_port())
.field("icmp_id", &self.icmp_id())
.field("icmp_type", &self.icmp_type())
.field("icmp_code", &self.icmp_code())
.field("icmp6_id", &self.icmp6_id())
.field("icmp6_type", &self.icmp6_type())
.field("icmp6_code", &self.icmp6_code())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CtProtoInfo<'a>([Option<&'a nlattr>; CTA_PROTOINFO_MAX as usize + 1]);
impl<'a> CtProtoInfo<'a> {
pub fn tcp(&self) -> Option<CtProtoInfoTcp<'a>> {
self.0[CTA_PROTOINFO_TCP as usize].map(|nla| {
let mut tb = [None; CTA_PROTOINFO_TCP_MAX as usize + 1];
if unsafe {
mnl_attr_parse_nested(
nla,
Some(parse_protoinfo_tcp_attr_cb),
tb.as_mut_ptr() as *mut c_void,
)
} < 0
{
return None;
}
Some(CtProtoInfoTcp(tb))
})?
}
}
impl Debug for CtProtoInfo<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtProtoInfo")
.field("tcp", &self.tcp())
.finish()
}
}
extern "C" fn parse_protoinfo_tcp_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
let tb = data as *mut Option<&nlattr>;
let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
if unsafe { mnl_attr_type_valid(attr, CTA_PROTOINFO_TCP_MAX as u16) } < 0 {
return MNL_CB_OK as c_int;
}
match attr_type {
CTA_PROTOINFO_TCP_STATE
| CTA_PROTOINFO_TCP_WSCALE_ORIGINAL
| CTA_PROTOINFO_TCP_WSCALE_REPLY => {
if unsafe { mnl_attr_validate(attr, MNL_TYPE_U8) } < 0 {
return MNL_CB_ERROR;
}
}
CTA_PROTOINFO_TCP_FLAGS_ORIGINAL | CTA_PROTOINFO_TCP_FLAGS_REPLY => {
if unsafe { mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, 2) } < 0 {
return MNL_CB_ERROR;
}
}
_ => {}
}
unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
MNL_CB_OK as c_int
}
#[derive(Copy, Clone)]
pub struct CtProtoInfoTcp<'a>([Option<&'a nlattr>; CTA_PROTOINFO_TCP_MAX as usize + 1]);
impl<'a> CtProtoInfoTcp<'a> {
pub fn state(&self) -> Option<u8> {
self.0[CTA_PROTOINFO_TCP_STATE as usize]
.map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn wscale_original(&self) -> Option<u8> {
self.0[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL as usize]
.map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
pub fn wscale_reply(&self) -> Option<u8> {
self.0[CTA_PROTOINFO_TCP_WSCALE_REPLY as usize]
.map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
}
}
impl Debug for CtProtoInfoTcp<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtProtoInfoTcp")
.field("state", &self.state())
.field("wscale_original", &self.wscale_original())
.field("wscale_reply", &self.wscale_reply())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CtCounters<'a>([Option<&'a nlattr>; CTA_COUNTERS_MAX as usize + 1]);
impl<'a> CtCounters<'a> {
pub fn packets(&self) -> Option<u64> {
self.0[CTA_COUNTERS_PACKETS as usize]
.map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
}
pub fn bytes(&self) -> Option<u64> {
self.0[CTA_COUNTERS_BYTES as usize]
.map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
}
}
impl Debug for CtCounters<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtCounters")
.field("packets", &self.packets())
.field("bytes", &self.bytes())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CtTimestamp<'a>([Option<&'a nlattr>; CTA_TIMESTAMP_MAX as usize + 1]);
impl<'a> CtTimestamp<'a> {
pub fn start(&self) -> Option<u64> {
self.0[CTA_TIMESTAMP_START as usize]
.map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
}
pub fn stop(&self) -> Option<u64> {
self.0[CTA_TIMESTAMP_STOP as usize]
.map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
}
}
impl Debug for CtTimestamp<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CtTimestamp")
.field("start", &self.start())
.field("stop", &self.stop())
.finish()
}
}