1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
use block::*;
use byteorder::{BigEndian, ByteOrder, LittleEndian};
use std::io;
use std::result;
use std::time::SystemTime;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Fail)]
pub enum Error {
    #[fail(display = "Didn't understand magic number {:?}", _0)]
    DidntUnderstandMagicNumber([u8; 4]),
    #[fail(display = "Not enough bytes (expected {}, saw {})", _0, _1)]
    NotEnoughBytes { expected: usize, actual: usize },
    #[fail(display = "Section didn't start with an SHB")]
    DidntStartWithSHB,
    #[fail(display = "IO error: {}", _0)]
    IO(#[cause] io::Error),
}

impl From<io::Error> for Error {
    fn from(x: io::Error) -> Error {
        Error::IO(x)
    }
}

/// A single captured packet.
#[derive(Debug, Clone, PartialEq)]
pub struct Packet<'a> {
    /// The time at which the packet was captured.  The resolution depends on the interface.
    pub timestamp: Option<SystemTime>,
    /// The interface used to capture this packet.
    pub interface: Option<&'a Interface>,
    /// The raw packet data.
    pub data: &'a [u8],
}

/// The type of physical link backing a network interface.
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum LinkType {
    /// No link layer information. A packet saved with this link layer contains a raw L3 packet
    /// preceded by a 32-bit host-byte-order AF_ value indicating the specific L3 type.
    NULL,
    /// D/I/X and 802.3 Ethernet
    ETHERNET,
    /// Experimental Ethernet (3Mb)
    EXP_ETHERNET,
    /// Amateur Radio AX.25
    AX24,
    /// Proteon ProNET Token Ring
    PRONET,
    /// Chaos
    CHAOS,
    /// IEEE 802 Networks
    TOKEN_RING,
    /// ARCNET, with BSD-style header
    ARCNET,
    /// Serial Line IP
    SLIP,
    /// Point-to-point Protocol
    PPP,
    /// FDDI
    FDDI,
    /// PPP in HDLC-like framing
    PPP_HDLC,
    /// NetBSD PPP-over-Ethernet
    PPP_ETHER,
    /// Symantec Enterprise Firewall
    SYMANTEC_FIREWALL,
    /// LLC/SNAP-encapsulated ATM
    ATM_RFC1483,
    /// Raw IP
    RAW,
    /// BSD/OS SLIP BPF header
    SLIP_BSDOS,
    /// BSD/OS PPP BPF header
    PPP_BSDOS,
    /// Cisco HDLC
    C_HDLC,
    /// IEEE 802.11 (wireless)
    IEEE802_11,
    /// Linux Classical IP over ATM
    ATM_CLIP,
    /// Frame Relay
    FRELAY,
    /// OpenBSD loopback
    LOOP,
    /// OpenBSD IPSEC enc
    ENC,
    /// ATM LANE + 802.3 (Reserved for future use)
    LANE8023,
    /// NetBSD HIPPI (Reserved for future use)
    HIPPI,
    /// NetBSD HDLC framing (Reserved for future use)
    HDLC,
    /// Linux cooked socket capture
    LINUX_SLL,
    /// Apple LocalTalk hardware
    LTALK,
    /// Acorn Econet
    ECONET,
    /// Reserved for use with OpenBSD ipfilter
    IPFILTER,
    /// OpenBSD DLT_PFLOG
    PFLOG,
    /// For Cisco-internal use
    CISCO_IOS,
    /// 802.11+Prism II monitor mode
    PRISM_HEADER,
    /// FreeBSD Aironet driver stuff
    AIRONET_HEADER,
    /// Reserved for Siemens HiPath HDLC
    HHDLC,
    /// RFC 2625 IP-over-Fibre Channel
    IP_OVER_FC,
    /// Solaris+SunATM
    SUNATM,
    /// RapidIO - Reserved as per request from Kent Dahlgren <kent@praesum.com> for private use.
    RIO,
    /// PCI Express - Reserved as per request from Kent Dahlgren <kent@praesum.com> for private
    /// use.
    PCI_EXP,
    /// Xilinx Aurora link layer - Reserved as per request from Kent Dahlgren <kent@praesum.com>
    /// for private use.
    AURORA,
    /// 802.11 plus BSD radio header
    IEEE802_11_RADIO,
    /// Tazmen Sniffer Protocol - Reserved for the TZSP encapsulation, as per request from Chris
    /// Waters <chris.waters@networkchemistry.com> TZSP is a generic encapsulation for any other
    /// link type, which includes a means to include meta-information with the packet, e.g. signal
    /// strength and channel for 802.11 packets.
    TZSP,
    /// Linux-style headers
    ARCNET_LINUX,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_MLPPP,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_MLFR,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_ES,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_GGSN,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_MFR,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_ATM2,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_SERVICES,
    /// Juniper-private data link type, as per request from Hannes Gredler <hannes@juniper.net>.
    /// The corresponding DLT_s are used for passing on chassis-internal metainformation such as
    /// QOS profiles, etc..
    JUNIPER_ATM1,
    /// Apple IP-over-IEEE 1394 cooked header
    APPLE_IP_OVER_IEEE1394,
    /// ???
    MTP2_WITH_PHDR,
    /// ???
    MTP2,
    /// ???
    MTP3,
    /// ???
    SCCP,
    /// DOCSIS MAC frames
    DOCSIS,
    /// Linux-IrDA
    LINUX_IRDA,
    /// Reserved for IBM SP switch and IBM Next Federation switch.
    IBM_SP,
    /// Reserved for IBM SP switch and IBM Next Federation switch.
    IBM_SN,
    /// A link type we didn't recognise.
    Unknown(u16),
}

