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