Skip to main content

pcap_parser/pcapng/
time.rs

1use std::convert::TryFrom;
2
3use super::{OptionCode, PcapNGOption};
4
5/// Compute the timestamp resolution, in units per second
6///
7/// Return the resolution, or `None` if the resolution is invalid (for ex. greater than `2^64`)
8pub fn build_ts_resolution(ts_resol: u8) -> Option<u64> {
9    let ts_mode = ts_resol & 0x80;
10    let unit = if ts_mode == 0 {
11        // 10^if_tsresol
12        // check that if_tsresol <= 19 (10^19 is the largest power of 10 to fit in a u64)
13        if ts_resol > 19 {
14            return None;
15        }
16        10u64.pow(ts_resol as u32)
17    } else {
18        // 2^if_tsresol
19        // check that if_tsresol <= 63
20        if ts_resol > 63 {
21            return None;
22        }
23        1 << ((ts_resol & 0x7f) as u64)
24    };
25    Some(unit)
26}
27
28/// Given the timestamp parameters, return the timestamp seconds and fractional part (in resolution
29/// units)
30pub fn build_ts(ts_high: u32, ts_low: u32, ts_offset: u64, resolution: u64) -> (u32, u32) {
31    let if_tsoffset = ts_offset;
32    let ts: u64 = ((ts_high as u64) << 32) | (ts_low as u64);
33    let ts_sec = (if_tsoffset + (ts / resolution)) as u32;
34    let ts_fractional = (ts % resolution) as u32;
35    (ts_sec, ts_fractional)
36}
37
38/// Given the timestamp parameters, return the timestamp as a `f64` value.
39///
40/// The resolution is given in units per second. In pcap-ng files, it is stored in the
41/// Interface Description Block, and can be obtained using [`crate::InterfaceDescriptionBlock::ts_resolution`]
42pub fn build_ts_f64(ts_high: u32, ts_low: u32, ts_offset: u64, resolution: u64) -> f64 {
43    let ts: u64 = ((ts_high as u64) << 32) | (ts_low as u64);
44    let ts_sec = (ts_offset + (ts / resolution)) as u32;
45    let ts_fractional = (ts % resolution) as u32;
46    // XXX should we round to closest unit?
47    ts_sec as f64 + ((ts_fractional as f64) / (resolution as f64))
48}
49
50pub(crate) fn if_extract_tsoffset_and_tsresol(options: &[PcapNGOption]) -> (u8, i64) {
51    let mut if_tsresol: u8 = 6;
52    let mut if_tsoffset: i64 = 0;
53    for opt in options {
54        match opt.code {
55            OptionCode::IfTsresol => {
56                if !opt.value.is_empty() {
57                    if_tsresol = opt.value[0];
58                }
59            }
60            OptionCode::IfTsoffset => {
61                if opt.value.len() >= 8 {
62                    let int_bytes =
63                        <[u8; 8]>::try_from(&opt.value[..8]).expect("Convert bytes to i64");
64                    if_tsoffset = i64::from_le_bytes(int_bytes);
65                }
66            }
67            _ => (),
68        }
69    }
70    (if_tsresol, if_tsoffset)
71}
72
73#[cfg(test)]
74mod tests {
75    use hex_literal::hex;
76
77    use super::*;
78
79    #[test]
80    fn decode_ts() {
81        // from https://datatracker.ietf.org/doc/html/draft-ietf-opsawg-pcapng section 4.6 (ISB)
82        // '97 c3 04 00 aa 47 ca 64', in Little Endian, decodes to 2012-06-29 07:28:25.298858 UTC.
83
84        const INPUT_HIGH: [u8; 4] = hex!("97 c3 04 00");
85        const INPUT_LOW: [u8; 4] = hex!("aa 47 ca 64");
86        let ts_high = u32::from_le_bytes(INPUT_HIGH);
87        let ts_low = u32::from_le_bytes(INPUT_LOW);
88        let ts_offset = 0;
89        let resolution = build_ts_resolution(6).unwrap();
90        // eprintln!("{ts_high:x?}");
91
92        let (ts_sec, ts_usec) = build_ts(ts_high, ts_low, ts_offset, resolution);
93        eprintln!("{ts_sec}:{ts_usec}");
94
95        const EXPECTED_TS_SEC: u32 = 1340954905;
96        const EXPECTED_TS_USEC: u32 = 298858;
97        // // to obtain the above values value, add "chrono" to dev-dependencies and uncomment:
98        // use chrono::DateTime;
99        // let dt = DateTime::from_timestamp(ts_sec as i64, ts_usec * 1000).unwrap();
100        // assert_eq!(dt.to_string(), "2012-06-29 07:28:25.298858 UTC");
101        assert_eq!(ts_sec, EXPECTED_TS_SEC);
102        assert_eq!(ts_usec, EXPECTED_TS_USEC);
103    }
104}