rust-ipmi 0.1.1

IPMI client library for rust
Documentation
use bitvec::{prelude::*, vec::BitVec};

use crate::err::{IpmiHeaderError, IpmiV2HeaderError};

use super::ipmi_header::AuthType;

#[derive(Clone, Copy, Debug)]
pub struct IpmiV2Header {
    pub auth_type: AuthType,
    pub payload_enc: bool,
    pub payload_auth: bool,
    pub payload_type: PayloadType,
    pub oem_iana: Option<u32>,
    pub oem_payload_id: Option<u16>,
    pub rmcp_plus_session_id: u32,
    pub session_seq_number: u32,
    pub payload_length: u16,
}

impl TryFrom<&[u8]> for IpmiV2Header {
    type Error = IpmiHeaderError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        if (value.len() != 12) && (value.len() != 18) {
            Err(IpmiV2HeaderError::WrongLength)?
        }

        let auth_type: AuthType = value[0].try_into()?;
        let payload_bit_slice = BitSlice::<u8, Msb0>::from_element(&value[1]);
        let payload_enc = payload_bit_slice[0];
        let payload_auth = payload_bit_slice[1];
        let payload_type: PayloadType = payload_bit_slice[3..].load::<u8>().try_into()?;
        let oem_iana: Option<u32>;
        let oem_payload_id: Option<u16>;
        let rmcp_plus_session_id: u32;
        let session_seq_number: u32;
        let payload_length: u16;
        match payload_type {
            PayloadType::OEM => {
                if value.len() != 18 {
                    Err(IpmiV2HeaderError::WrongLength)?
                }
                oem_iana = Some(u32::from_be_bytes([value[2], value[3], value[4], value[5]]));
                oem_payload_id = Some(u16::from_be_bytes([value[6], value[7]]));
                rmcp_plus_session_id =
                    u32::from_be_bytes([value[8], value[9], value[10], value[11]]);
                session_seq_number =
                    u32::from_be_bytes([value[12], value[13], value[14], value[15]]);
                payload_length = u16::from_le_bytes([value[16], value[17]]);
            }
            _ => {
                oem_iana = None;
                oem_payload_id = None;
                rmcp_plus_session_id = u32::from_be_bytes([value[2], value[3], value[4], value[5]]);
                session_seq_number = u32::from_be_bytes([value[6], value[7], value[8], value[9]]);
                payload_length = u16::from_le_bytes([value[10], value[11]]);
            }
        }

        Ok(IpmiV2Header {
            auth_type,
            payload_enc,
            payload_auth,
            payload_type,
            oem_iana,
            oem_payload_id,
            rmcp_plus_session_id,
            session_seq_number,
            payload_length,
        })
    }
}

impl Into<Vec<u8>> for IpmiV2Header {
    fn into(self) -> Vec<u8> {
        match self.payload_type {
            PayloadType::OEM => {
                let oem_iana_be = self.oem_iana.unwrap().to_le_bytes();
                let oem_payload_id_be = self.oem_payload_id.unwrap().to_le_bytes();
                let rmcp_ses_be = self.rmcp_plus_session_id.to_le_bytes();
                let ses_seq_be = self.session_seq_number.to_le_bytes();
                let len_be = self.payload_length.to_le_bytes();

                let mut result = Vec::new();
                result.extend([self.auth_type.into(), {
                    let mut bv: BitVec<u8, Msb0> = bitvec![u8, Msb0; 0;8];
                    bv.set(0, self.payload_enc);
                    bv.set(1, self.payload_auth);
                    bv[2..].store::<u8>(self.payload_type.into());
                    let payload_type = bv[..].load::<u8>();
                    payload_type
                }]);
                result.extend(oem_iana_be);
                result.extend(oem_payload_id_be);
                result.extend(rmcp_ses_be);
                result.extend(ses_seq_be);
                result.extend(len_be);
                result
            }
            _ => {
                let rmcp_ses_be = self.rmcp_plus_session_id.to_le_bytes();
                let ses_seq_be = self.session_seq_number.to_le_bytes();
                let len_be = self.payload_length.to_le_bytes();

                let mut result = Vec::new();
                result.extend([self.auth_type.into(), {
                    let mut bv: BitVec<u8, Msb0> = bitvec![u8, Msb0; 0;8];
                    bv.set(0, self.payload_enc);
                    bv.set(1, self.payload_auth);
                    bv[2..].store::<u8>(self.payload_type.into());
                    let payload_type = bv[..].load::<u8>();
                    payload_type
                }]);
                result.extend(rmcp_ses_be);
                result.extend(ses_seq_be);
                result.extend(len_be);
                result
            }
        }
    }
}

impl IpmiV2Header {
    pub fn new(
        auth_type: AuthType,
        payload_enc: bool,
        payload_auth: bool,
        payload_type: PayloadType,
        rmcp_plus_session_id: u32,
        session_seq_number: u32,
        payload_length: u16,
    ) -> IpmiV2Header {
        IpmiV2Header {
            auth_type,
            payload_enc,
            payload_auth,
            payload_type,
            oem_iana: None,
            oem_payload_id: None,
            rmcp_plus_session_id,
            session_seq_number,
            payload_length,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub enum PayloadType {
    IPMI,
    SOL,
    OEM,
    RcmpOpenSessionRequest,
    RcmpOpenSessionResponse,
    RAKP1,
    RAKP2,
    RAKP3,
    RAKP4,
}

impl TryFrom<u8> for PayloadType {
    type Error = IpmiV2HeaderError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0x00 => Ok(PayloadType::IPMI),
            0x01 => Ok(PayloadType::SOL),
            0x02 => Ok(PayloadType::OEM),
            0x10 => Ok(PayloadType::RcmpOpenSessionRequest),
            0x11 => Ok(PayloadType::RcmpOpenSessionResponse),
            0x12 => Ok(PayloadType::RAKP1),
            0x13 => Ok(PayloadType::RAKP2),
            0x14 => Ok(PayloadType::RAKP3),
            0x15 => Ok(PayloadType::RAKP4),
            _ => Err(IpmiV2HeaderError::UnsupportedPayloadType(value)),
        }
    }
}

impl Into<u8> for PayloadType {
    fn into(self) -> u8 {
        match &self {
            PayloadType::IPMI => 0x00,
            PayloadType::SOL => 0x01,
            PayloadType::OEM => 0x02,
            PayloadType::RcmpOpenSessionRequest => 0x10,
            PayloadType::RcmpOpenSessionResponse => 0x11,
            PayloadType::RAKP1 => 0x12,
            PayloadType::RAKP2 => 0x13,
            PayloadType::RAKP3 => 0x14,
            PayloadType::RAKP4 => 0x15,
        }
    }
}