kawaiifi 0.2.1

Wi-Fi scanning library for Linux, macOS, and Windows.
Documentation
use std::fmt::Display;

use deku::{DekuContainerWrite, DekuRead, DekuWrite};
use serde::{Deserialize, Serialize};

use crate::ies::{BitRange, Field, IeId};

#[derive(Debug, Clone, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
pub struct MeshConfiguration {
    pub active_path_selection_protocol_identifier: ActivePathSelectionProtocolIdentifier,
    pub active_path_selection_metric_identifier: ActivePathSelectionMetricIdentifier,
    pub congestion_control_mode_identifier: CongestionControlModeIdentifier,
    pub synchronization_method_identifier: SynchronizationMethodIdentifier,
    pub authentication_protocol_identifier: AuthenticationProtocolIdentifier,
    pub mesh_formation_info: MeshFormationInfo,
    pub mesh_capability: MeshCapability,
}

impl MeshConfiguration {
    pub const NAME: &'static str = "Mesh Configuration";
    pub const ID: u8 = 113;
    pub const ID_EXT: Option<u8> = None;
    pub(crate) const IE_ID: IeId = IeId::new(Self::ID, Self::ID_EXT);
    pub const MIN_LENGTH: usize = 7;

    pub fn summary(&self) -> String {
        format!(
            "Path Selection Protocol: {}, Path Selection Metric: {}",
            self.active_path_selection_protocol_identifier,
            self.active_path_selection_metric_identifier
        )
    }

