pcap_parser/pcapng/
interface_description.rs

1use std::net::{Ipv4Addr, Ipv6Addr};
2
3use nom::error::{ErrorKind, ParseError};
4use nom::{Err, IResult};
5
6use crate::endianness::{PcapBE, PcapEndianness, PcapLE};
7use crate::{opt_parse_options, Linktype, PcapError, PcapNGOption, IDB_MAGIC};
8
9use super::*;
10
11/// An Interface Description Block (IDB) is the container for information
12/// describing an interface on which packet data is captured.
13#[derive(Debug)]
14pub struct InterfaceDescriptionBlock<'a> {
15    pub block_type: u32,
16    pub block_len1: u32,
17    pub linktype: Linktype,
18    pub reserved: u16,
19    pub snaplen: u32,
20    pub options: Vec<PcapNGOption<'a>>,
21    pub block_len2: u32,
22    pub if_tsresol: u8,
23    pub if_tsoffset: i64,
24}
25
26impl InterfaceDescriptionBlock<'_> {
27    /// Decode the interface time resolution, in units per second
28    ///
29    /// Return the resolution, or `None` if the resolution is invalid (for ex. greater than `2^64`)
30    #[inline]
31    pub fn ts_resolution(&self) -> Option<u64> {
32        build_ts_resolution(self.if_tsresol)
33    }
34
35    /// Return the interface timestamp offset
36    #[inline]
37    pub fn ts_offset(&self) -> i64 {
38        self.if_tsoffset
39    }
40
41    /// Return the `if_name` option value, if present
42    ///
43    /// If the option is present multiple times, the first value is returned.
44    ///
45    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
46    /// or `Some(Err(_))` if value is present but invalid
47    pub fn if_name(&self) -> Option<Result<&str, PcapNGOptionError>> {
48        options_get_as_str(&self.options, OptionCode::IfName)
49    }
50
51    /// Return the `if_description` option value, if present
52    ///
53    /// If the option is present multiple times, the first value is returned.
54    ///
55    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
56    /// or `Some(Err(_))` if value is present but invalid
57    pub fn if_description(&self) -> Option<Result<&str, PcapNGOptionError>> {
58        options_get_as_str(&self.options, OptionCode::IfDescription)
59    }
60
61    /// Return the `if_os` option value, if present
62    ///
63    /// If the option is present multiple times, the first value is returned.
64    ///
65    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
66    /// or `Some(Err(_))` if value is present but invalid
67    pub fn if_os(&self) -> Option<Result<&str, PcapNGOptionError>> {
68        options_get_as_str(&self.options, OptionCode::IfOs)
69    }
70
71    /// Return the `if_ipv4addr` option values, if present
72    ///
73    /// This option can be multi-valued.
74    ///
75    /// Returns `None` if option is not present, `Some(Ok(Vec))` if the value is present and valid,
76    /// or `Some(Err(_))` if value is present but invalid
77    ///
78    /// Each item of the `Vec` is a pair `(IPv4Addr, IPv4Mask)`
79    pub fn if_ipv4addr(&self) -> Option<Result<Vec<(Ipv4Addr, Ipv4Addr)>, PcapNGOptionError>> {
80        let res = self.options.iter().try_fold(Vec::new(), |mut acc, opt| {
81            if opt.code == OptionCode::IfIpv4Addr {
82                let b = opt.as_bytes()?;
83                if b.len() != 8 {
84                    return Err(PcapNGOptionError::InvalidLength);
85                }
86                let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
87                let mask = Ipv4Addr::new(b[4], b[5], b[6], b[7]);
88                acc.push((addr, mask));
89                Ok(acc)
90            } else {
91                Ok(acc)
92            }
93        });
94        if res.as_ref().map_or(false, |v| v.is_empty()) {
95            None
96        } else {
97            Some(res)
98        }
99    }
100
101    /// Return the `if_ipv6addr` option values, if present
102    ///
103    /// This option can be multi-valued.
104    ///
105    /// Returns `None` if option is not present, `Some(Ok(Vec))` if the value is present and valid,
106    /// or `Some(Err(_))` if value is present but invalid
107    ///
108    /// Each item of the `Vec` is a pair `(IPv6Addr, PrefixLen)`
109    pub fn if_ipv6addr(&self) -> Option<Result<Vec<(Ipv6Addr, u8)>, PcapNGOptionError>> {
110        let res = self.options.iter().try_fold(Vec::new(), |mut acc, opt| {
111            if opt.code == OptionCode::IfIpv4Addr {
112                let b = opt.as_bytes()?;
113                if b.len() != 17 {
114                    return Err(PcapNGOptionError::InvalidLength);
115                }
116                let mut array_u16 = [0u16; 8];
117                for i in 0..8 {
118                    array_u16[i] = ((b[2 * i] as u16) << 8) + b[2 * i + 1] as u16;
119                }
120                let addr = Ipv6Addr::new(
121                    array_u16[0],
122                    array_u16[1],
123                    array_u16[2],
124                    array_u16[3],
125                    array_u16[4],
126                    array_u16[5],
127                    array_u16[6],
128                    array_u16[7],
129                );
130                let mask = b[16];
131                acc.push((addr, mask));
132                Ok(acc)
133            } else {
134                Ok(acc)
135            }
136        });
137        if res.as_ref().map_or(false, |v| v.is_empty()) {
138            None
139        } else {
140            Some(res)
141        }
142    }
143
144    /// Return the `if_macaddr` option value, if present
145    ///
146    /// If the option is present multiple times, the first value is returned.
147    ///
148    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
149    /// or `Some(Err(_))` if value is present but invalid
150    pub fn if_macaddr(&self) -> Option<Result<&[u8], PcapNGOptionError>> {
151        options_get_as_bytes(&self.options, OptionCode::IfMacAddr)
152    }
153
154    /// Return the `if_euiaddr` option value, if present
155    ///
156    /// If the option is present multiple times, the first value is returned.
157    ///
158    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
159    /// or `Some(Err(_))` if value is present but invalid
160    pub fn if_euiaddr(&self) -> Option<Result<&[u8], PcapNGOptionError>> {
161        options_get_as_bytes(&self.options, OptionCode::IfEuiAddr)
162    }
163
164    /// Return the `if_speed` option value, if present
165    ///
166    /// If the option is present multiple times, the first value is returned.
167    ///
168    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
169    /// or `Some(Err(_))` if value is present but invalid
170    pub fn if_speed(&self) -> Option<Result<u64, PcapNGOptionError>> {
171        options_get_as_u64_le(&self.options, OptionCode::IfSpeed)
172    }
173
174    /// Return the `if_tsresol` option value, if present
175    ///
176    /// If the option is present multiple times, the first value is returned.
177    ///
178    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
179    /// or `Some(Err(_))` if value is present but invalid
180    pub fn if_tsresol(&self) -> Option<Result<u8, PcapNGOptionError>> {
181        options_get_as_u8(&self.options, OptionCode::IfTsresol)
182    }
183
184    /// Return the `if_filter` option value, if present
185    ///
186    /// If the option is present multiple times, the first value is returned.
187    ///
188    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
189    /// or `Some(Err(_))` if value is present but invalid
190    pub fn if_filter(&self) -> Option<Result<&str, PcapNGOptionError>> {
191        options_get_as_str(&self.options, OptionCode::IfFilter)
192    }
193
194    /// Return the `if_tsoffset` option value, if present
195    ///
196    /// If the option is present multiple times, the first value is returned.
197    ///
198    /// Returns `None` if option is not present, `Some(Ok(value))` if the value is present and valid,
199    /// or `Some(Err(_))` if value is present but invalid
200    pub fn if_tsoffset(&self) -> Option<Result<i64, PcapNGOptionError>> {
201        options_get_as_i64_le(&self.options, OptionCode::IfTsoffset)
202    }
203}
204
205impl<'a, En: PcapEndianness> PcapNGBlockParser<'a, En, InterfaceDescriptionBlock<'a>>
206    for InterfaceDescriptionBlock<'a>
207{
208    const HDR_SZ: usize = 20;
209    const MAGIC: u32 = IDB_MAGIC;
210
211    fn inner_parse<E: ParseError<&'a [u8]>>(
212        block_type: u32,
213        block_len1: u32,
214        i: &'a [u8],
215        block_len2: u32,
216    ) -> IResult<&'a [u8], InterfaceDescriptionBlock<'a>, E> {
217        // caller function already tested header type(magic) and length
218        // read end of header
219        let (i, linktype) = En::parse_u16(i)?;
220        let (i, reserved) = En::parse_u16(i)?;
221        let (i, snaplen) = En::parse_u32(i)?;
222        // read options
223        let (i, options) = opt_parse_options::<En, E>(i, block_len1 as usize, 20)?;
224        if block_len2 != block_len1 {
225            return Err(Err::Error(E::from_error_kind(i, ErrorKind::Verify)));
226        }
227        let (if_tsresol, if_tsoffset) = if_extract_tsoffset_and_tsresol(&options);
228        let block = InterfaceDescriptionBlock {
229            block_type,
230            block_len1,
231            linktype: Linktype(linktype as i32),
232            reserved,
233            snaplen,
234            options,
235            block_len2,
236            if_tsresol,
237            if_tsoffset,
238        };
239        Ok((i, block))
240    }
241}
242
243/// Parse an Interface Packet Block (little-endian)
244pub fn parse_interfacedescriptionblock_le(
245    i: &[u8],
246) -> IResult<&[u8], InterfaceDescriptionBlock<'_>, PcapError<&[u8]>> {
247    ng_block_parser::<InterfaceDescriptionBlock, PcapLE, _, _>()(i)
248}
249
250/// Parse an Interface Packet Block (big-endian)
251pub fn parse_interfacedescriptionblock_be(
252    i: &[u8],
253) -> IResult<&[u8], InterfaceDescriptionBlock<'_>, PcapError<&[u8]>> {
254    ng_block_parser::<InterfaceDescriptionBlock, PcapBE, _, _>()(i)
255}