1#![allow(clippy::cast_lossless)]
2
3use 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#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
25pub struct InterfaceDescriptionBlock<'a> {
26 pub linktype: DataLink,
31
32 pub snaplen: u32,
37
38 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 pub fn new(linktype: DataLink, snaplen: u32) -> Self {
80 Self { linktype, snaplen, options: vec![] }
81 }
82
83 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 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#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
115pub enum InterfaceDescriptionOption<'a> {
116 Comment(Cow<'a, str>),
119
120 IfName(Cow<'a, str>),
122
123 IfDescription(Cow<'a, str>),
125
126 IfIpv4Addr(Cow<'a, [u8]>),
128
129 IfIpv6Addr(Cow<'a, [u8]>),
131
132 IfMacAddr(Cow<'a, [u8]>),
134
135 IfEuIAddr(u64),
137
138 IfSpeed(u64),
140
141 IfTsResol(u8),
143
144 IfTzone(u32),
146
147 IfFilter(Cow<'a, [u8]>),
149
150 IfOs(Cow<'a, str>),
153
154 IfFcsLen(u8),
157
158 IfTsOffset(u64),
161
162 IfHardware(Cow<'a, str>),
164
165 CustomBinary(CustomBinaryOption<'a>),
167
168 CustomUtf8(CustomUtf8Option<'a>),
170
171 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
282pub struct TsResolution(u8);
283
284impl TsResolution {
285 pub const MICRO: Self = TsResolution(6);
287 pub const MILLI: Self = TsResolution(3);
289 pub const NANO: Self = TsResolution(9);
291 pub const SEC: Self = TsResolution(0);
293
294 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 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 pub fn to_raw(&self) -> u8 {
328 self.0
329 }
330}
331
332impl Default for TsResolution {
333 fn default() -> Self {
335 Self::MICRO
336 }
337}