pcap_parser/data/
mod.rs

1//! Helper functions to access block contents (depending in linktype)
2//!
3//! ## Example
4//!
5//! ```rust
6//! use pcap_parser::data::{get_packetdata, PacketData};
7//! use pcap_parser::pcapng::EnhancedPacketBlock;
8//! use pcap_parser::Linktype;
9//!
10//! fn parse_block_content<'a>(
11//!     epb: &'a EnhancedPacketBlock<'_>,
12//!     linktype: Linktype
13//! ) -> Option<()> {
14//!     let packet_data =  get_packetdata(epb.data, linktype, epb.caplen as usize)?;
15//!     match packet_data {
16//!         PacketData::L3(_, _data) => {
17//!             // ...
18//!         },
19//!         _ => println!("Unsupported link type"),
20//!     }
21//!     Some(())
22//! }
23//! ```
24
25mod exported_pdu;
26mod pcap_nflog;
27
28pub use crate::data::exported_pdu::*;
29pub use crate::data::pcap_nflog::*;
30use crate::linktype::Linktype;
31use crate::read_u32_e;
32use nom::number::complete::be_u32;
33use nom::number::streaming::{be_u16, be_u64, be_u8};
34use nom::IResult;
35
36pub const ETHERTYPE_IPV4: u16 = 0x0800;
37pub const ETHERTYPE_IPV6: u16 = 0x86dd;
38
39/// Contents of a pcap/pcap-ng block. This can be network data, USB, etc.
40#[derive(Clone, Debug)]
41pub enum PacketData<'a> {
42    L2(&'a [u8]),
43    L3(u16, &'a [u8]),
44    L4(u8, &'a [u8]),
45
46    Unsupported(&'a [u8]),
47}
48
49/// Get packet data for LINKTYPE_NULL (0)
50///
51/// BSD loopback encapsulation; the link layer header is a 4-byte field, in host byte order,
52/// containing a value of 2 for IPv4 packets, a value of either 24, 28, or 30 for IPv6 packets, a
53/// value of 7 for OSI packets, or a value of 23 for IPX packets. All of the IPv6 values correspond
54/// to IPv6 packets; code reading files should check for all of them.
55///
56/// Note that ``host byte order'' is the byte order of the machine on which the packets are
57/// captured; if a live capture is being done, ``host byte order'' is the byte order of the machine
58/// capturing the packets, but if a ``savefile'' is being read, the byte order is not necessarily
59/// that of the machine reading the capture file.
60pub fn get_packetdata_null(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
61    // debug!("data.len: {}, caplen: {}", packet.data.len(), packet.header.caplen);
62    if i.len() < caplen || caplen < 4 {
63        None
64    } else {
65        let vers = read_u32_e!(i, false);
66        let ethertype = match vers {
67            2 => ETHERTYPE_IPV4,
68            24 | 28 | 30 => ETHERTYPE_IPV6,
69            _ => 0,
70        };
71        Some(PacketData::L3(ethertype, &i[4..caplen]))
72    }
73}
74
75/// Get packet data for LINKTYPE_ETHERNET (1)
76///
77/// IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up); the 10MB in the DLT_ name is historical.
78pub fn get_packetdata_ethernet(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
79    if i.len() < caplen || caplen == 0 {
80        None
81    } else {
82        Some(PacketData::L2(&i[..caplen]))
83    }
84}
85
86/// Get packet data for LINKTYPE_RAW (101)
87///
88/// Raw IP; the packet begins with an IPv4 or IPv6 header, with the "version" field of the header
89/// indicating whether it's an IPv4 or IPv6 header.
90pub fn get_packetdata_raw(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
91    if i.len() < caplen || caplen == 0 {
92        None
93    } else {
94        let vers = i[0] >> 4;
95        let ethertype = match vers {
96            4 => ETHERTYPE_IPV4,
97            6 => ETHERTYPE_IPV6,
98            _ => 0,
99        };
100        Some(PacketData::L3(ethertype, &i[..caplen]))
101    }
102}
103
104/// Get packet data for LINKTYPE_LINUX_SLL (113)
105///
106/// See <http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html>
107pub fn get_packetdata_linux_sll(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
108    if i.len() < caplen || caplen == 0 {
109        None
110    } else {
111        match parse_sll_header(i) {
112            Err(_) => None,
113            Ok((rem, sll)) => {
114                match sll.arphrd_type {
115                    778 /* ARPHRD_IPGRE */ => Some(PacketData::L4(47, rem)),
116                    803 /* ARPHRD_IEEE80211_RADIOTAP */ |
117                    824 /* ARPHRD_NETLINK */ => None,
118                    _ => Some(PacketData::L3(sll.proto, rem)),
119                }
120            }
121        }
122    }
123}
124
125struct SLLHeader {
126    _packet_type: u16,
127    arphrd_type: u16,
128    _ll_addr_len: u16,
129    _ll_addr: u64,
130    proto: u16,
131}
132
133fn parse_sll_header(i: &[u8]) -> IResult<&[u8], SLLHeader> {
134    let (i, _packet_type) = be_u16(i)?;
135    let (i, arphrd_type) = be_u16(i)?;
136    let (i, _ll_addr_len) = be_u16(i)?;
137    let (i, _ll_addr) = be_u64(i)?;
138    let (i, proto) = be_u16(i)?;
139    let header = SLLHeader {
140        _packet_type,
141        arphrd_type,
142        _ll_addr_len,
143        _ll_addr,
144        proto,
145    };
146    Ok((i, header))
147}
148
149/// Get packet data for LINKTYPE_LINUX_SLL2 (276)
150///
151/// See <https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html>
152pub fn get_packetdata_linux_sll2(i: &[u8], caplen: usize) -> Option<PacketData<'_>> {
153    if i.len() < caplen || caplen == 0 {
154        None
155    } else {
156        match parse_sll2_header(i) {
157            Err(_) => None,
158            Ok((rem, sll)) => {
159                match sll.arphrd_type {
160                    778 /* ARPHRD_IPGRE */ => Some(PacketData::L4(47, rem)),
161                    803 /* ARPHRD_IEEE80211_RADIOTAP */ |
162                    824 /* ARPHRD_NETLINK */ => None,
163                    _ => Some(PacketData::L3(sll.protocol_type, rem)),
164                }
165            }
166        }
167    }
168}
169
170struct SLL2Header {
171    protocol_type: u16,
172    _reserved: u16,
173    _interface_index: u32,
174    arphrd_type: u16,
175    _packet_type: u8,
176    _ll_addr_len: u8,
177    _ll_addr: u64,
178}
179
180fn parse_sll2_header(i: &[u8]) -> IResult<&[u8], SLL2Header> {
181    let (i, protocol_type) = be_u16(i)?;
182    let (i, _reserved) = be_u16(i)?;
183    let (i, _interface_index) = be_u32(i)?;
184    let (i, arphrd_type) = be_u16(i)?;
185    let (i, _packet_type) = be_u8(i)?;
186    let (i, _ll_addr_len) = be_u8(i)?;
187    let (i, _ll_addr) = be_u64(i)?;
188    let header = SLL2Header {
189        protocol_type,
190        _reserved,
191        _interface_index,
192        arphrd_type,
193        _packet_type,
194        _ll_addr_len,
195        _ll_addr,
196    };
197    Ok((i, header))
198}
199
200/// Get packet data for LINKTYPE_IPV4 (228)
201///
202/// Raw IPv4; the packet begins with an IPv4 header.
203pub fn get_packetdata_ipv4(i: &[u8], _caplen: usize) -> Option<PacketData<'_>> {
204    Some(PacketData::L3(ETHERTYPE_IPV4, i))
205}
206
207/// Get packet data for LINKTYPE_IPV6 (229)
208///
209/// Raw IPv4; the packet begins with an IPv6 header.
210pub fn get_packetdata_ipv6(i: &[u8], _caplen: usize) -> Option<PacketData<'_>> {
211    Some(PacketData::L3(ETHERTYPE_IPV6, i))
212}
213
214/// Get packet data, depending on linktype.
215///
216/// Returns packet data, or None if data could not be extracted (for ex, inner parsing error).
217/// If linktype is not supported, `PacketData::Unsupported` is used.
218pub fn get_packetdata(i: &[u8], linktype: Linktype, caplen: usize) -> Option<PacketData<'_>> {
219    match linktype {
220        Linktype::NULL => get_packetdata_null(i, caplen),
221        Linktype::ETHERNET => get_packetdata_ethernet(i, caplen),
222        Linktype::RAW => get_packetdata_raw(i, caplen),
223        Linktype::LINUX_SLL => get_packetdata_linux_sll(i, caplen),
224        Linktype::LINUX_SLL2 => get_packetdata_linux_sll2(i, caplen),
225        Linktype::IPV4 => get_packetdata_ipv4(i, caplen),
226        Linktype::IPV6 => get_packetdata_ipv6(i, caplen),
227        Linktype::NFLOG => get_packetdata_nflog(i, caplen),
228        Linktype::WIRESHARK_UPPER_PDU => get_packetdata_wireshark_upper_pdu(i, caplen),
229        _ => Some(PacketData::Unsupported(i)),
230    }
231}