c-its-parser 2.4.1

Tools for encoding and decoding ETSI messages (GN + Transport + CAM/DENM/IVIM/SSEM/SREM/MAPEM/SPATEM)
Documentation
use alloc::string::ToString;

/// Strips Radiotap, IEEE 802.11p and LLC headers from a binary message buffer
///
/// Convenience function combining [`remove_radiotap_hdr`], [`remove_80211_hdr`] and [`remove_llc_hdr`].
///
/// # Errors
/// Returns a human-readable error when parsing failed
pub fn remove_pcap_headers(data: &[u8]) -> Result<&[u8], alloc::string::String> {
    remove_radiotap_hdr(data).and_then(remove_wlan_headers)
}

/// Strips IEEE 802.11p and LLC headers from a binary message buffer
///
/// Convenience function combining [`remove_80211_hdr`] and [`remove_llc_hdr`].
///
/// IEEE 802.11 data frames always have an LLC header.
/// Since `remove_80211_hdr` only allows data frames, we usually need to remove them, too.
///
/// # Errors
/// Returns a human-readable error when parsing failed
pub fn remove_wlan_headers(data: &[u8]) -> Result<&[u8], alloc::string::String> {
    remove_80211_hdr(data).and_then(remove_llc_hdr)
}

/// Strips Radiotap header from a binary message buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn remove_radiotap_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
    extract_radiotap_hdr(data).map(|(_, remaining)| remaining)
}

/// Extracts Radiotap header from a binary message buffer and returns the header and remaining buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn extract_radiotap_hdr(data: &[u8]) -> Result<(&[u8], &[u8]), alloc::string::String> {
    /*
     * Radiotap Header has the following format
     * - u_int8_t   header version (currently always zero)
     * - u_int8_t   (padding for byte alignment);
     * - u_int16_t  entire header length;
     * - u_int32_t  bitmask which subsequent data is present
     */

    let radiotap_version: u8 = data[0];
    if radiotap_version != 0 {
        return Err(alloc::format!(
            "Unknown header version {radiotap_version:#x}"
        ));
    }

    let hdr_len: usize = u16::from_le_bytes([data[2], data[3]]).into();
    Ok(data.split_at(hdr_len))
}

/// Strips IEEE 802.11p header from a binary message buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn remove_80211_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
    extract_80211_hdr(data).map(|(_, remaining)| remaining)
}

/// Extracts IEEE 802.11p header from a binary message buffer and returns the header and remaining buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn extract_80211_hdr(data: &[u8]) -> Result<(&[u8], &[u8]), alloc::string::String> {
    /*
     * IEEE 802.11 Header has the following format (24-26 bytes)
     * - 2 bytes frame control
     *      byte 0: .... ..00: Version 0
     *      byte 0: .... 10..: Type data frame
     *      byte 0: 1000 ....: QoS Data (or 0 for non-QoS data)
     * - 2 bytes duration
     * - 6 bytes addr 1 (receiver)
     * - 6 bytes addr 2 (transmitter)
     * - 6 bytes addr 3 (BSSID)
     * - 2 bytes sequence control
     * - addr 4 not used in 802.11p mode
     * - 0 or 2 bytes QOS control
     * - HT control not used in 802.11p mode
     */

    let ieee80211_framecontrol: u8 = data[0];

    let ieee80211_fc_version: u8 = ieee80211_framecontrol & 0x03; // 0000.00xx
    if ieee80211_fc_version != 0 {
        return Err(
            alloc::format!("Unknown 802.11 header version {ieee80211_fc_version}").to_string(),
        );
    }

    let ieee80211_fc_type: u8 = (ieee80211_framecontrol & 0x0c) >> 2; // 0000.xx00
    if ieee80211_fc_type != 0b10 {
        // only select data frames
        return Err(
            alloc::format!("Unsupported 802.11 frame type {ieee80211_fc_type}").to_string(),
        );
    }

    let ieee80211_fc_subtype: u8 = (ieee80211_framecontrol & 0xf0) >> 4; // xxxx.0000
    let qos_hdr_bytes = if ieee80211_fc_subtype == 0b1000 {
        2
    } else if ieee80211_fc_subtype == 0b0000 {
        0
    } else {
        // only select QoS or "normal" data frames
        return Err(
            alloc::format!("Unsupported 802.11 frame subtype {ieee80211_fc_subtype:#04x}")
                .to_string(),
        );
    };

    let hdr_len: usize = 24 + qos_hdr_bytes;
    Ok(data.split_at(hdr_len))
}

/// Strips LLC header from a binary message buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn remove_llc_hdr(data: &[u8]) -> Result<&[u8], alloc::string::String> {
    extract_llc_hdr(data).map(|(_, remaining)| remaining)
}

/// Extracts LLC header from a binary message buffer and returns the header and remaining buffer
///
/// # Errors
/// Return a human-readable error when parsing failed
pub fn extract_llc_hdr(data: &[u8]) -> Result<(&[u8], &[u8]), alloc::string::String> {
    /*
     * LLC Header has the following format (8 bytes)
     * - 1 bytes DSAP
     * - 1 bytes SSAP
     * - 1 bytes control field
     * - 3 bytes organization code
     * - 2 bytes Type (0x8947 BE is GeoNetworking)
     */

    let llc_type: u16 = (u16::from(data[6]) << 8) | u16::from(data[7]);

    if llc_type != 0x8947 {
        return Err(alloc::format!("Unknown LLC payload type {llc_type:#x}").to_string());
    }

    let hdr_len: usize = 8; // TODO: Is this the right size?
    Ok(data.split_at(hdr_len))
}