rusty_pcap/pcap_ng/blocks/
interface.rs

1use std::io::{Cursor, Read};
2
3use crate::{
4    byte_order::{Endianness, ReadExt, UndertminedByteOrder},
5    link_type::LinkType,
6    pcap_ng::{
7        PcapNgParseError,
8        blocks::{Block, BlockHeader},
9        options::{BlockOptions, define_options_enum},
10    },
11};
12define_options_enum! {
13    /// Options for the Interface Description Block
14    enum InterfaceOptionCodes {
15        /// The if_name option is a UTF-8 string containing the name of the device used to capture data. The string is not zero-terminated.
16        IfName = 2,
17        /// The if_description option is a UTF-8 string containing the description of the device used to capture data. The string is not zero-terminated.
18        IfDescription = 3,
19        IfIPv4Address = 4,
20        IfIPv6Address = 5,
21        IfMACAddress = 6,
22        IfEuiAddr = 7,
23        /// The if_speed option is a 64-bit unsigned value indicating the interface speed, in bits per second.
24        IfSpeed = 8,
25        IfTimestampResolution = 9,
26        IfTZone = 10,
27        IfFilter = 11,
28        IfOS = 12,
29        IfFcsLength = 13,
30        IfTsOffset = 14,
31        IfHardware = 15,
32        IfTxSpeed = 16,
33        IfRxSpeed = 17,
34    }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct InterfaceDescriptionBlock {
39    pub block_length: u32,
40    pub link_type: LinkType,
41    pub reserved: [u8; 2],
42    pub snap_length: u32,
43    pub options: Option<BlockOptions>,
44}
45impl Block for InterfaceDescriptionBlock {
46    fn block_id() -> u32 {
47        1
48    }
49    fn block_id_le() -> [u8; 4] {
50        [0x01, 0x00, 0x00, 0x00] // Interface ID for SHB
51    }
52    fn block_id_be() -> [u8; 4] {
53        [0x00, 0x00, 0x00, 0x01] //  Interface ID for SHB
54    }
55    fn minimum_size() -> usize {
56        // 12 base + 2 for link_type + 2 for reserved + 4 for snap_length
57        20
58    }
59
60    fn read_with_header<R: Read>(
61        reader: &mut R,
62        header: &BlockHeader,
63        byte_order: Option<Endianness>,
64    ) -> Result<Self, PcapNgParseError>
65    where
66        Self: Sized,
67    {
68        header.matches_block_id::<Self>()?;
69        let byte_order = byte_order
70            .or(header.endianness_from_block::<Self>())
71            .ok_or(UndertminedByteOrder)?;
72        let mut cursor = Cursor::new(reader.read_bytes::<8>()?);
73
74        let link_type = LinkType::try_from(cursor.read_u16(byte_order)?)?;
75        let reserved = cursor.read_bytes::<2>()?;
76        let snap_length = cursor.read_u32(byte_order)?;
77
78        let block_length = header.block_length_as_u32(byte_order);
79        let options_space = block_length as usize - Self::minimum_size();
80
81        let options = if options_space > 0 {
82            BlockOptions::read_option(reader, byte_order)?
83        } else {
84            None
85        };
86
87        reader.read_bytes::<4>()?;
88        Ok(Self {
89            block_length,
90            link_type,
91            reserved,
92            snap_length,
93            options,
94        })
95    }
96}
97impl InterfaceDescriptionBlock {
98    pub fn read<R: Read>(reader: &mut R, byte_order: Endianness) -> Result<Self, PcapNgParseError> {
99        let header = BlockHeader::read(reader)?;
100        Self::read_with_header::<_>(reader, &header, Some(byte_order))
101    }
102}
103
104#[cfg(test)]
105mod tests {
106
107    use crate::{
108        byte_order::Endianness,
109        pcap_ng::blocks::{InterfaceOptionCodes, interface::InterfaceDescriptionBlock},
110    };
111    #[test]
112    fn parse_bytes() -> anyhow::Result<()> {
113        let content = [
114            1, 0, 0, 0, 52, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 24, 0, 115, 105, 108, 108, 121,
115            32, 101, 116, 104, 101, 114, 110, 101, 116, 32, 105, 110, 116, 101, 114, 102, 97, 99,
116            101, 0, 0, 0, 0, 52, 0, 0, 0,
117        ];
118        let mut reader = std::io::Cursor::new(&content);
119        let interface = InterfaceDescriptionBlock::read(&mut reader, Endianness::LittleEndian)?;
120
121        assert_eq!(interface.block_length, 52);
122        assert_eq!(interface.link_type, crate::link_type::LinkType::Ethernet);
123        assert_eq!(interface.reserved, [0, 0]);
124        assert_eq!(interface.snap_length, 0);
125        assert!(interface.options.is_some());
126        let options = interface.options.unwrap();
127        assert_eq!(options.0.len(), 1);
128        assert_eq!(options.0[0].code, 2);
129        assert_eq!(
130            InterfaceOptionCodes::try_from(options.0[0].code),
131            Ok(InterfaceOptionCodes::IfName)
132        );
133        assert_eq!(options.0[0].length, 24);
134        assert_eq!(options.0[0].value, b"silly ethernet interface");
135
136        assert_eq!(reader.position(), 52);
137        Ok(())
138    }
139}