nf_queue/
lib.rs

1//! # Netfilter Queue
2//!
3//! A thin wrapper around `libmnl` for netfilter queue.
4//!
5//! # Example
6//! ```
7//! use crate::{CfgFlag, CmdType, CopyMode, MsgBuilder, SOCKET_BUFFER_SIZE, Socket, VerdictType};
8//! use std::error::Error;
9//!
10//! const QUEUE_NUM: u16 = 0;
11//!
12//! fn main() -> Result<(), Box<dyn Error>> {
13//!     let socket = Socket::new(0)?;
14//!
15//!     let mut recv_buf = vec![0; 0xffff + (SOCKET_BUFFER_SIZE / 2)];
16//!     let mut send_buf = vec![0; SOCKET_BUFFER_SIZE];
17//!
18//!     let msg = MsgBuilder::new(&mut send_buf, QUEUE_NUM)
19//!         .ack()
20//!         .cfg()
21//!         .cmd(CmdType::Bind)
22//!         .params(0xffff, CopyMode::Packet)
23//!         .queue_maxlen(1024)
24//!         .flags(CfgFlag::FailOpen | CfgFlag::ConnTrack | CfgFlag::Gso)
25//!         .build();
26//!
27//!     socket.send(msg)?;
28//!     socket.recv(&mut recv_buf)?; // ACK
29//!
30//!     loop {
31//!         let messages = socket.recv(&mut recv_buf)?;
32//!
33//!         for msg in messages {
34//!             let packet_msg = msg.as_packet_msg().unwrap();
35//!             let packet_hdr = packet_msg.packet_hdr().unwrap();
36//!
37//!             println!("{packet_msg:#?}");
38//!
39//!             let verdict = MsgBuilder::new(&mut send_buf, QUEUE_NUM)
40//!                 .verdict()
41//!                 .verdict_hdr(packet_hdr.packet_id, VerdictType::Accept)
42//!                 .build();
43//!
44//!             socket.send(verdict)?;
45//!         }
46//!     }
47//! }
48//! ```
49
50pub mod bindings;
51
52use crate::bindings::*;
53use core::ffi::{c_int, c_uint, c_void};
54use std::fmt::{Debug, Formatter};
55use std::io;
56use std::net::{Ipv4Addr, Ipv6Addr};
57use std::ops::BitOr;
58use std::os::fd::{AsRawFd, RawFd};
59
60trait IsMinusOne {
61    fn is_minus_one(&self) -> bool;
62}
63
64macro_rules! impl_is_minus_one {
65    ($($t:ident)*) => ($(impl IsMinusOne for $t {
66        fn is_minus_one(&self) -> bool {
67            *self == -1
68        }
69    })*)
70}
71
72impl_is_minus_one! { i8 i16 i32 i64 isize }
73
74fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
75    if t.is_minus_one() {
76        Err(io::Error::last_os_error())
77    } else {
78        Ok(t)
79    }
80}
81
82#[derive(Copy, Clone)]
83pub struct Msg<'a>(&'a nlmsghdr);
84
85impl<'a> Msg<'a> {
86    unsafe fn from(nlh: *const nlmsghdr, len: c_int) -> Option<Self> {
87        unsafe {
88            if !mnl_nlmsg_ok(nlh, len) {
89                return None;
90            }
91            Some(Self(&*nlh))
92        }
93    }
94
95    fn next(&self, len: &mut c_int) -> Option<Self> {
96        unsafe {
97            let next = mnl_nlmsg_next(self.0, len as _);
98            Self::from(next, *len)
99        }
100    }
101
102    pub fn as_packet_msg(&self) -> Option<PacketMsg<'a>> {
103        if self.0.nlmsg_flags as u8 != NFQNL_MSG_PACKET as u8 {
104            return None;
105        }
106        let mut tb = [None; NFQA_MAX as usize + 1];
107        if unsafe {
108            mnl_attr_parse(
109                self.0 as _,
110                size_of::<nfgenmsg>() as c_uint,
111                Some(parse_attr_cb),
112                tb.as_mut_ptr() as *mut c_void,
113            )
114        } < 0
115        {
116            return None;
117        }
118        Some(PacketMsg(tb))
119    }
120
121    pub fn as_bytes(&self) -> &'a [u8] {
122        unsafe {
123            std::slice::from_raw_parts(self.0 as *const _ as *const u8, self.0.nlmsg_len as usize)
124        }
125    }
126}
127
128extern "C" fn parse_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
129    let tb = data as *mut Option<&nlattr>;
130    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
131    if unsafe { mnl_attr_type_valid(attr, NFQA_MAX as u16) } < 0 {
132        return MNL_CB_OK as c_int;
133    }
134    match attr_type {
135        NFQA_MARK
136        | NFQA_IFINDEX_INDEV
137        | NFQA_IFINDEX_OUTDEV
138        | NFQA_IFINDEX_PHYSINDEV
139        | NFQA_IFINDEX_PHYSOUTDEV
140        | NFQA_CT_INFO
141        | NFQA_CAP_LEN
142        | NFQA_SKB_INFO
143        | NFQA_UID
144        | NFQA_GID
145        | NFQA_PRIORITY
146        | NFQA_CGROUP_CLASSID => {
147            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
148                return MNL_CB_ERROR;
149            }
150        }
151        NFQA_PACKET_HDR => {
152            if unsafe {
153                mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, size_of::<nfqnl_msg_packet_hdr>())
154            } < 0
155            {
156                return MNL_CB_ERROR;
157            }
158        }
159        NFQA_TIMESTAMP => {
160            if unsafe {
161                mnl_attr_validate2(
162                    attr,
163                    MNL_TYPE_UNSPEC,
164                    size_of::<nfqnl_msg_packet_timestamp>(),
165                )
166            } < 0
167            {
168                return MNL_CB_ERROR;
169            }
170        }
171        NFQA_HWADDR => {
172            if unsafe {
173                mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, size_of::<nfqnl_msg_packet_hw>())
174            } < 0
175            {
176                return MNL_CB_ERROR;
177            }
178        }
179        NFQA_CT | NFQA_VLAN => {
180            if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
181                return MNL_CB_ERROR;
182            }
183        }
184        NFQA_PAYLOAD | NFQA_SECCTX | NFQA_L2HDR => {}
185        _ => {}
186    }
187    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
188    MNL_CB_OK as c_int
189}
190
191#[derive(Copy, Clone)]
192pub struct Messages<'a>(c_int, Option<Msg<'a>>);
193
194impl Messages<'_> {
195    pub fn new(buf: &[u8]) -> Self {
196        let len = buf.len() as c_int;
197        Self(len, unsafe { Msg::from(buf.as_ptr() as _, len) })
198    }
199}
200
201impl<'a> Iterator for Messages<'a> {
202    type Item = Msg<'a>;
203
204    fn next(&mut self) -> Option<Self::Item> {
205        let next = self.1.as_ref().and_then(|msg| msg.next(&mut self.0));
206        let msg = self.1.take();
207        self.1 = next;
208        msg
209    }
210}
211
212pub const SOCKET_BUFFER_SIZE: usize = 8192;
213
214#[derive(Copy, Clone)]
215pub struct Socket<'a>(&'a mnl_socket);
216
217impl Socket<'_> {
218    pub fn new(flags: i32) -> io::Result<Self> {
219        unsafe {
220            let socket = mnl_socket_open2(NETLINK_NETFILTER as c_int, flags);
221            if socket.is_null() {
222                return Err(io::Error::last_os_error());
223            }
224            let mut ret: c_int = 1;
225            cvt(mnl_socket_setsockopt(
226                socket as _,
227                NETLINK_NO_ENOBUFS as c_int,
228                &mut ret as *mut _ as _,
229                size_of::<c_int>() as _,
230            ))?;
231            cvt(mnl_socket_bind(socket, 0, MNL_SOCKET_AUTOPID as _))?;
232            Ok(Self(&*socket))
233        }
234    }
235
236    pub fn recv(&self, buf: &mut [u8]) -> io::Result<Messages<'_>> {
237        unsafe {
238            let ret = cvt(mnl_socket_recvfrom(
239                self.0,
240                buf.as_mut_ptr() as _,
241                buf.len(),
242            ))? as usize;
243            if mnl_cb_run(
244                buf.as_ptr() as _,
245                ret,
246                0,
247                mnl_socket_get_portid(self.0),
248                None,
249                std::ptr::null_mut(),
250            ) < 0
251            {
252                return Err(io::Error::last_os_error());
253            }
254            Ok(Messages::new(&buf[..ret]))
255        }
256    }
257
258    pub fn send(&self, msg: Msg) -> io::Result<()> {
259        cvt(unsafe {
260            mnl_socket_sendto(self.0, msg.as_bytes().as_ptr() as _, msg.as_bytes().len())
261        })?;
262        Ok(())
263    }
264}
265
266impl AsRawFd for Socket<'_> {
267    fn as_raw_fd(&self) -> RawFd {
268        unsafe { mnl_socket_get_fd(self.0) }
269    }
270}
271
272#[derive(Debug, Clone, Copy)]
273#[repr(u8)]
274pub enum CopyMode {
275    Meta = NFQNL_COPY_META as u8,
276    Packet = NFQNL_COPY_PACKET as u8,
277}
278
279#[derive(Debug, Clone, Copy)]
280#[repr(u8)]
281pub enum CmdType {
282    None = NFQNL_CFG_CMD_NONE as u8,
283    Bind = NFQNL_CFG_CMD_BIND as u8,
284    Unbind = NFQNL_CFG_CMD_UNBIND as u8,
285}
286
287#[derive(Debug, Clone, Copy)]
288#[repr(u32)]
289pub enum CfgFlag {
290    /// Accept the packets if the kernel queue gets full.
291    FailOpen = NFQA_CFG_F_FAIL_OPEN,
292    ConnTrack = NFQA_CFG_F_CONNTRACK,
293    /// Send GSO packets to userspace without segmenting.
294    Gso = NFQA_CFG_F_GSO,
295    UidGid = NFQA_CFG_F_UID_GID,
296    SecCtx = NFQA_CFG_F_SECCTX,
297}
298
299impl BitOr for CfgFlag {
300    type Output = u32;
301
302    fn bitor(self, rhs: Self) -> Self::Output {
303        (self as u32) | (rhs as u32)
304    }
305}
306
307impl BitOr<CfgFlag> for u32 {
308    type Output = u32;
309
310    fn bitor(self, rhs: CfgFlag) -> Self::Output {
311        self | (rhs as u32)
312    }
313}
314
315pub enum VerdictType {
316    Drop,
317    Accept,
318    Queue(u32),
319    Repeat,
320}
321
322pub struct MsgBuilder<'a>(&'a mut nlmsghdr);
323
324impl<'a> MsgBuilder<'a> {
325    pub fn new(buf: &'a mut [u8], queue_num: u16) -> Self {
326        unsafe {
327            let nlh = mnl_nlmsg_put_header(buf.as_mut_ptr() as _);
328            (*nlh).nlmsg_type = (NFNL_SUBSYS_QUEUE as u16) << 8;
329            (*nlh).nlmsg_flags = NLM_F_REQUEST as u16;
330            let nfg = mnl_nlmsg_put_extra_header(nlh, size_of::<nfgenmsg>()) as *mut nfgenmsg;
331            (*nfg).nfgen_family = 0;
332            (*nfg).version = NFNETLINK_V0 as u8;
333            (*nfg).res_id = queue_num.to_be();
334            Self(&mut *nlh)
335        }
336    }
337
338    pub fn ack(self) -> Self {
339        self.0.nlmsg_flags |= NLM_F_ACK as u16;
340        self
341    }
342
343    pub fn cfg(self) -> CfgMsgBuilder<'a> {
344        self.0.nlmsg_type |= NFQNL_MSG_CONFIG as u16;
345        CfgMsgBuilder(self)
346    }
347
348    pub fn verdict(self) -> VerdictMsgBuilder<'a> {
349        self.0.nlmsg_type |= NFQNL_MSG_VERDICT as u16;
350        VerdictMsgBuilder(self)
351    }
352
353    pub fn build(self) -> Msg<'a> {
354        Msg(self.0)
355    }
356}
357
358pub struct CfgMsgBuilder<'a>(MsgBuilder<'a>);
359
360impl<'a> CfgMsgBuilder<'a> {
361    pub fn cmd(self, cmd: CmdType) -> Self {
362        unsafe {
363            mnl_attr_put(
364                self.0.0,
365                NFQA_CFG_CMD as u16,
366                size_of::<nfqnl_msg_config_cmd>(),
367                &nfqnl_msg_config_cmd {
368                    command: cmd as u8,
369                    _pad: 0,
370                    pf: 0,
371                } as *const _ as _,
372            );
373        }
374        self
375    }
376
377    pub fn params(self, copy_range: u32, copy_mode: CopyMode) -> Self {
378        unsafe {
379            mnl_attr_put(
380                self.0.0,
381                NFQA_CFG_PARAMS as u16,
382                size_of::<nfqnl_msg_config_params>(),
383                &nfqnl_msg_config_params {
384                    copy_range: copy_range.to_be(),
385                    copy_mode: copy_mode as u8,
386                } as *const _ as _,
387            );
388        }
389        self
390    }
391
392    pub fn queue_maxlen(self, queue_maxlen: u32) -> Self {
393        unsafe {
394            mnl_attr_put_u32(self.0.0, NFQA_CFG_QUEUE_MAXLEN as u16, queue_maxlen.to_be());
395        }
396        self
397    }
398
399    pub fn flags(self, flags: u32) -> Self {
400        unsafe {
401            mnl_attr_put_u32(self.0.0, NFQA_CFG_FLAGS as u16, flags.to_be());
402            mnl_attr_put_u32(self.0.0, NFQA_CFG_MASK as u16, flags.to_be());
403        }
404        self
405    }
406
407    pub fn build(self) -> Msg<'a> {
408        self.0.build()
409    }
410}
411
412pub struct VerdictMsgBuilder<'a>(MsgBuilder<'a>);
413
414impl<'a> VerdictMsgBuilder<'a> {
415    pub fn verdict_hdr(self, id: u32, verdict: VerdictType) -> Self {
416        unsafe {
417            mnl_attr_put(
418                self.0.0,
419                NFQA_VERDICT_HDR as u16,
420                size_of::<nfqnl_msg_verdict_hdr>(),
421                &nfqnl_msg_verdict_hdr {
422                    verdict: match verdict {
423                        VerdictType::Drop => NF_DROP,
424                        VerdictType::Accept => NF_ACCEPT,
425                        VerdictType::Queue(num) => ((num << 16) & NF_VERDICT_QMASK) | NF_QUEUE,
426                        VerdictType::Repeat => NF_REPEAT,
427                    }
428                    .to_be(),
429                    id: id.to_be(),
430                } as *const _ as _,
431            );
432        }
433        self
434    }
435
436    pub fn ct(self) -> VerdictCtBuilder<'a> {
437        unsafe {
438            let start = mnl_attr_nest_start(self.0.0, NFQA_CT as u16);
439            VerdictCtBuilder(self.0, start)
440        }
441    }
442
443    pub fn payload(self, payload: &[u8]) -> Self {
444        unsafe {
445            mnl_attr_put(
446                self.0.0,
447                NFQA_PAYLOAD as u16,
448                payload.len(),
449                &payload as *const _ as _,
450            )
451        }
452        self
453    }
454
455    pub fn mark(self, mark: u32) -> Self {
456        unsafe {
457            mnl_attr_put_u32(self.0.0, NFQA_MARK as u16, mark.to_be());
458        }
459        self
460    }
461
462    pub fn priority(self, priority: u32) -> Self {
463        unsafe {
464            mnl_attr_put_u32(self.0.0, NFQA_PRIORITY as u16, priority.to_be());
465        }
466        self
467    }
468
469    pub fn build(self) -> Msg<'a> {
470        self.0.build()
471    }
472}
473
474pub struct VerdictCtBuilder<'a>(MsgBuilder<'a>, *mut nlattr);
475
476impl<'a> VerdictCtBuilder<'a> {
477    pub fn timeout(self, timeout: u32) -> Self {
478        unsafe {
479            mnl_attr_put_u32(self.0.0, CTA_TIMEOUT as u16, timeout.to_be());
480        }
481        self
482    }
483
484    pub fn status(self, status: u32) -> Self {
485        unsafe {
486            mnl_attr_put_u32(self.0.0, CTA_STATUS as u16, status.to_be());
487        }
488        self
489    }
490
491    pub fn mark(self, mark: u32) -> Self {
492        unsafe {
493            mnl_attr_put_u32(self.0.0, CTA_MARK as u16, mark.to_be());
494        }
495        self
496    }
497
498    pub fn build(self) -> Msg<'a> {
499        unsafe { mnl_attr_nest_end(self.0.0, self.1) }
500        self.0.build()
501    }
502}
503
504#[derive(Debug, Copy, Clone)]
505pub struct PacketHdr {
506    pub packet_id: u32,
507    /// ETH_P_* constants in if_ether.h
508    pub hw_protocol: u16,
509    /// enum nf_inet_hooks in netfilter.h
510    pub hook: u8,
511}
512
513#[derive(Debug, Copy, Clone)]
514pub struct Timestamp {
515    pub sec: u64,
516    pub usec: u64,
517}
518
519#[derive(Debug, Copy, Clone)]
520pub struct HwAddr {
521    pub hw_addrlen: u16,
522    pub hw_addr: [u8; 8],
523}
524
525#[derive(Copy, Clone)]
526pub struct PacketMsg<'a>([Option<&'a nlattr>; NFQA_MAX as usize + 1]);
527
528impl<'a> PacketMsg<'a> {
529    pub fn packet_hdr(&self) -> Option<PacketHdr> {
530        self.0[NFQA_PACKET_HDR as usize].map(|nla| unsafe {
531            let packet_hdr = mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_hdr;
532            PacketHdr {
533                packet_id: u32::from_be((*packet_hdr).packet_id),
534                hw_protocol: u16::from_be((*packet_hdr).hw_protocol),
535                hook: (*packet_hdr).hook,
536            }
537        })
538    }
539
540    pub fn mark(&self) -> Option<u32> {
541        self.0[NFQA_MARK as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
542    }
543
544    pub fn timestamp(&self) -> Option<Timestamp> {
545        self.0[NFQA_TIMESTAMP as usize].map(|nla| unsafe {
546            let timestamp = std::ptr::read_unaligned(
547                mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_timestamp
548            );
549            Timestamp {
550                sec: u64::from_be(timestamp.sec),
551                usec: u64::from_be(timestamp.usec),
552            }
553        })
554    }
555
556    pub fn indev(&self) -> Option<u32> {
557        self.0[NFQA_IFINDEX_INDEV as usize]
558            .map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
559    }
560
561    pub fn outdev(&self) -> Option<u32> {
562        self.0[NFQA_IFINDEX_OUTDEV as usize]
563            .map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
564    }
565
566    pub fn phys_indev(&self) -> Option<u32> {
567        self.0[NFQA_IFINDEX_PHYSINDEV as usize]
568            .map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
569    }
570
571    pub fn phys_outdev(&self) -> Option<u32> {
572        self.0[NFQA_IFINDEX_PHYSOUTDEV as usize]
573            .map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
574    }
575
576    pub fn hw_addr(&self) -> Option<HwAddr> {
577        self.0[NFQA_HWADDR as usize].map(|nla| unsafe {
578            let hw_addr = mnl_attr_get_payload(nla) as *mut nfqnl_msg_packet_hw;
579            HwAddr {
580                hw_addrlen: u16::from_be((*hw_addr).hw_addrlen),
581                hw_addr: (*hw_addr).hw_addr,
582            }
583        })
584    }
585
586    pub fn payload(&self) -> Option<&'a [u8]> {
587        self.0[NFQA_PAYLOAD as usize].map(|nla| unsafe {
588            std::slice::from_raw_parts(
589                mnl_attr_get_payload(nla) as *const u8,
590                mnl_attr_get_payload_len(nla) as usize,
591            )
592        })
593    }
594
595    pub fn ct(&self) -> Option<Ct<'a>> {
596        self.0[NFQA_CT as usize].map(|nla| {
597            let mut tb = [None; CTA_MAX as usize + 1];
598            if unsafe {
599                mnl_attr_parse_nested(nla, Some(parse_ct_attr_cb), tb.as_mut_ptr() as *mut c_void)
600            } < 0
601            {
602                return None;
603            }
604            Some(Ct(tb))
605        })?
606    }
607
608    pub fn ct_info(&self) -> Option<u32> {
609        self.0[NFQA_CT_INFO as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
610    }
611
612    pub fn cap_len(&self) -> Option<u32> {
613        self.0[NFQA_CAP_LEN as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
614    }
615
616    pub fn skb_info(&self) -> Option<u32> {
617        self.0[NFQA_SKB_INFO as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
618    }
619
620    pub fn uid(&self) -> Option<u32> {
621        self.0[NFQA_UID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
622    }
623
624    pub fn gid(&self) -> Option<u32> {
625        self.0[NFQA_GID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
626    }
627
628    pub fn secctx(&self) -> Option<&'a [u8]> {
629        self.0[NFQA_SECCTX as usize].map(|nla| unsafe {
630            std::slice::from_raw_parts(
631                mnl_attr_get_payload(nla) as *const u8,
632                mnl_attr_get_payload_len(nla) as usize,
633            )
634        })
635    }
636
637    pub fn vlan(&self) -> Option<Vlan<'a>> {
638        self.0[NFQA_VLAN as usize].map(|nla| {
639            let mut tb = [None; NFQA_VLAN_MAX as usize + 1];
640            if unsafe {
641                mnl_attr_parse_nested(
642                    nla,
643                    Some(parse_vlan_attr_cb),
644                    tb.as_mut_ptr() as *mut c_void,
645                )
646            } < 0
647            {
648                return None;
649            }
650            Some(Vlan(tb))
651        })?
652    }
653
654    pub fn l2hdr(&self) -> Option<&'a [u8]> {
655        self.0[NFQA_L2HDR as usize].map(|nla| unsafe {
656            std::slice::from_raw_parts(
657                mnl_attr_get_payload(nla) as *const u8,
658                mnl_attr_get_payload_len(nla) as usize,
659            )
660        })
661    }
662
663    pub fn priority(&self) -> Option<u32> {
664        self.0[NFQA_PRIORITY as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
665    }
666
667    pub fn cgroup_classid(&self) -> Option<u32> {
668        self.0[NFQA_CGROUP_CLASSID as usize]
669            .map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
670    }
671}
672
673impl Debug for PacketMsg<'_> {
674    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
675        f.debug_struct("PacketMsg")
676            .field("packet_hdr", &self.packet_hdr())
677            .field("mark", &self.mark())
678            .field("timestamp", &self.timestamp())
679            .field("indev", &self.indev())
680            .field("outdev", &self.outdev())
681            .field("phys_indev", &self.phys_indev())
682            .field("phys_outdev", &self.phys_outdev())
683            .field("hw_addr", &self.hw_addr())
684            .field("payload", &self.payload())
685            .field("ct", &self.ct())
686            .field("ct_info", &self.ct_info())
687            .field("cap_len", &self.cap_len())
688            .field("skb_info", &self.skb_info())
689            .field("uid", &self.uid())
690            .field("gid", &self.gid())
691            .field("secctx", &self.secctx())
692            .field("vlan", &self.vlan())
693            .field("l2hdr", &self.l2hdr())
694            .field("priority", &self.priority())
695            .field("cgroup_classid", &self.cgroup_classid())
696            .finish()
697    }
698}
699
700extern "C" fn parse_ct_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
701    let tb = data as *mut Option<&nlattr>;
702    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
703    if unsafe { mnl_attr_type_valid(attr, CTA_MAX as u16) } < 0 {
704        return MNL_CB_OK as c_int;
705    }
706    match attr_type {
707        CTA_ZONE => {
708            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
709                return MNL_CB_ERROR;
710            }
711        }
712        CTA_STATUS | CTA_TIMEOUT | CTA_MARK | CTA_USE | CTA_ID => {
713            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
714                return MNL_CB_ERROR;
715            }
716        }
717        CTA_TUPLE_ORIG | CTA_TUPLE_REPLY | CTA_TUPLE_MASTER | CTA_PROTOINFO | CTA_COUNTERS_ORIG
718        | CTA_COUNTERS_REPLY | CTA_TIMESTAMP => {
719            if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
720                return MNL_CB_ERROR;
721            }
722        }
723        _ => {}
724    }
725    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
726    MNL_CB_OK as c_int
727}
728
729extern "C" fn parse_vlan_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
730    let tb = data as *mut Option<&nlattr>;
731    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
732    if unsafe { mnl_attr_type_valid(attr, NFQA_VLAN_MAX as u16) } < 0 {
733        return MNL_CB_OK as c_int;
734    }
735    match attr_type {
736        NFQA_VLAN_PROTO | NFQA_VLAN_TCI => {
737            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
738                return MNL_CB_ERROR;
739            }
740        }
741        _ => {}
742    }
743    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
744    MNL_CB_OK as c_int
745}
746
747#[derive(Copy, Clone)]
748pub struct Vlan<'a>([Option<&'a nlattr>; NFQA_VLAN_MAX as usize + 1]);
749
750impl Vlan<'_> {
751    pub fn proto(&self) -> Option<u16> {
752        self.0[NFQA_VLAN_PROTO as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
753    }
754
755    pub fn tci(&self) -> Option<u16> {
756        self.0[NFQA_VLAN_TCI as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
757    }
758}
759
760impl Debug for Vlan<'_> {
761    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
762        f.debug_struct("Vlan")
763            .field("proto", &self.proto())
764            .field("tci", &self.tci())
765            .finish()
766    }
767}
768
769#[derive(Copy, Clone)]
770pub struct Ct<'a>([Option<&'a nlattr>; CTA_MAX as usize + 1]);
771
772impl<'a> Ct<'a> {
773    fn tuple(&self, tuple: usize) -> Option<CtTuple<'a>> {
774        self.0[tuple].map(|nla| {
775            let mut tb = [None; CTA_TUPLE_MAX as usize + 1];
776            if unsafe {
777                mnl_attr_parse_nested(
778                    nla,
779                    Some(parse_tuple_attr_cb),
780                    tb.as_mut_ptr() as *mut c_void,
781                )
782            } < 0
783            {
784                return None;
785            }
786            Some(CtTuple(tb))
787        })?
788    }
789
790    pub fn tuple_orig(&self) -> Option<CtTuple<'a>> {
791        self.tuple(CTA_TUPLE_ORIG as usize)
792    }
793
794    pub fn tuple_reply(&self) -> Option<CtTuple<'a>> {
795        self.tuple(CTA_TUPLE_REPLY as usize)
796    }
797
798    pub fn tuple_master(&self) -> Option<CtTuple<'a>> {
799        self.tuple(CTA_TUPLE_MASTER as usize)
800    }
801
802    pub fn status(&self) -> Option<u32> {
803        self.0[CTA_STATUS as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
804    }
805
806    pub fn protoinfo(&self) -> Option<CtProtoInfo<'a>> {
807        self.0[CTA_PROTOINFO as usize].map(|nla| {
808            let mut tb = [None; CTA_PROTOINFO_MAX as usize + 1];
809            if unsafe {
810                mnl_attr_parse_nested(
811                    nla,
812                    Some(parse_protoinfo_attr_cb),
813                    tb.as_mut_ptr() as *mut c_void,
814                )
815            } < 0
816            {
817                return None;
818            }
819            Some(CtProtoInfo(tb))
820        })?
821    }
822
823    fn counters(&self, counters: usize) -> Option<CtCounters<'a>> {
824        self.0[counters].map(|nla| {
825            let mut tb = [None; CTA_COUNTERS_MAX as usize + 1];
826            if unsafe {
827                mnl_attr_parse_nested(
828                    nla,
829                    Some(parse_counters_attr_cb),
830                    tb.as_mut_ptr() as *mut c_void,
831                )
832            } < 0
833            {
834                return None;
835            }
836            Some(CtCounters(tb))
837        })?
838    }
839
840    pub fn counters_orig(&self) -> Option<CtCounters<'a>> {
841        self.counters(CTA_COUNTERS_ORIG as usize)
842    }
843
844    pub fn counters_reply(&self) -> Option<CtCounters<'a>> {
845        self.counters(CTA_COUNTERS_REPLY as usize)
846    }
847
848    pub fn timeout(&self) -> Option<u32> {
849        self.0[CTA_TIMEOUT as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
850    }
851
852    pub fn mark(&self) -> Option<u32> {
853        self.0[CTA_MARK as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
854    }
855
856    pub fn r#use(&self) -> Option<u32> {
857        self.0[CTA_USE as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
858    }
859
860    pub fn id(&self) -> Option<u32> {
861        self.0[CTA_ID as usize].map(|nla| u32::from_be(unsafe { mnl_attr_get_u32(nla) }))
862    }
863
864    pub fn zone(&self) -> Option<u16> {
865        self.0[CTA_ZONE as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
866    }
867
868    pub fn timestamp(&self) -> Option<CtTimestamp<'a>> {
869        self.0[CTA_TIMESTAMP as usize].map(|nla| {
870            let mut tb = [None; CTA_TIMESTAMP_MAX as usize + 1];
871            if unsafe {
872                mnl_attr_parse_nested(
873                    nla,
874                    Some(parse_timestamp_attr_cb),
875                    tb.as_mut_ptr() as *mut c_void,
876                )
877            } < 0
878            {
879                return None;
880            }
881            Some(CtTimestamp(tb))
882        })?
883    }
884}
885
886impl Debug for Ct<'_> {
887    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
888        f.debug_struct("Ct")
889            .field("tuple_orig", &self.tuple_orig())
890            .field("tuple_reply", &self.tuple_reply())
891            .field("tuple_master", &self.tuple_master())
892            .field("status", &self.status())
893            .field("protoinfo", &self.protoinfo())
894            .field("counters_orig", &self.counters_orig())
895            .field("counters_reply", &self.counters_reply())
896            .field("timeout", &self.timeout())
897            .field("mark", &self.mark())
898            .field("use", &self.r#use())
899            .field("id", &self.id())
900            .field("zone", &self.zone())
901            .field("timestamp", &self.timestamp())
902            .finish()
903    }
904}
905
906extern "C" fn parse_tuple_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
907    let tb = data as *mut Option<&nlattr>;
908    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
909    if unsafe { mnl_attr_type_valid(attr, CTA_TUPLE_MAX as u16) } < 0 {
910        return MNL_CB_OK as c_int;
911    }
912    match attr_type {
913        CTA_TUPLE_IP | CTA_TUPLE_PROTO => {
914            if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
915                return MNL_CB_ERROR;
916            }
917        }
918        CTA_TUPLE_ZONE => {
919            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
920                return MNL_CB_ERROR;
921            }
922        }
923        _ => {}
924    }
925    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
926    MNL_CB_OK as c_int
927}
928
929extern "C" fn parse_protoinfo_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
930    let tb = data as *mut Option<&nlattr>;
931    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
932    if unsafe { mnl_attr_type_valid(attr, CTA_PROTOINFO_MAX as u16) } < 0 {
933        return MNL_CB_OK as c_int;
934    }
935    match attr_type {
936        CTA_PROTOINFO_TCP => {
937            if unsafe { mnl_attr_validate(attr, MNL_TYPE_NESTED) } < 0 {
938                return MNL_CB_ERROR;
939            }
940        }
941        CTA_PROTOINFO_DCCP | CTA_PROTOINFO_SCTP => {}
942        _ => {}
943    }
944    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
945    MNL_CB_OK as c_int
946}
947
948extern "C" fn parse_counters_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
949    let tb = data as *mut Option<&nlattr>;
950    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
951    if unsafe { mnl_attr_type_valid(attr, CTA_COUNTERS_MAX as u16) } < 0 {
952        return MNL_CB_OK as c_int;
953    }
954    match attr_type {
955        CTA_COUNTERS_PACKETS | CTA_COUNTERS_BYTES => {
956            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U64) } < 0 {
957                return MNL_CB_ERROR;
958            }
959        }
960        _ => {}
961    }
962    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
963    MNL_CB_OK as c_int
964}
965
966extern "C" fn parse_timestamp_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
967    let tb = data as *mut Option<&nlattr>;
968    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
969    if unsafe { mnl_attr_type_valid(attr, CTA_TIMESTAMP_MAX as u16) } < 0 {
970        return MNL_CB_OK as c_int;
971    }
972    match attr_type {
973        CTA_TIMESTAMP_START | CTA_TIMESTAMP_STOP => {
974            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U64) } < 0 {
975                return MNL_CB_ERROR;
976            }
977        }
978        _ => {}
979    }
980    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
981    MNL_CB_OK as c_int
982}
983
984#[derive(Copy, Clone)]
985pub struct CtTuple<'a>([Option<&'a nlattr>; CTA_TUPLE_MAX as usize + 1]);
986
987impl<'a> CtTuple<'a> {
988    pub fn ip(&self) -> Option<CtTupleIp<'a>> {
989        self.0[CTA_TUPLE_IP as usize].map(|nla| {
990            let mut tb = [None; CTA_IP_MAX as usize + 1];
991            if unsafe {
992                mnl_attr_parse_nested(nla, Some(parse_ip_attr_cb), tb.as_mut_ptr() as *mut c_void)
993            } < 0
994            {
995                return None;
996            }
997            Some(CtTupleIp(tb))
998        })?
999    }
1000
1001    pub fn proto(&self) -> Option<CtTupleProto<'a>> {
1002        self.0[CTA_TUPLE_PROTO as usize].map(|nla| {
1003            let mut tb = [None; CTA_PROTO_MAX as usize + 1];
1004            if unsafe {
1005                mnl_attr_parse_nested(
1006                    nla,
1007                    Some(parse_proto_attr_cb),
1008                    tb.as_mut_ptr() as *mut c_void,
1009                )
1010            } < 0
1011            {
1012                return None;
1013            }
1014            Some(CtTupleProto(tb))
1015        })?
1016    }
1017
1018    pub fn zone(&self) -> Option<u16> {
1019        self.0[CTA_TUPLE_ZONE as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
1020    }
1021}
1022
1023impl Debug for CtTuple<'_> {
1024    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1025        f.debug_struct("CtTuple")
1026            .field("ip", &self.ip())
1027            .field("proto", &self.proto())
1028            .field("zone", &self.zone())
1029            .finish()
1030    }
1031}
1032
1033extern "C" fn parse_ip_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
1034    let tb = data as *mut Option<&nlattr>;
1035    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
1036    if unsafe { mnl_attr_type_valid(attr, CTA_IP_MAX as u16) } < 0 {
1037        return MNL_CB_OK as c_int;
1038    }
1039    match attr_type {
1040        CTA_IP_V4_SRC | CTA_IP_V4_DST => {
1041            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U32) } < 0 {
1042                return MNL_CB_ERROR;
1043            }
1044        }
1045        CTA_IP_V6_SRC | CTA_IP_V6_DST => {
1046            if unsafe { mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, 16) } < 0 {
1047                return MNL_CB_ERROR;
1048            }
1049        }
1050        _ => {}
1051    }
1052    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
1053    MNL_CB_OK as c_int
1054}
1055
1056extern "C" fn parse_proto_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
1057    let tb = data as *mut Option<&nlattr>;
1058    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
1059    if unsafe { mnl_attr_type_valid(attr, CTA_PROTO_MAX as u16) } < 0 {
1060        return MNL_CB_OK as c_int;
1061    }
1062    match attr_type {
1063        CTA_PROTO_NUM
1064        | CTA_PROTO_ICMP_TYPE
1065        | CTA_PROTO_ICMP_CODE
1066        | CTA_PROTO_ICMPV6_TYPE
1067        | CTA_PROTO_ICMPV6_CODE => {
1068            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U8) } < 0 {
1069                return MNL_CB_ERROR;
1070            }
1071        }
1072        CTA_PROTO_SRC_PORT | CTA_PROTO_DST_PORT | CTA_PROTO_ICMP_ID | CTA_PROTO_ICMPV6_ID => {
1073            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U16) } < 0 {
1074                return MNL_CB_ERROR;
1075            }
1076        }
1077        _ => {}
1078    }
1079    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
1080    MNL_CB_OK as c_int
1081}
1082
1083#[derive(Copy, Clone)]
1084pub struct CtTupleIp<'a>([Option<&'a nlattr>; CTA_IP_MAX as usize + 1]);
1085
1086impl CtTupleIp<'_> {
1087    pub fn ipv4_src(&self) -> Option<Ipv4Addr> {
1088        self.0[CTA_IP_V4_SRC as usize]
1089            .map(|nla| Ipv4Addr::from(u32::from_be(unsafe { mnl_attr_get_u32(nla) })))
1090    }
1091
1092    pub fn ipv4_dst(&self) -> Option<Ipv4Addr> {
1093        self.0[CTA_IP_V4_DST as usize]
1094            .map(|nla| Ipv4Addr::from(u32::from_be(unsafe { mnl_attr_get_u32(nla) })))
1095    }
1096
1097    fn ipv6(&self, r#type: usize) -> Option<Ipv6Addr> {
1098        self.0[r#type].map(|nla| {
1099            let mut bytes = [0u8; 16];
1100            unsafe {
1101                std::ptr::copy_nonoverlapping(
1102                    mnl_attr_get_payload(nla) as *const u8,
1103                    bytes.as_mut_ptr(),
1104                    16,
1105                );
1106            }
1107            Ipv6Addr::from(bytes)
1108        })
1109    }
1110
1111    pub fn ipv6_src(&self) -> Option<Ipv6Addr> {
1112        self.ipv6(CTA_IP_V6_SRC as usize)
1113    }
1114
1115    pub fn ipv6_dst(&self) -> Option<Ipv6Addr> {
1116        self.ipv6(CTA_IP_V6_DST as usize)
1117    }
1118}
1119
1120impl Debug for CtTupleIp<'_> {
1121    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1122        f.debug_struct("CtTupleIp")
1123            .field("ipv4_src", &self.ipv4_src())
1124            .field("ipv4_dst", &self.ipv4_dst())
1125            .field("ipv6_src", &self.ipv6_src())
1126            .field("ipv6_dst", &self.ipv6_dst())
1127            .finish()
1128    }
1129}
1130
1131#[derive(Copy, Clone)]
1132pub struct CtTupleProto<'a>([Option<&'a nlattr>; CTA_PROTO_MAX as usize + 1]);
1133
1134impl CtTupleProto<'_> {
1135    pub fn num(&self) -> Option<u8> {
1136        self.0[CTA_PROTO_NUM as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1137    }
1138
1139    pub fn src_port(&self) -> Option<u16> {
1140        self.0[CTA_PROTO_SRC_PORT as usize]
1141            .map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
1142    }
1143
1144    pub fn dst_port(&self) -> Option<u16> {
1145        self.0[CTA_PROTO_DST_PORT as usize]
1146            .map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
1147    }
1148
1149    pub fn icmp_id(&self) -> Option<u16> {
1150        self.0[CTA_PROTO_ICMP_ID as usize].map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
1151    }
1152
1153    pub fn icmp_type(&self) -> Option<u8> {
1154        self.0[CTA_PROTO_ICMP_TYPE as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1155    }
1156
1157    pub fn icmp_code(&self) -> Option<u8> {
1158        self.0[CTA_PROTO_ICMP_CODE as usize].map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1159    }
1160
1161    pub fn icmp6_id(&self) -> Option<u16> {
1162        self.0[CTA_PROTO_ICMPV6_ID as usize]
1163            .map(|nla| u16::from_be(unsafe { mnl_attr_get_u16(nla) }))
1164    }
1165
1166    pub fn icmp6_type(&self) -> Option<u8> {
1167        self.0[CTA_PROTO_ICMPV6_TYPE as usize]
1168            .map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1169    }
1170
1171    pub fn icmp6_code(&self) -> Option<u8> {
1172        self.0[CTA_PROTO_ICMPV6_CODE as usize]
1173            .map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1174    }
1175}
1176
1177impl Debug for CtTupleProto<'_> {
1178    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1179        f.debug_struct("CtTupleProto")
1180            .field("num", &self.num())
1181            .field("src_port", &self.src_port())
1182            .field("dst_port", &self.dst_port())
1183            .field("icmp_id", &self.icmp_id())
1184            .field("icmp_type", &self.icmp_type())
1185            .field("icmp_code", &self.icmp_code())
1186            .field("icmp6_id", &self.icmp6_id())
1187            .field("icmp6_type", &self.icmp6_type())
1188            .field("icmp6_code", &self.icmp6_code())
1189            .finish()
1190    }
1191}
1192
1193#[derive(Copy, Clone)]
1194pub struct CtProtoInfo<'a>([Option<&'a nlattr>; CTA_PROTOINFO_MAX as usize + 1]);
1195
1196impl<'a> CtProtoInfo<'a> {
1197    pub fn tcp(&self) -> Option<CtProtoInfoTcp<'a>> {
1198        self.0[CTA_PROTOINFO_TCP as usize].map(|nla| {
1199            let mut tb = [None; CTA_PROTOINFO_TCP_MAX as usize + 1];
1200            if unsafe {
1201                mnl_attr_parse_nested(
1202                    nla,
1203                    Some(parse_protoinfo_tcp_attr_cb),
1204                    tb.as_mut_ptr() as *mut c_void,
1205                )
1206            } < 0
1207            {
1208                return None;
1209            }
1210            Some(CtProtoInfoTcp(tb))
1211        })?
1212    }
1213}
1214
1215impl Debug for CtProtoInfo<'_> {
1216    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1217        f.debug_struct("CtProtoInfo")
1218            .field("tcp", &self.tcp())
1219            .finish()
1220    }
1221}
1222
1223extern "C" fn parse_protoinfo_tcp_attr_cb(attr: *const nlattr, data: *mut c_void) -> c_int {
1224    let tb = data as *mut Option<&nlattr>;
1225    let attr_type = unsafe { mnl_attr_get_type(attr) } as u32;
1226    if unsafe { mnl_attr_type_valid(attr, CTA_PROTOINFO_TCP_MAX as u16) } < 0 {
1227        return MNL_CB_OK as c_int;
1228    }
1229    match attr_type {
1230        CTA_PROTOINFO_TCP_STATE
1231        | CTA_PROTOINFO_TCP_WSCALE_ORIGINAL
1232        | CTA_PROTOINFO_TCP_WSCALE_REPLY => {
1233            if unsafe { mnl_attr_validate(attr, MNL_TYPE_U8) } < 0 {
1234                return MNL_CB_ERROR;
1235            }
1236        }
1237        CTA_PROTOINFO_TCP_FLAGS_ORIGINAL | CTA_PROTOINFO_TCP_FLAGS_REPLY => {
1238            if unsafe { mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, 2) } < 0 {
1239                return MNL_CB_ERROR;
1240            }
1241        }
1242        _ => {}
1243    }
1244    unsafe { *tb.add(attr_type as usize) = Some(&*attr) };
1245    MNL_CB_OK as c_int
1246}
1247
1248#[derive(Copy, Clone)]
1249pub struct CtProtoInfoTcp<'a>([Option<&'a nlattr>; CTA_PROTOINFO_TCP_MAX as usize + 1]);
1250
1251impl<'a> CtProtoInfoTcp<'a> {
1252    pub fn state(&self) -> Option<u8> {
1253        self.0[CTA_PROTOINFO_TCP_STATE as usize]
1254            .map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1255    }
1256
1257    pub fn wscale_original(&self) -> Option<u8> {
1258        self.0[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL as usize]
1259            .map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1260    }
1261
1262    pub fn wscale_reply(&self) -> Option<u8> {
1263        self.0[CTA_PROTOINFO_TCP_WSCALE_REPLY as usize]
1264            .map(|nla| u8::from_be(unsafe { mnl_attr_get_u8(nla) }))
1265    }
1266}
1267
1268impl Debug for CtProtoInfoTcp<'_> {
1269    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1270        f.debug_struct("CtProtoInfoTcp")
1271            .field("state", &self.state())
1272            .field("wscale_original", &self.wscale_original())
1273            .field("wscale_reply", &self.wscale_reply())
1274            .finish()
1275    }
1276}
1277
1278#[derive(Copy, Clone)]
1279pub struct CtCounters<'a>([Option<&'a nlattr>; CTA_COUNTERS_MAX as usize + 1]);
1280
1281impl<'a> CtCounters<'a> {
1282    pub fn packets(&self) -> Option<u64> {
1283        self.0[CTA_COUNTERS_PACKETS as usize]
1284            .map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
1285    }
1286
1287    pub fn bytes(&self) -> Option<u64> {
1288        self.0[CTA_COUNTERS_BYTES as usize]
1289            .map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
1290    }
1291}
1292
1293impl Debug for CtCounters<'_> {
1294    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1295        f.debug_struct("CtCounters")
1296            .field("packets", &self.packets())
1297            .field("bytes", &self.bytes())
1298            .finish()
1299    }
1300}
1301
1302#[derive(Copy, Clone)]
1303pub struct CtTimestamp<'a>([Option<&'a nlattr>; CTA_TIMESTAMP_MAX as usize + 1]);
1304
1305impl<'a> CtTimestamp<'a> {
1306    pub fn start(&self) -> Option<u64> {
1307        self.0[CTA_TIMESTAMP_START as usize]
1308            .map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
1309    }
1310
1311    pub fn stop(&self) -> Option<u64> {
1312        self.0[CTA_TIMESTAMP_STOP as usize]
1313            .map(|nla| u64::from_be(unsafe { mnl_attr_get_u64(nla) }))
1314    }
1315}
1316
1317impl Debug for CtTimestamp<'_> {
1318    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1319        f.debug_struct("CtTimestamp")
1320            .field("start", &self.start())
1321            .field("stop", &self.stop())
1322            .finish()
1323    }
1324}