nfqueue/
message.rs

1extern crate libc;
2
3use hwaddr::*;
4use std;
5
6type NfqueueData = *const libc::c_void;
7
8/// Opaque struct `Message`: abstracts NFLOG data representing a packet data and metadata
9pub struct Message {
10    qqh  : *const libc::c_void,
11    nfad : NfqueueData,
12    id   : u32,
13}
14
15#[derive(Debug)]
16pub enum NfqueueError {
17    NoSuchAttribute,
18}
19
20/// Decision on the packet
21#[derive(Clone)]
22pub enum Verdict {
23    /// Discard the packet
24    Drop,
25    /// Accept the packet (continue iterations)
26    Accept,
27    /// Gone away
28    Stolen,
29    /// Inject the packet into a different queue ((the target queue number is in the high 16 bits of the verdict)
30    Queue,
31    /// Iterate the same cycle one more
32    Repeat,
33    /// Accept, but don't continue iterations
34    Stop,
35}
36
37const NF_DROP   : u32 = 0x0000;
38const NF_ACCEPT : u32 = 0x0001;
39const NF_STOLEN : u32 = 0x0002;
40const NF_QUEUE  : u32 = 0x0003;
41const NF_REPEAT : u32 = 0x0004;
42const NF_STOP   : u32 = 0x0005;
43
44fn u32_of_verdict(v: Verdict) -> u32 {
45    match v {
46        Verdict::Drop   => NF_DROP,
47        Verdict::Accept => NF_ACCEPT,
48        Verdict::Stolen => NF_STOLEN,
49        Verdict::Queue  => NF_QUEUE,
50        Verdict::Repeat => NF_REPEAT,
51        Verdict::Stop   => NF_STOP,
52    }
53}
54
55/// XML formatting flags
56pub enum XMLFormatFlags {
57    XmlHw,
58    XmlMark,
59    XmlDev,
60    XmlPhysDev,
61    XmlPayload,
62    XmlTime,
63    XmlAll,
64}
65
66const NFQ_XML_HW      : u32  = (1 << 0);
67const NFQ_XML_MARK    : u32  = (1 << 1);
68const NFQ_XML_DEV     : u32  = (1 << 2);
69const NFQ_XML_PHYSDEV : u32  = (1 << 3);
70const NFQ_XML_PAYLOAD : u32  = (1 << 4);
71const NFQ_XML_TIME    : u32  = (1 << 5);
72const NFQ_XML_ALL     : u32  = (!0u32);
73
74/// Hardware address
75#[repr(C)]
76struct NfMsgPacketHw {
77    /// Hardware address length
78    pub hw_addrlen : u16,
79    /// Padding (should be ignored)
80    pub _pad : u16,
81    /// The hardware address
82    pub hw_addr : [u8;8],
83}
84
85/// Metaheader wrapping a packet
86#[repr(C)]
87pub struct NfMsgPacketHdr {
88    /// unique ID of the packet
89    pub packet_id : u32,
90    /// hw protocol (network order)
91    pub hw_protocol : u16,
92    /// Netfilter hook
93    pub hook : u8,
94}
95
96#[link(name = "netfilter_queue")]
97extern {
98    // queue handling
99    //fn nfq_set_verdict(qqh: *const libc::c_void, id: u32, verdict: u32, data_len: u32, data: *const libc::c_uchar);
100    // requires netfilter_queue >= 1.0
101    fn nfq_set_verdict2(qqh: *const libc::c_void, id: u32, verdict: u32, mark: u32, data_len: u32, data: *const libc::c_uchar);
102
103    // message parsing functions
104    fn nfq_get_msg_packet_hdr(nfad: NfqueueData) -> *const libc::c_void;
105    fn nfq_get_nfmark (nfad: NfqueueData) -> u32;
106    fn nfq_get_timestamp (nfad: NfqueueData, tv: *mut libc::timeval) -> u32;
107    fn nfq_get_indev (nfad: NfqueueData) -> u32;
108    fn nfq_get_physindev (nfad: NfqueueData) -> u32;
109    fn nfq_get_outdev (nfad: NfqueueData) -> u32;
110    fn nfq_get_physoutdev (nfad: NfqueueData) -> u32;
111
112    fn nfq_get_packet_hw (nfad: NfqueueData) -> *const NfMsgPacketHw;
113    fn nfq_get_payload (nfad: NfqueueData, data: &*mut libc::c_void) -> libc::c_int;
114
115    // printing functions
116    fn nfq_snprintf_xml (buf: *mut u8, rem: libc::size_t, tb: NfqueueData, flags: libc::c_uint) -> libc::c_int;
117}
118
119impl Message {
120    /// Create a `Messsage` from a valid NfqueueData pointer
121    ///
122    /// **This function should never be called directly**
123    #[doc(hidden)]
124    pub fn new(qqh: *const libc::c_void, nfad: *const libc::c_void) -> Message {
125        let msg_hdr = unsafe { nfq_get_msg_packet_hdr(nfad) as *const NfMsgPacketHdr };
126        assert!(!msg_hdr.is_null());
127        let id = u32::from_be( unsafe{(*msg_hdr).packet_id} );
128        Message {
129            qqh : qqh,
130            nfad: nfad,
131            id : id,
132        }
133    }
134
135    /// Returns the unique ID of the packet
136    pub fn get_id(&self) -> u32 {
137        self.id
138    }
139
140    /// Get the packet mark
141    pub fn get_nfmark(&self) -> u32 {
142        return unsafe { nfq_get_nfmark(self.nfad) };
143    }
144
145    /// Get the packet timestamp
146    pub fn get_timestamp(&self) -> Result<libc::timeval,NfqueueError> {
147        let mut tv = libc::timeval {
148            tv_sec: 0,
149            tv_usec: 0,
150        };
151        let rc = unsafe { nfq_get_timestamp(self.nfad,&mut tv) };
152        match rc {
153            0 => Ok(tv),
154            _ => Err(NfqueueError::NoSuchAttribute),
155        }
156    }
157
158    /// Get the interface that the packet was received through
159    ///
160    /// Returns the index of the device the packet was received via.
161    /// If the returned index is 0, the packet was locally generated or the
162    /// input interface is not known (ie. `POSTROUTING`?).
163    pub fn get_indev(&self) -> u32 {
164        return unsafe { nfq_get_indev(self.nfad) };
165    }
166
167    /// Get the physical interface that the packet was received through
168    ///
169    /// Returns the index of the physical device the packet was received via.
170    /// If the returned index is 0, the packet was locally generated or the
171    /// physical input interface is no longer known (ie. `POSTROUTING`?).
172    pub fn get_physindev(&self) -> u32 {
173        return unsafe { nfq_get_physindev(self.nfad) };
174    }
175
176    /// Get the interface that the packet will be routed out
177    ///
178    /// Returns the index of the device the packet will be sent out.
179    /// If the returned index is 0, the packet is destined to localhost or
180    /// the output interface is not yet known (ie. `PREROUTING`?).
181    pub fn get_outdev(&self) -> u32 {
182        return unsafe { nfq_get_outdev(self.nfad) };
183    }
184
185    /// Get the physical interface that the packet will be routed out
186    ///
187    /// Returns the index of the physical device the packet will be sent out.
188    /// If the returned index is 0, the packet is destined to localhost or
189    /// the physical output interface is not yet known (ie. `PREROUTING`?).
190    pub fn get_physoutdev(&self) -> u32 {
191        return unsafe { nfq_get_physoutdev(self.nfad) };
192    }
193
194
195
196
197
198    /// Get hardware address
199    ///
200    /// Retrieves the hardware address associated with the given packet.
201    ///
202    /// For ethernet packets, the hardware address returned (if any) will be
203    /// the MAC address of the packet *source* host.
204    ///
205    /// The destination MAC address is not
206    /// known until after POSTROUTING and a successful ARP request, so cannot
207    /// currently be retrieved.
208    pub fn get_packet_hw<'a>(&'a self) -> Result<HwAddr<'a>,NfqueueError> {
209        let c_hw = unsafe { nfq_get_packet_hw(self.nfad) };
210
211        if c_hw == std::ptr::null() {
212            return Err(NfqueueError::NoSuchAttribute);
213        }
214
215        let c_len = u16::from_be(unsafe{(*c_hw).hw_addrlen}) as usize;
216        match c_len {
217            0 => Err(NfqueueError::NoSuchAttribute),
218            _ => Ok( HwAddr::new(unsafe{&((*c_hw).hw_addr)[0..c_len]})),
219        }
220    }
221
222
223
224    /// Issue a verdict on a packet
225    ///
226    /// Notifies netfilter of the userspace verdict for the given packet.
227    ///
228    /// Every queued packet **must** have a verdict specified by userspace,
229    /// either by calling this function, or by calling any other
230    /// `set_verdict_*` function.
231    ///
232    /// Arguments
233    ///
234    /// * `verdict`: verdict to return to netfilter (`Verdict::Accept`,
235    ///   `Verdict::Drop`, ...)
236    pub fn set_verdict(&self, verdict: Verdict) {
237        assert!(!self.qqh.is_null());
238        let c_verdict = u32_of_verdict(verdict);
239        //unsafe { nfq_set_verdict(self.qqh, self.id, c_verdict, 0, std::ptr::null_mut()) };
240        unsafe { nfq_set_verdict2(self.qqh, self.id, c_verdict, 0, 0, std::ptr::null_mut()) };
241    }
242
243    /// Issue a verdict on a packet, with a mark
244    ///
245    /// Notifies netfilter of the userspace verdict for the given packet.
246    ///
247    /// Every queued packet **must** have a verdict specified by userspace,
248    /// either by calling this function, or by calling any other
249    /// `set_verdict_*` function.
250    ///
251    /// Arguments
252    ///
253    /// * `verdict`: verdict to return to netfilter (`Verdict::Accept`,
254    ///   `Verdict::Drop`, ...)
255    /// * `mark`: the mark to put on the packet, in network-byte order
256    pub fn set_verdict_mark(&self, verdict: Verdict, mark: u32) {
257        assert!(!self.qqh.is_null());
258        let c_verdict = u32_of_verdict(verdict);
259        //unsafe { nfq_set_verdict(self.qqh, self.id, c_verdict, 0, std::ptr::null_mut()) };
260        unsafe { nfq_set_verdict2(self.qqh, self.id, c_verdict, mark, 0, std::ptr::null_mut()) };
261    }
262
263    /// Issue a verdict on a packet, with a mark and new data
264    ///
265    /// Notifies netfilter of the userspace verdict for the given packet.
266    /// The new packet will replace the one that was queued.
267    ///
268    /// Every queued packet **must** have a verdict specified by userspace,
269    /// either by calling this function, or by calling any other
270    /// `set_verdict_*` function.
271    ///
272    /// Arguments
273    ///
274    /// * `verdict`: verdict to return to netfilter (`Verdict::Accept`,
275    ///   `Verdict::Drop`, ...)
276    /// * `mark`: the mark to put on the packet, in network-byte order
277    /// * `data`: the new packet
278    pub fn set_verdict_full(&self, verdict: Verdict, mark: u32, data: &[u8]) {
279        assert!(!self.qqh.is_null());
280        let c_verdict = u32_of_verdict(verdict);
281        let data_ptr = data.as_ptr() as *const libc::c_uchar;
282        let data_len = data.len() as u32;
283        //unsafe { nfq_set_verdict(self.qqh, self.id, c_verdict, 0, std::ptr::null_mut()) };
284        unsafe { nfq_set_verdict2(self.qqh, self.id, c_verdict, mark, data_len, data_ptr) };
285    }
286
287    /// Get payload
288    ///
289    /// Depending on set_mode, we may not have a payload.
290    /// The actual amount and type of data retrieved by this function will
291    /// depend on the mode set with the `set_mode()` function.
292    pub fn get_payload<'a>(&'a self) -> &'a [u8] {
293        let c_ptr = std::ptr::null_mut();
294        let payload_len = unsafe { nfq_get_payload(self.nfad, &c_ptr) };
295        let payload : &[u8] = unsafe { std::slice::from_raw_parts(c_ptr as *mut u8, payload_len as usize) };
296
297        return payload;
298    }
299
300    /// Print the queued packet in XML format into a buffer
301    pub fn as_xml_str(&self, flags: &[XMLFormatFlags]) -> Result<String,std::str::Utf8Error> {
302        // if buffer size is smaller than output, nfq_snprintf_xml will fail
303        let mut buf : [u8;65536] = [0;65536];
304        let buf_ptr = buf.as_mut_ptr() as *mut libc::c_uchar;
305        let buf_len = buf.len() as libc::size_t;
306
307        let xml_flags = flags.iter().map(|flag| {
308            match *flag {
309                XMLFormatFlags::XmlHw      => NFQ_XML_HW,
310                XMLFormatFlags::XmlMark    => NFQ_XML_MARK,
311                XMLFormatFlags::XmlDev     => NFQ_XML_DEV,
312                XMLFormatFlags::XmlPhysDev => NFQ_XML_PHYSDEV,
313                XMLFormatFlags::XmlPayload => NFQ_XML_PAYLOAD,
314                XMLFormatFlags::XmlTime    => NFQ_XML_TIME,
315                XMLFormatFlags::XmlAll     => NFQ_XML_ALL,
316            }
317        }).fold(0u32, |acc, i| acc | i);
318
319        let rc = unsafe { nfq_snprintf_xml(buf_ptr, buf_len, self.nfad, xml_flags) };
320        if rc < 0 { panic!("nfq_snprintf_xml"); } // XXX see snprintf error codes
321
322        match std::str::from_utf8(&buf) {
323            Ok(v) => Ok(v.to_string()),
324            Err(e) => Err(e),
325        }
326    }
327}
328
329use std::fmt;
330use std::fmt::Write;
331
332impl fmt::Display for Message {
333    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
334        let payload_data = self.get_payload();
335        let mut s = String::new();
336        for &byte in payload_data {
337            write!(&mut s, "{:X} ", byte).unwrap();
338        }
339        write!(out, "{}", s)
340    }
341}
342