pcap_parser/data/
pcap_nflog.rs

1//! NFLOG link layer encapsulation for PCAP
2//!
3//! Helper module to access content of data stored using NFLOG (239)
4//! data link type.
5//!
6//! See <http://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html> for details.
7
8use crate::data::{PacketData, ETHERTYPE_IPV4, ETHERTYPE_IPV6};
9use nom::bytes::streaming::take;
10use nom::combinator::{complete, cond, verify};
11use nom::multi::many0;
12use nom::number::streaming::{be_u16, le_u16, le_u8};
13use nom::{IResult, Parser as _};
14
15// Defined in linux/netfilter/nfnetlink_log.h
16#[derive(Copy, Clone)]
17#[repr(u16)]
18pub enum NfAttrType {
19    /// packet header structure: hardware protocol (2 bytes), nf hook (1 byte), padding (1 byte)
20    PacketHdr = 1,
21    /// packet mark value from the skbuff for the packet
22    Mark = 2,
23    /// packet time stamp structure: seconds (8 bytes), microseconds (8 bytes)
24    Timestamp = 3,
25    /// 32-bit ifindex of the device on which the packet was received, which could be a bridge group
26    IfIndexInDev = 4,
27    /// 32-bit ifindex of the device on which the packet was sent, which could be a bridge group
28    IfIndexOutDev = 5,
29    /// 32-bit ifindex of the physical device on which the packet was received, which is not a bridge group
30    IfIndexPhysInDev = 6,
31    /// 32-bit ifindex of the physical device on which the packet was sent, which is not a bridge group
32    IfIndexPhysOutDev = 7,
33    /// hardware address structure:
34    /// address length (2 bytes), padding (1 byte), address (8 bytes)
35    HwAddr = 8,
36    /// packet payload following the link-layer header
37    Payload = 9,
38    /// null-terminated text string
39    Prefix = 10,
40    /// 32-bit ifindex of the device on which the packet was received, which could be a bridge group
41    Uid = 11,
42    /// 32-bit sequence number for packets provided by this nflog device
43    Seq = 12,
44    /// 32-bit sequence number for packets provided by all nflog devices
45    SeqGlobal = 13,
46    /// 32-bit group ID that owned the socket on which the packet was sent or received
47    Gid = 14,
48    /// 32-bit Linux ARPHRD_ value for the device associated with the skbuff for the packet
49    HwType = 15,
50    /// MAC-layer header for the skbuff for the packet
51    HwHeader = 16,
52    /// length of the MAC-layer header
53    HwLen = 17,
54    /// conntrack header (nfnetlink_conntrack.h)
55    Ct = 18,
56    /// enum ip_conntrack_info
57    CtInfo = 19,
58}
59
60#[derive(Debug)]
61pub struct NflogTlv<'a> {
62    /// Length of data (including 4 bytes for length and types)
63    pub l: u16,
64    /// Type of data (see `NfAttrType`)
65    pub t: u16,
66    /// Data
67    pub v: &'a [u8],
68}
69
70pub fn parse_nflog_tlv(i: &[u8]) -> IResult<&[u8], NflogTlv<'_>> {
71    let (i, l) = verify(le_u16, |&n| n >= 4).parse(i)?;
72    let (i, t) = le_u16(i)?;
73    let (i, v) = take(l - 4)(i)?;
74    let (i, _padding) = cond(l % 4 != 0, take(4 - (l % 4))).parse(i)?;
75    Ok((i, NflogTlv { l, t, v }))
76}
77
78#[derive(Debug)]
79pub struct NflogHdr {
80    /// Address family
81    pub af: u8,
82    /// Version (currently: 0)
83    pub vers: u8,
84    /// Resource ID: nflog group for the packet
85    pub res_id: u16,
86}
87
88#[derive(Debug)]
89pub struct NflogPacket<'a> {
90    /// The nflog packet header
91    pub header: NflogHdr,
92    /// The objects (Type-Length-Value)
93    pub data: Vec<NflogTlv<'a>>,
94}
95
96pub fn parse_nflog_header(i: &[u8]) -> IResult<&[u8], NflogHdr> {
97    let (i, af) = le_u8(i)?;
98    let (i, vers) = le_u8(i)?;
99    let (i, res_id) = be_u16(i)?;
100    Ok((i, NflogHdr { af, vers, res_id }))
101}
102
103impl NflogPacket<'_> {
104    pub fn get(&self, attr: NfAttrType) -> Option<&NflogTlv<'_>> {
105        self.data.iter().find(|v| v.t == attr as u16)
106    }
107
108    pub fn get_payload(&self) -> Option<&[u8]> {
109        self.get(NfAttrType::Payload).map(|tlv| tlv.v)
110    }
111}
112
113pub fn parse_nflog(i: &[u8]) -> IResult<&[u8], NflogPacket<'_>> {
114    let (i, header) = parse_nflog_header(i)?;
115    let (i, data) = many0(complete(parse_nflog_tlv)).parse(i)?;
116    Ok((i, NflogPacket { header, data }))
117}
118
119/// Get packet data for LINKTYPE_NFLOG (239)
120///
121/// Parse nflog data, and extract only packet payload
122///
123/// See <http://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html>
124pub fn get_packetdata_nflog(i: &[u8], _caplen: usize) -> Option<PacketData<'_>> {
125    match parse_nflog(i) {
126        Ok((_, res)) => {
127            let ethertype = match res.header.af {
128                2 => ETHERTYPE_IPV4,
129                10 => ETHERTYPE_IPV6,
130                _ => 0,
131            };
132            res.data
133                .into_iter()
134                .find(|v| v.t == NfAttrType::Payload as u16)
135                .map(|tlv| PacketData::L3(ethertype, tlv.v))
136        }
137        _ => None,
138    }
139}