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}