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, Parser as _};
#[derive(Copy, Clone)]
#[repr(u16)]
pub enum NfAttrType {
PacketHdr = 1,
Mark = 2,
Timestamp = 3,
IfIndexInDev = 4,
IfIndexOutDev = 5,
IfIndexPhysInDev = 6,
IfIndexPhysOutDev = 7,
HwAddr = 8,
Payload = 9,
Prefix = 10,
Uid = 11,
Seq = 12,
SeqGlobal = 13,
Gid = 14,
HwType = 15,
HwHeader = 16,
HwLen = 17,
Ct = 18,
CtInfo = 19,
}
#[derive(Debug)]
pub struct NflogTlv<'a> {
pub l: u16,
pub t: u16,
pub v: &'a [u8],
}
pub fn parse_nflog_tlv(i: &[u8]) -> IResult<&[u8], NflogTlv<'_>> {
let (i, l) = verify(le_u16, |&n| n >= 4).parse(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))).parse(i)?;
Ok((i, NflogTlv { l, t, v }))
}
#[derive(Debug)]
pub struct NflogHdr {
pub af: u8,
pub vers: u8,
pub res_id: u16,
}
#[derive(Debug)]
pub struct NflogPacket<'a> {
pub header: NflogHdr,
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 NflogPacket<'_> {
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)).parse(i)?;
Ok((i, NflogPacket { header, data }))
}
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,
}
}