Skip to main content

c_its_parser/pcap/
mod.rs

1use alloc::string::ToString;
2
3/// Strips Radiotap, IEEE 802.11p and LLC headers from a binary message buffer
4///
5/// # Errors
6/// Returns a human-readable error when parsing failed
7pub fn remove_pcap_headers(data: &[u8]) -> Result<&[u8], alloc::string::String> {
8    remove_radiotap_hdr(data)
9        .and_then(remove_80211_hdr)
10        .and_then(remove_llc_hdr)
11}
12
13#[allow(clippy::missing_errors_doc, reason = "no documentation present")]
14fn remove_radiotap_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
15    /*
16     * Radiotap Header has the following format
17     * - u_int8_t   header version (currently always zero)
18     * - u_int8_t   (padding for byte alignment);
19     * - u_int16_t  entire header length;
20     * - u_int32_t  bitmask which subsequent data is present
21     */
22
23    let radiotap_version: u8 = data[0];
24    if radiotap_version != 0 {
25        return Err(alloc::format!(
26            "Unknown header version {radiotap_version:#x}"
27        ));
28    }
29
30    let hdr_len: usize = u16::from_le_bytes([data[2], data[3]]).into();
31    let (_, remaining) = data.split_at(hdr_len);
32
33    Ok(remaining)
34}
35
36#[allow(clippy::missing_errors_doc, reason = "no documentation present")]
37fn remove_80211_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
38    /*
39     * IEEE 802.11 Header has the following format (26-32 bytes)
40     * - 2 bytes frame control
41     *      byte 0: .... ..00: Version 0
42     *      byte 0: .... 10..: Type data frame
43     *      byte 0: 1000 ....: QoS Data
44     * - 2 bytes duration
45     * - 6 bytes addr 1 (receiver)
46     * - 6 bytes addr 2 (transmitter)
47     * - 6 bytes addr 3 (BSSID)
48     * - 2 bytes sequence control
49     * - 0 or 6 bytes addr 4
50     * - 0 or 2 bytes QOS control
51     * - 0 or 4 bytes HT control
52     */
53
54    let ieee80211_framecontrol: u8 = data[0];
55
56    let ieee80211_fc_version: u8 = ieee80211_framecontrol & 0x03; // 0000.00xx
57    if ieee80211_fc_version != 0 {
58        return Err(
59            alloc::format!("Unknown 802.11 header version {ieee80211_fc_version}").to_string(),
60        );
61    }
62
63    let ieee80211_fc_type: u8 = (ieee80211_framecontrol & 0x0c) >> 2; // 0000.xx00
64    if ieee80211_fc_type != 0b10 {
65        // only select data frames
66        return Err(
67            alloc::format!("Unsupported 802.11 frame type {ieee80211_fc_type}").to_string(),
68        );
69    }
70
71    let ieee80211_fc_subtype: u8 = (ieee80211_framecontrol & 0xf0) >> 4; // xxxx.0000
72    if ieee80211_fc_subtype != 0b1000 {
73        // only select QoS frames
74        return Err(
75            alloc::format!("Unsupported 802.11 frame subtype {ieee80211_fc_type:#04x}").to_string(),
76        );
77    }
78
79    let hdr_len: usize = 26; // QoS data frame is usually 26 bytes (sequence control, no addr 4, QoS, no HT)
80    let (_, remaining) = data.split_at(hdr_len);
81
82    Ok(remaining)
83}
84
85#[allow(clippy::missing_errors_doc, reason = "no documentation present")]
86fn remove_llc_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
87    /*
88     * LLC Header has the following format (8 bytes)
89     * - 1 bytes DSAP
90     * - 1 bytes SSAP
91     * - 1 bytes control field
92     * - 3 bytes organization code
93     * - 2 bytes Type (0x8947 BE is GeoNetworking)
94     */
95
96    let llc_type: u16 = (u16::from(data[6]) << 8) | u16::from(data[7]);
97
98    if llc_type != 0x8947 {
99        return Err(alloc::format!("Unknown LLC payload type {llc_type:#x}").to_string());
100    }
101
102    let hdr_len: usize = 8; // TODO: Is this the right size?
103    let (_, remaining) = data.split_at(hdr_len);
104
105    Ok(remaining)
106}