    pub fn fields(&self) -> Vec<Field> {
        let bytes = self.to_bytes().unwrap_or_default();
        vec![
            Field::builder()
                .title("Active Path Selection Protocol ID")
                .value(self.active_path_selection_protocol_identifier)
                .byte(*bytes.first().unwrap_or(&0))
                .build(),
            Field::builder()
                .title("Active Path Selection Metric ID")
                .value(self.active_path_selection_metric_identifier)
                .byte(*bytes.get(1).unwrap_or(&0))
                .build(),
            Field::builder()
                .title("Congestion Control Mode ID")
                .value(self.congestion_control_mode_identifier)
                .byte(*bytes.get(2).unwrap_or(&0))
                .build(),
            Field::builder()
                .title("Synchronization Method ID")
                .value(self.synchronization_method_identifier)
                .byte(*bytes.get(3).unwrap_or(&0))
                .build(),
            Field::builder()
                .title("Authentication Protocol ID")
                .value(self.authentication_protocol_identifier)
                .byte(*bytes.get(4).unwrap_or(&0))
                .build(),
            self.mesh_formation_info.to_field(),
            self.mesh_capability.to_field(),
        ]
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(bit_order = "lsb")]
pub struct MeshCapability {
    #[deku(bits = 1)]
    pub accepting_additional_mesh_peerings: bool,
    #[deku(bits = 1)]
    pub mcca_supported: bool,
    #[deku(bits = 1)]
    pub mcca_enabled: bool,
    #[deku(bits = 1)]
    pub forwarding: bool,
    #[deku(bits = 1)]
    pub mbca_enabled: bool,
    #[deku(bits = 1)]
    pub tbtt_adjusting: bool,
    #[deku(bits = 1)]
    pub mesh_power_save_level: bool,
    #[deku(bits = 1)]
    reserved: bool,
}

impl MeshCapability {
    pub fn to_field(&self) -> Field {
        let byte = self
            .to_bytes()
            .unwrap_or_default()
            .first()
            .cloned()
            .unwrap_or_default();
        Field::builder()
            .title("Mesh Capability")
            .value("")
            .subfields([
                Field::builder()
                    .title("Accepting Additional Mesh Peerings")
                    .value(self.accepting_additional_mesh_peerings)
                    .bits(BitRange::from_byte(byte, 0, 1))
                    .build(),
                Field::builder()
                    .title("MCCA Supported")
                    .value(self.mcca_supported)
                    .bits(BitRange::from_byte(byte, 1, 1))
                    .build(),
                Field::builder()
                    .title("MCCA Enabled")
                    .value(self.mcca_enabled)
                    .bits(BitRange::from_byte(byte, 2, 1))
                    .build(),
                Field::builder()
                    .title("Forwarding")
                    .value(self.forwarding)
                    .bits(BitRange::from_byte(byte, 3, 1))
                    .build(),
                Field::builder()
                    .title("MBCA Enabled")
                    .value(self.mbca_enabled)
                    .bits(BitRange::from_byte(byte, 4, 1))
                    .build(),
                Field::builder()
                    .title("TBTT Adjusting")
                    .value(self.tbtt_adjusting)
                    .bits(BitRange::from_byte(byte, 5, 1))
                    .build(),
                Field::builder()
                    .title("Mesh Power Save Level")
                    .value(self.mesh_power_save_level)
                    .bits(BitRange::from_byte(byte, 6, 1))
                    .build(),
                Field::reserved(BitRange::from_byte(byte, 7, 1)),
            ])
            .byte(byte)
            .build()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(bit_order = "lsb")]
pub struct MeshFormationInfo {
    #[deku(bits = 1)]
    pub connected_to_mesh_gate: bool,
    #[deku(bits = 6)]
    pub number_of_peerings: u8,
    #[deku(bits = 1)]
    pub connected_to_as: bool,
}

impl MeshFormationInfo {
    pub fn to_field(&self) -> Field {
        let byte = self
            .to_bytes()
            .unwrap_or_default()
            .first()
            .cloned()
            .unwrap_or_default();

        Field::builder()
            .title("Mesh Formation Info")
            .value("")
            .subfields([
                Field::builder()
                    .title("Connected to Mesh Gate")
                    .value(self.connected_to_mesh_gate)
                    .bits(BitRange::from_byte(byte, 0, 1))
                    .build(),
                Field::builder()
                    .title("Number of Peerings")
                    .value(self.number_of_peerings)
                    .bits(BitRange::from_byte(byte, 1, 6))
                    .build(),
                Field::builder()
                    .title("Connected to AS")
                    .value(self.connected_to_as)
                    .bits(BitRange::from_byte(byte, 7, 1))
                    .build(),
            ])
            .byte(byte)
            .build()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(id_type = "u8")]
#[repr(u8)]
pub enum ActivePathSelectionProtocolIdentifier {
    Hybrid = 1,
    #[deku(id_pat = "_")]
    Reserved(u8),
    VendorSpecific = 255,
}

impl Display for ActivePathSelectionProtocolIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Hybrid => write!(f, "Hybrid"),
            Self::Reserved(val) => write!(f, "Reserved ({})", val),
            Self::VendorSpecific => write!(f, "Vendor Specific"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(id_type = "u8")]
#[repr(u8)]
pub enum ActivePathSelectionMetricIdentifier {
    AirtimeLink = 1,
    HighPhyRate = 2,
    #[deku(id_pat = "_")]
    Reserved(u8),
    VendorSpecific = 255,
}

impl Display for ActivePathSelectionMetricIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::AirtimeLink => write!(f, "Airtime Link Metric"),
            Self::HighPhyRate => write!(f, "High PHY Rate Airtime Link Metric"),
            Self::Reserved(val) => write!(f, "Reserved ({})", val),
            Self::VendorSpecific => write!(f, "Vendor Specific"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(id_type = "u8")]
#[repr(u8)]
pub enum CongestionControlModeIdentifier {
    NotActivated = 0,
    CongestionControlSignaling = 1,
    #[deku(id_pat = "_")]
    Reserved(u8),
    VendorSpecific = 255,
}

impl Display for CongestionControlModeIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NotActivated => write!(f, "Not Activated"),
            Self::CongestionControlSignaling => write!(f, "Congestion Control Signaling"),
            Self::Reserved(val) => write!(f, "Reserved ({})", val),
            Self::VendorSpecific => write!(f, "Vendor Specific"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(id_type = "u8")]
#[repr(u8)]
pub enum SynchronizationMethodIdentifier {
    NeighborOffset = 1,
    #[deku(id_pat = "_")]
    Reserved(u8),
    VendorSpecific = 255,
}

impl Display for SynchronizationMethodIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NeighborOffset => write!(f, "Neighbor Offset"),
            Self::Reserved(val) => write!(f, "Reserved ({})", val),
            Self::VendorSpecific => write!(f, "Vendor Specific"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(id_type = "u8")]
#[repr(u8)]
pub enum AuthenticationProtocolIdentifier {
    NotRequired = 0,
    Sae = 1,
    Ieee8021X = 2,
    #[deku(id_pat = "_")]
    Reserved(u8),
    VendorSpecific = 255,
}

impl Display for AuthenticationProtocolIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NotRequired => write!(f, "Not Required"),
            Self::Sae => write!(f, "SAE"),
            Self::Ieee8021X => write!(f, "IEEE 802.1X"),
            Self::Reserved(val) => write!(f, "Reserved ({})", val),
            Self::VendorSpecific => write!(f, "Vendor Specific"),
        }
    }
}