impl LinkType {
    /// Decode LinkType from u16
    pub fn from_u16(i: u16) -> LinkType {
        match i {
            0 => LinkType::NULL,
            1 => LinkType::ETHERNET,
            2 => LinkType::EXP_ETHERNET,
            3 => LinkType::AX24,
            4 => LinkType::PRONET,
            5 => LinkType::CHAOS,
            6 => LinkType::TOKEN_RING,
            7 => LinkType::ARCNET,
            8 => LinkType::SLIP,
            9 => LinkType::PPP,
            10 => LinkType::FDDI,
            50 => LinkType::PPP_HDLC,
            51 => LinkType::PPP_ETHER,
            99 => LinkType::SYMANTEC_FIREWALL,
            100 => LinkType::ATM_RFC1483,
            101 => LinkType::RAW,
            102 => LinkType::SLIP_BSDOS,
            103 => LinkType::PPP_BSDOS,
            104 => LinkType::C_HDLC,
            105 => LinkType::IEEE802_11,
            106 => LinkType::ATM_CLIP,
            107 => LinkType::FRELAY,
            108 => LinkType::LOOP,
            109 => LinkType::ENC,
            110 => LinkType::LANE8023,
            111 => LinkType::HIPPI,
            112 => LinkType::HDLC,
            113 => LinkType::LINUX_SLL,
            114 => LinkType::LTALK,
            115 => LinkType::ECONET,
            116 => LinkType::IPFILTER,
            117 => LinkType::PFLOG,
            118 => LinkType::CISCO_IOS,
            119 => LinkType::PRISM_HEADER,
            120 => LinkType::AIRONET_HEADER,
            121 => LinkType::HHDLC,
            122 => LinkType::IP_OVER_FC,
            123 => LinkType::SUNATM,
            124 => LinkType::RIO,
            125 => LinkType::PCI_EXP,
            126 => LinkType::AURORA,
            127 => LinkType::IEEE802_11_RADIO,
            128 => LinkType::TZSP,
            129 => LinkType::ARCNET_LINUX,
            130 => LinkType::JUNIPER_MLPPP,
            131 => LinkType::JUNIPER_MLFR,
            132 => LinkType::JUNIPER_ES,
            133 => LinkType::JUNIPER_GGSN,
            134 => LinkType::JUNIPER_MFR,
            135 => LinkType::JUNIPER_ATM2,
            136 => LinkType::JUNIPER_SERVICES,
            137 => LinkType::JUNIPER_ATM1,
            138 => LinkType::APPLE_IP_OVER_IEEE1394,
            139 => LinkType::MTP2_WITH_PHDR,
            140 => LinkType::MTP2,
            141 => LinkType::MTP3,
            142 => LinkType::SCCP,
            143 => LinkType::DOCSIS,
            144 => LinkType::LINUX_IRDA,
            145 => LinkType::IBM_SP,
            146 => LinkType::IBM_SN,
            // LINKTYPE_RAW is defined as 101 in the registry but for some reason libpcap uses DLT_RAW
            // defined as 14 on OpenBSD and as 12 for other platforms for the link type. So in order to
            // reliably decode link types we need to remap those numbers as LinkType::RAW here.
            12 => LinkType::RAW,
            14 => LinkType::RAW,
            x => LinkType::Unknown(x),
        }
    }
}

