pcap_parser/data/
exported_pdu.rs

1use crate::data::PacketData;
2use nom::bytes::streaming::{tag, take};
3use nom::multi::many_till;
4use nom::number::streaming::be_u16;
5use nom::{IResult, Parser as _};
6use std::convert::TryFrom;
7
8/* values from epan/exported_pdu.h */
9
10pub const EXP_PDU_TAG_PROTO_NAME: u16 = 12;
11pub const EXP_PDU_TAG_DISSECTOR_TABLE_NAME: u16 = 14;
12
13pub const EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL: u16 = 32;
14
15#[derive(Debug)]
16pub struct ExportedTlv<'a> {
17    pub t: u16,
18    pub l: u16,
19    pub v: &'a [u8],
20}
21
22pub fn parse_exported_tlv(i: &[u8]) -> IResult<&[u8], ExportedTlv<'_>> {
23    let (i, t) = be_u16(i)?;
24    let (i, l) = be_u16(i)?;
25    let (i, v) = take(l)(i)?;
26    Ok((i, ExportedTlv { t, l, v }))
27}
28
29pub fn parse_many_exported_tlv(i: &[u8]) -> IResult<&[u8], Vec<ExportedTlv<'_>>> {
30    many_till(parse_exported_tlv, tag(b"\x00\x00\x00\x00" as &[u8]))
31        .parse(i)
32        .map(|(rem, (v, _))| (rem, v))
33}
34
35/// Get packet data for WIRESHARK_UPPER_PDU (252)
36///
37/// Upper-layer protocol saves from Wireshark
38pub fn get_packetdata_wireshark_upper_pdu(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
39    if i.len() < caplen || caplen == 0 {
40        None
41    } else {
42        match parse_many_exported_tlv(i) {
43            Ok((rem, v)) => {
44                // get protocol name (or return None)
45                let proto_name = v
46                    .iter()
47                    .find(|tlv| tlv.t == EXP_PDU_TAG_DISSECTOR_TABLE_NAME)
48                    .map(|tlv| tlv.v)?;
49                // get protocol value (or return None)
50                let ip_proto = v
51                    .iter()
52                    .find(|tlv| tlv.t == EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL && tlv.l >= 4)
53                    .map(|tlv| {
54                        let int_bytes = <[u8; 4]>::try_from(tlv.v).expect("Convert bytes to u32");
55                        u32::from_be_bytes(int_bytes)
56                    })?;
57                match proto_name {
58                    b"ip.proto" => Some(PacketData::L4(ip_proto as u8, rem)),
59                    _ => {
60                        // XXX unknown protocol name
61                        None
62                    }
63                }
64            }
65            _ => None,
66        }
67    }
68}
69
70#[cfg(test)]
71pub mod tests {
72    use super::get_packetdata_wireshark_upper_pdu;
73    use crate::data::PacketData;
74    use hex_literal::hex;
75    pub const UPPER_PDU: &[u8] = &hex!(
76        "
7700 0e 00 08 69 70 2e 70 72 6f 74 6f 00 20 00 04
7800 00 00 11 00 00 00 00 00 58 20 20 20 ff ff 20
7963 20 68 20 a0 20 7f 20 8a 20 20 20 20 20 20 ff
80ff ff ff ff 20 00 00 00"
81    );
82    #[test]
83    fn test_wireshark_exported_pdu() {
84        match get_packetdata_wireshark_upper_pdu(UPPER_PDU, UPPER_PDU.len()) {
85            Some(PacketData::L4(proto, data)) => {
86                assert_eq!(proto, 17);
87                assert_eq!(data.len(), 32);
88            }
89            None => panic!("get_packetdata_wireshark_upper_pdu could not decode exported PDU"),
90            _ => panic!("unexpected result type from get_packetdata_wireshark_upper_pdu"),
91        }
92    }
93}