pcap_file/pcapng/blocks/
interface_description.rs

1#![allow(clippy::cast_lossless)]
2
3//! Interface Description Block (IDB).
4
5use std::borrow::Cow;
6use std::io::Write;
7use std::time::Duration;
8
9use byteorder_slice::byteorder::WriteBytesExt;
10use byteorder_slice::result::ReadSlice;
11use byteorder_slice::ByteOrder;
12use derive_into_owned::IntoOwned;
13use once_cell::sync::Lazy;
14
15use super::block_common::{Block, PcapNgBlock};
16use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo};
17use crate::errors::PcapError;
18use crate::pcapng::PcapNgState;
19use crate::DataLink;
20
21
22/// An Interface Description Block (IDB) is the container for information describing an interface
23/// on which packet data is captured.
24#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
25pub struct InterfaceDescriptionBlock<'a> {
26    /// A value that defines the link layer type of this interface.
27    ///
28    /// The list of Standardized Link Layer Type codes is available in the
29    /// [tcpdump.org link-layer header types registry.](http://www.tcpdump.org/linktypes.html).
30    pub linktype: DataLink,
31
32    /// Maximum number of octets captured from each packet.
33    ///
34    /// The portion of each packet that exceeds this value will not be stored in the file.
35    /// A value of zero indicates no limit.
36    pub snaplen: u32,
37
38    /// Options
39    pub options: Vec<InterfaceDescriptionOption<'a>>,
40}
41
42impl<'a> PcapNgBlock<'a> for InterfaceDescriptionBlock<'a> {
43    fn from_slice<B: ByteOrder>(state: &PcapNgState, mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
44        if slice.len() < 8 {
45            return Err(PcapError::InvalidField("InterfaceDescriptionBlock: block length < 8"));
46        }
47
48        let linktype = (slice.read_u16::<B>().unwrap() as u32).into();
49
50        let reserved = slice.read_u16::<B>().unwrap();
51        if reserved != 0 {
52            return Err(PcapError::InvalidField("InterfaceDescriptionBlock: reserved != 0"));
53        }
54
55        let snaplen = slice.read_u32::<B>().unwrap();
56        let (slice, options) = InterfaceDescriptionOption::opts_from_slice::<B>(state, None, slice)?;
57
58        let block = InterfaceDescriptionBlock { linktype, snaplen, options };
59
60        Ok((slice, block))
61    }
62
63    fn write_to<B: ByteOrder, W: Write>(&self, state: &PcapNgState, writer: &mut W) -> Result<usize, PcapError> {
64        writer.write_u16::<B>(u32::from(self.linktype) as u16)?;
65        writer.write_u16::<B>(0)?;
66        writer.write_u32::<B>(self.snaplen)?;
67
68        let opt_len = InterfaceDescriptionOption::write_opts_to::<B, W>(&self.options, state, None, writer)?;
69        Ok(8 + opt_len)
70    }
71
72    fn into_block(self) -> Block<'a> {
73        Block::InterfaceDescription(self)
74    }
75}
76
77impl<'a> InterfaceDescriptionBlock<'a> {
78    /// Creates a new [`InterfaceDescriptionBlock`]
79    pub fn new(linktype: DataLink, snaplen: u32) -> Self {
80        Self { linktype, snaplen, options: vec![] }
81    }
82
83    /// Returns the timestamp resolution of the interface.
84    /// If no ts_resolution is set, defaults to μs.
85    pub fn ts_resolution(&self) -> Result<TsResolution, PcapError> {
86        let mut ts_resol = Ok(TsResolution::default());
87
88        for opt in &self.options {
89            if let InterfaceDescriptionOption::IfTsResol(resol) = opt {
90                ts_resol = TsResolution::new(*resol);
91                break;
92            }
93        }
94
95        ts_resol
96    }
97
98    /// Returns the timestamp offset of the interface, or zero if it has none.
99    pub fn ts_offset(&self) -> Duration {
100        for opt in &self.options {
101            if let InterfaceDescriptionOption::IfTsOffset(offset) = opt {
102                return Duration::from_secs(*offset)
103            }
104        }
105
106        Duration::ZERO
107    }
108}
109
110
111/* ----- */
112
113/// The Interface Description Block (IDB) options
114#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
115pub enum InterfaceDescriptionOption<'a> {
116    /// The opt_comment option is a UTF-8 string containing human-readable comment text
117    /// that is associated to the current block.
118    Comment(Cow<'a, str>),
119
120    /// The if_name option is a UTF-8 string containing the name of the device used to capture data.
121    IfName(Cow<'a, str>),
122
123    /// The if_description option is a UTF-8 string containing the description of the device used to capture data.
124    IfDescription(Cow<'a, str>),
125
126    /// The if_IPv4addr option is an IPv4 network address and corresponding netmask for the interface.
127    IfIpv4Addr(Cow<'a, [u8]>),
128
129    /// The if_IPv6addr option is an IPv6 network address and corresponding prefix length for the interface.
130    IfIpv6Addr(Cow<'a, [u8]>),
131
132    /// The if_MACaddr option is the Interface Hardware MAC address (48 bits), if available.
133    IfMacAddr(Cow<'a, [u8]>),
134
135    /// The if_EUIaddr option is the Interface Hardware EUI address (64 bits), if available.
136    IfEuIAddr(u64),
137
138    /// The if_speed option is a 64-bit number for the Interface speed (in bits per second).
139    IfSpeed(u64),
140
141    /// The if_tsresol option identifies the resolution of timestamps.
142    IfTsResol(u8),
143
144    /// The if_tzone option identifies the time zone for GMT support.
145    IfTzone(u32),
146
147    /// The if_filter option identifies the filter (e.g. "capture only TCP traffic") used to capture traffic.
148    IfFilter(Cow<'a, [u8]>),
149
150    /// The if_os option is a UTF-8 string containing the name of the operating system
151    /// of the machine in which this interface is installed.
152    IfOs(Cow<'a, str>),
153
154    /// The if_fcslen option is an 8-bit unsigned integer value that specifies
155    /// the length of the Frame Check Sequence (in bits) for this interface.
156    IfFcsLen(u8),
157
158    /// The if_tsoffset option is a 64-bit integer value that specifies an offset (in seconds)
159    /// that must be added to the timestamp of each packet to obtain the absolute timestamp of a packet.
160    IfTsOffset(u64),
161
162    /// The if_hardware option is a UTF-8 string containing the description of the interface hardware.
163    IfHardware(Cow<'a, str>),
164
165    /// Custom option containing binary octets in the Custom Data portion
166    CustomBinary(CustomBinaryOption<'a>),
167
168    /// Custom option containing a UTF-8 string in the Custom Data portion
169    CustomUtf8(CustomUtf8Option<'a>),
170
171    /// Unknown option
172    Unknown(UnknownOption<'a>),
173}
174
175impl<'a> PcapNgOption<'a> for InterfaceDescriptionOption<'a> {
176    fn from_slice<B: ByteOrder>(_state: &PcapNgState, _interface_id: Option<u32>, code: u16, length: u16, mut slice: &'a [u8]) -> Result<Self, PcapError> {
177        let opt = match code {
178            1 => InterfaceDescriptionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)),
179            2 => InterfaceDescriptionOption::IfName(Cow::Borrowed(std::str::from_utf8(slice)?)),
180            3 => InterfaceDescriptionOption::IfDescription(Cow::Borrowed(std::str::from_utf8(slice)?)),
181            4 => {
182                if slice.len() != 8 {
183                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv4Addr length != 8"));
184                }
185                InterfaceDescriptionOption::IfIpv4Addr(Cow::Borrowed(slice))
186            },
187            5 => {
188                if slice.len() != 17 {
189                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv6Addr length != 17"));
190                }
191                InterfaceDescriptionOption::IfIpv6Addr(Cow::Borrowed(slice))
192            },
193            6 => {
194                if slice.len() != 6 {
195                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfMacAddr length != 6"));
196                }
197                InterfaceDescriptionOption::IfMacAddr(Cow::Borrowed(slice))
198            },
199            7 => {
200                if slice.len() != 8 {
201                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfEuIAddr length != 8"));
202                }
203                InterfaceDescriptionOption::IfEuIAddr(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
204            },
205            8 => {
206                if slice.len() != 8 {
207                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfSpeed length != 8"));
208                }
209                InterfaceDescriptionOption::IfSpeed(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
210            },
211            9 => {
212                if slice.len() != 1 {
213                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsResol length != 1"));
214                }
215                InterfaceDescriptionOption::IfTsResol(slice.read_u8().map_err(|_| PcapError::IncompleteBuffer)?)
216            },
217            10 => {
218                if slice.len() != 1 {
219                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTzone length != 1"));
220                }
221                InterfaceDescriptionOption::IfTzone(slice.read_u32::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
222            },
223            11 => {
224                if slice.is_empty() {
225                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFilter is empty"));
226                }
227                InterfaceDescriptionOption::IfFilter(Cow::Borrowed(slice))
228            },
229            12 => InterfaceDescriptionOption::IfOs(Cow::Borrowed(std::str::from_utf8(slice)?)),
230            13 => {
231                if slice.len() != 1 {
232                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFcsLen length != 1"));
233                }
234                InterfaceDescriptionOption::IfFcsLen(slice.read_u8().map_err(|_| PcapError::IncompleteBuffer)?)
235            },
236            14 => {
237                if slice.len() != 8 {
238                    return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsOffset length != 8"));
239                }
240                InterfaceDescriptionOption::IfTsOffset(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
241            },
242            15 => InterfaceDescriptionOption::IfHardware(Cow::Borrowed(std::str::from_utf8(slice)?)),
243
244            2988 | 19372 => InterfaceDescriptionOption::CustomUtf8(CustomUtf8Option::from_slice::<B>(code, slice)?),
245            2989 | 19373 => InterfaceDescriptionOption::CustomBinary(CustomBinaryOption::from_slice::<B>(code, slice)?),
246
247            _ => InterfaceDescriptionOption::Unknown(UnknownOption::new(code, length, slice)),
248        };
249
250        Ok(opt)
251    }
252
253    fn write_to<B: ByteOrder, W: Write>(&self, _state: &PcapNgState, _interface_id: Option<u32>, writer: &mut W) -> Result<usize, PcapError> {
254        Ok(match self {
255            InterfaceDescriptionOption::Comment(a) => a.write_opt_to::<B, W>(1, writer),
256            InterfaceDescriptionOption::IfName(a) => a.write_opt_to::<B, W>(2, writer),
257            InterfaceDescriptionOption::IfDescription(a) => a.write_opt_to::<B, W>(3, writer),
258            InterfaceDescriptionOption::IfIpv4Addr(a) => a.write_opt_to::<B, W>(4, writer),
259            InterfaceDescriptionOption::IfIpv6Addr(a) => a.write_opt_to::<B, W>(5, writer),
260            InterfaceDescriptionOption::IfMacAddr(a) => a.write_opt_to::<B, W>(6, writer),
261            InterfaceDescriptionOption::IfEuIAddr(a) => a.write_opt_to::<B, W>(7, writer),
262            InterfaceDescriptionOption::IfSpeed(a) => a.write_opt_to::<B, W>(8, writer),
263            InterfaceDescriptionOption::IfTsResol(a) => a.write_opt_to::<B, W>(9, writer),
264            InterfaceDescriptionOption::IfTzone(a) => a.write_opt_to::<B, W>(10, writer),
265            InterfaceDescriptionOption::IfFilter(a) => a.write_opt_to::<B, W>(11, writer),
266            InterfaceDescriptionOption::IfOs(a) => a.write_opt_to::<B, W>(12, writer),
267            InterfaceDescriptionOption::IfFcsLen(a) => a.write_opt_to::<B, W>(13, writer),
268            InterfaceDescriptionOption::IfTsOffset(a) => a.write_opt_to::<B, W>(14, writer),
269            InterfaceDescriptionOption::IfHardware(a) => a.write_opt_to::<B, W>(15, writer),
270            InterfaceDescriptionOption::CustomBinary(a) => a.write_opt_to::<B, W>(a.code, writer),
271            InterfaceDescriptionOption::CustomUtf8(a) => a.write_opt_to::<B, W>(a.code, writer),
272            InterfaceDescriptionOption::Unknown(a) => a.write_opt_to::<B, W>(a.code, writer),
273        }?)
274    }
275}
276
277
278/* ----- */
279
280/// Timestamp resolution of an interface.
281#[derive(Copy, Clone, Debug, PartialEq, Eq)]
282pub struct TsResolution(u8);
283
284impl TsResolution {
285    /// Micro-second resolution
286    pub const MICRO: Self = TsResolution(6);
287    /// Milli-second resolution
288    pub const MILLI: Self = TsResolution(3);
289    /// Nano-second resolution
290    pub const NANO: Self = TsResolution(9);
291    /// Second resolution
292    pub const SEC: Self = TsResolution(0);
293
294    /// Creates a new [`TsResolution`] from an [`u8`] if is it in the range [0-9].
295    pub fn new(ts_resol: u8) -> Result<Self, PcapError> {
296        let is_bin = (ts_resol >> 7) & 0x1 == 1;
297        let resol = ts_resol & 0x7F;
298
299        if is_bin && resol > 30 {
300            return Err(PcapError::InvalidTsResolution(ts_resol));
301        }
302
303        if !is_bin && resol > 9 {
304            return Err(PcapError::InvalidTsResolution(ts_resol));
305        }
306
307        Ok(TsResolution(ts_resol))
308    }
309
310    /// Returns the number of nanoseconds coresponding to the [`TsResolution`].
311    pub fn to_nano_secs(&self) -> u32 {
312        static TS_RESOL_BIN_TO_DURATION: Lazy<Vec<u32>> = Lazy::new(|| (0..30).map(|i| 2_u32.pow(30 - i)).collect());
313        static TS_RESOL_DEC_TO_DURATION: Lazy<Vec<u32>> = Lazy::new(|| (0..10).map(|i| 10_u32.pow(9 - i)).collect());
314
315        let is_bin = (self.0 >> 7) & 0x1 == 1;
316        let resol = self.0 & 0x7F;
317
318        if is_bin {
319            TS_RESOL_BIN_TO_DURATION[resol as usize]
320        }
321        else {
322            TS_RESOL_DEC_TO_DURATION[resol as usize]
323        }
324    }
325
326    /// Returns the number of nanoseconds coresponding to the [`TsResolution`] in 10^-ts.
327    pub fn to_raw(&self) -> u8 {
328        self.0
329    }
330}
331
332impl Default for TsResolution {
333    /// Default to micro-seconds resolution
334    fn default() -> Self {
335        Self::MICRO
336    }
337}