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