#[derive(Clone, PartialEq, Debug, Copy)]
pub enum Endianness {
    Big,
    Little,
}

impl Endianness {
    pub fn parse_from_magic(buf: &[u8]) -> Result<Self> {
        let magic = &buf[0..4];
        match magic {
            [0x1A, 0x2B, 0x3C, 0x4D] => Ok(Endianness::Big),
            [0x4D, 0x3C, 0x2B, 0x1A] => Ok(Endianness::Little),
            _ => {
                let mut unknown_magic = [0; 4];
                unknown_magic.copy_from_slice(magic);
                Err(Error::DidntUnderstandMagicNumber(unknown_magic))
            }
        }
    }
}

pub trait KnownByteOrder {
    fn endianness() -> Endianness;
}

impl KnownByteOrder for BigEndian {
    fn endianness() -> Endianness {
        Endianness::Big
    }
}

impl KnownByteOrder for LittleEndian {
    fn endianness() -> Endianness {
        Endianness::Little
    }
}

#[derive(Clone, PartialEq, Debug, Copy)]
pub struct InterfaceId(pub u32);

pub trait FromBytes<'a>: Sized {
    fn parse<B: ByteOrder + KnownByteOrder>(buf: &'a [u8]) -> Self;
}

pub fn require_bytes(buf: &[u8], len: usize) -> Result<()> {
    if buf.len() < len {
        Err(Error::NotEnoughBytes {
            expected: len,
            actual: buf.len(),
        })
    } else {
        Ok(())
    }
}

/// A network interface.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Interface {
    pub link_type: LinkType,
    /// The if_tsresol option identifies the resolution of timestamps. If the Most Significant Bit
    /// is equal to zero, the remaining bits indicates the resolution of the timestamp as a negative
    /// power of 10 (e.g. 6 means microsecond resolution, timestamps are the number of microseconds
    /// since 1/1/1970). If the Most Significant Bit is equal to one, the remaining bits indicates
    /// the resolution as as negative power of 2 (e.g. 10 means 1/1024 of second). If this option is
    /// not present, a resolution of 10^-6 is assumed (i.e. timestamps have the same resolution of
    /// the standard 'libpcap' timestamps).
    pub(crate) units_per_sec: u32,
}

impl Interface {
    pub(crate) fn from_desc<B: ByteOrder>(desc: &InterfaceDescription) -> Interface {
        let mut units_per_sec = 1_000_000;
        let mut i = 0;
        loop {
            if desc.options.len() < i + 4 {
                // no further options
                break;
            }
            let option_type = B::read_u16(&desc.options[i..i + 2]);
            i += 2;
            let option_len = B::read_u16(&desc.options[i..i + 2]) as usize;
            i += 2;
            match option_type {
                0 => {
                    // end of options
                    assert!(i == desc.options.len());
                    break;
                }
                9 => {
                    // if_tsresol
                    assert!(
                        option_len == 1,
                        "option_len for if_tsresol should be 1 but got {}",
                        option_len
                    );
                    let v = desc.options[i];
                    let exp = u32::from(v & 0b0111_1111);
                    match v >> 7 {
                        0 => units_per_sec = 10_u32.pow(exp),
                        1 => units_per_sec = 2_u32.pow(exp),
                        _ => { /* impossible */ }
                    }
                }
                _ => { /* skip other option types */ }
            }
            let padding_len = (4 - option_len % 4) % 4;
            i += option_len + padding_len;
        }
        Interface {
            link_type: desc.link_type,
            units_per_sec,
        }
    }
}