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
140
141
142
143
//! 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,
            };
            match res
                .data
                .into_iter()
                .find(|v| v.t == NfAttrType::Payload as u16)
            {
                Some(v) => Some(PacketData::L3(ethertype, v.v)),
                None => None,
            }
        }
        _ => None,
    }
}