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
//! Helper functions to access block contents (depending in linktype)
//!
//! ## Example
//!
//! ```rust
//! use pcap_parser::data::{get_packetdata, PacketData};
//! use pcap_parser::pcapng::EnhancedPacketBlock;
//! use pcap_parser::Linktype;
//!
//! fn parse_block_content<'a>(
//! epb: &'a EnhancedPacketBlock<'_>,
//! linktype: Linktype
//! ) -> Option<()> {
//! let packet_data = get_packetdata(epb.data, linktype, epb.caplen as usize)?;
//! match packet_data {
//! PacketData::L3(_, _data) => {
//! // ...
//! },
//! _ => println!("Unsupported link type"),
//! }
//! Some(())
//! }
//! ```
mod exported_pdu;
mod pcap_nflog;
pub use crate::data::exported_pdu::*;
pub use crate::data::pcap_nflog::*;
use crate::linktype::Linktype;
use crate::read_u32_e;
use nom::number::streaming::{be_u16, be_u64};
use nom::IResult;
pub const ETHERTYPE_IPV4: u16 = 0x0800;
pub const ETHERTYPE_IPV6: u16 = 0x86dd;
/// Contents of a pcap/pcap-ng block. This can be network data, USB, etc.
#[derive(Clone, Debug)]
pub enum PacketData<'a> {
L2(&'a [u8]),
L3(u16, &'a [u8]),
L4(u8, &'a [u8]),
Unsupported(&'a [u8]),
}
/// Get packet data for LINKTYPE_NULL (0)
///
/// BSD loopback encapsulation; the link layer header is a 4-byte field, in host byte order,
/// containing a value of 2 for IPv4 packets, a value of either 24, 28, or 30 for IPv6 packets, a
/// value of 7 for OSI packets, or a value of 23 for IPX packets. All of the IPv6 values correspond
/// to IPv6 packets; code reading files should check for all of them.
///
/// Note that ``host byte order'' is the byte order of the machine on which the packets are
/// captured; if a live capture is being done, ``host byte order'' is the byte order of the machine
/// capturing the packets, but if a ``savefile'' is being read, the byte order is not necessarily
/// that of the machine reading the capture file.
pub fn get_packetdata_null(i: &[u8], caplen: usize) -> Option<PacketData> {
// debug!("data.len: {}, caplen: {}", packet.data.len(), packet.header.caplen);
if i.len() < caplen || caplen < 4 {
None
} else {
let vers = read_u32_e!(i, false);
let ethertype = match vers {
2 => ETHERTYPE_IPV4,
24 | 28 | 30 => ETHERTYPE_IPV6,
_ => 0,
};
Some(PacketData::L3(ethertype, &i[4..caplen]))
}
}
/// Get packet data for LINKTYPE_ETHERNET (1)
///
/// IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up); the 10MB in the DLT_ name is historical.
pub fn get_packetdata_ethernet(i: &[u8], caplen: usize) -> Option<PacketData> {
if i.len() < caplen || caplen == 0 {
None
} else {
Some(PacketData::L2(&i[..caplen]))
}
}
/// Get packet data for LINKTYPE_RAW (101)
///
/// Raw IP; the packet begins with an IPv4 or IPv6 header, with the "version" field of the header
/// indicating whether it's an IPv4 or IPv6 header.
pub fn get_packetdata_raw(i: &[u8], caplen: usize) -> Option<PacketData> {
if i.len() < caplen || caplen == 0 {
None
} else {
let vers = i[0] >> 4;
let ethertype = match vers {
4 => ETHERTYPE_IPV4,
6 => ETHERTYPE_IPV6,
_ => 0,
};
Some(PacketData::L3(ethertype, &i[..caplen]))
}
}
/// Get packet data for LINKTYPE_LINUX_SLL (113)
///
/// See <http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html>
pub fn get_packetdata_linux_sll(i: &[u8], caplen: usize) -> Option<PacketData> {
if i.len() < caplen || caplen == 0 {
None
} else {
match parse_sll_header(i) {
Err(_) => None,
Ok((rem, sll)) => {
match sll.arphrd_type {
778 /* ARPHRD_IPGRE */ => Some(PacketData::L4(47, rem)),
803 /* ARPHRD_IEEE80211_RADIOTAP */ |
824 /* ARPHRD_NETLINK */ => None,
_ => Some(PacketData::L3(sll.proto, rem)),
}
}
}
}
}
struct SLLHeader {
_packet_type: u16,
arphrd_type: u16,
_ll_addr_len: u16,
_ll_addr: u64,
proto: u16,
}
fn parse_sll_header(i: &[u8]) -> IResult<&[u8], SLLHeader> {
let (i, _packet_type) = be_u16(i)?;
let (i, arphrd_type) = be_u16(i)?;
let (i, _ll_addr_len) = be_u16(i)?;
let (i, _ll_addr) = be_u64(i)?;
let (i, proto) = be_u16(i)?;
let header = SLLHeader {
_packet_type,
arphrd_type,
_ll_addr_len,
_ll_addr,
proto,
};
Ok((i, header))
}
/// Get packet data for LINKTYPE_IPV4 (228)
///
/// Raw IPv4; the packet begins with an IPv4 header.
pub fn get_packetdata_ipv4(i: &[u8], _caplen: usize) -> Option<PacketData> {
Some(PacketData::L3(ETHERTYPE_IPV4, i))
}
/// Get packet data for LINKTYPE_IPV6 (229)
///
/// Raw IPv4; the packet begins with an IPv6 header.
pub fn get_packetdata_ipv6(i: &[u8], _caplen: usize) -> Option<PacketData> {
Some(PacketData::L3(ETHERTYPE_IPV6, i))
}
/// Get packet data, depending on linktype.
///
/// Returns packet data, or None if data could not be extracted (for ex, inner parsing error).
/// If linktype is not supported, `PacketData::Unsupported` is used.
pub fn get_packetdata(i: &[u8], linktype: Linktype, caplen: usize) -> Option<PacketData> {
match linktype {
Linktype::NULL => get_packetdata_null(i, caplen),
Linktype::ETHERNET => get_packetdata_ethernet(i, caplen),
Linktype::RAW => get_packetdata_raw(i, caplen),
Linktype::LINUX_SLL => get_packetdata_linux_sll(i, caplen),
Linktype::IPV4 => get_packetdata_ipv4(i, caplen),
Linktype::IPV6 => get_packetdata_ipv6(i, caplen),
Linktype::NFLOG => get_packetdata_nflog(i, caplen),
Linktype::WIRESHARK_UPPER_PDU => get_packetdata_wireshark_upper_pdu(i, caplen),
_ => Some(PacketData::Unsupported(i)),
}
}