ipmi_rs/rmcp/
mod.rs

1use crate::{
2    connection::{IpmiConnection, NotEnoughData},
3    IpmiError,
4};
5use std::{net::ToSocketAddrs, time::Duration};
6
7mod socket;
8
9mod v1_5;
10pub use v1_5::{
11    ActivationError as V1_5ActivationError, ReadError as V1_5ReadError,
12    WriteError as V1_5WriteError,
13};
14
15mod v2_0;
16pub use v2_0::{
17    ActivationError as V2_0ActivationError, ReadError as V2_0ReadError,
18    WriteError as V2_0WriteError, *,
19};
20
21pub use ipmi_rs_core::app::auth::{
22    AuthenticationAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm,
23};
24
25mod checksum;
26
27mod header;
28pub(crate) use header::*;
29
30mod asf;
31pub(crate) use asf::*;
32
33mod internal;
34use internal::{Active, RmcpWithState, Unbound};
35
36#[derive(Debug)]
37pub enum RmcpIpmiReceiveError {
38    Io(std::io::Error),
39    RmcpHeader(RmcpHeaderError),
40    Session(UnwrapSessionError),
41    NotIpmi,
42    NotEnoughData,
43    EmptyMessage,
44    IpmbChecksumFailed,
45}
46
47#[derive(Debug)]
48pub enum RmcpIpmiSendError {
49    V1_5(V1_5WriteError),
50    V2_0(V2_0WriteError),
51}
52
53impl From<V1_5WriteError> for RmcpIpmiSendError {
54    fn from(value: V1_5WriteError) -> Self {
55        Self::V1_5(value)
56    }
57}
58
59impl From<V2_0WriteError> for RmcpIpmiSendError {
60    fn from(value: V2_0WriteError) -> Self {
61        Self::V2_0(value)
62    }
63}
64
65#[derive(Debug, Clone, PartialEq)]
66pub enum UnwrapSessionError {
67    V1_5(V1_5ReadError),
68    V2_0(V2_0ReadError),
69}
70
71impl From<V1_5ReadError> for UnwrapSessionError {
72    fn from(value: V1_5ReadError) -> Self {
73        Self::V1_5(value)
74    }
75}
76
77impl From<V2_0ReadError> for UnwrapSessionError {
78    fn from(value: V2_0ReadError) -> Self {
79        Self::V2_0(value)
80    }
81}
82
83#[derive(Debug)]
84pub enum RmcpIpmiError {
85    NotActive,
86    Receive(RmcpIpmiReceiveError),
87    Send(RmcpIpmiSendError),
88}
89
90impl From<RmcpIpmiReceiveError> for RmcpIpmiError {
91    fn from(value: RmcpIpmiReceiveError) -> Self {
92        Self::Receive(value)
93    }
94}
95
96impl From<RmcpIpmiSendError> for RmcpIpmiError {
97    fn from(value: RmcpIpmiSendError) -> Self {
98        Self::Send(value)
99    }
100}
101
102type CommandError<T> = IpmiError<RmcpIpmiError, T>;
103
104#[derive(Debug)]
105pub enum ActivationError {
106    BindSocket(std::io::Error),
107    PingSend(std::io::Error),
108    PongReceive(std::io::Error),
109    PongRead,
110    /// The contacted host does not support IPMI over RMCP.
111    IpmiNotSupported,
112    NoSupportedIpmiLANVersions,
113    GetChannelAuthenticationCapabilities(CommandError<NotEnoughData>),
114    V1_5(V1_5ActivationError),
115    V2_0(V2_0ActivationError),
116    RmcpError(RmcpHeaderError),
117}
118
119impl From<V1_5ActivationError> for ActivationError {
120    fn from(value: V1_5ActivationError) -> Self {
121        Self::V1_5(value)
122    }
123}
124
125impl From<V2_0ActivationError> for ActivationError {
126    fn from(value: V2_0ActivationError) -> Self {
127        Self::V2_0(value)
128    }
129}
130
131#[derive(Debug)]
132pub struct Rmcp {
133    unbound_state: RmcpWithState<Unbound>,
134    active_state: Option<RmcpWithState<Active>>,
135}
136
137impl Rmcp {
138    pub fn new<R>(remote: R, timeout: Duration) -> Result<Self, std::io::Error>
139    where
140        R: ToSocketAddrs + std::fmt::Debug,
141    {
142        let unbound_state = RmcpWithState::new(remote, timeout)?;
143
144        Ok(Self {
145            unbound_state,
146            active_state: None,
147        })
148    }
149
150    pub fn inactive_clone(&self) -> Self {
151        Self {
152            unbound_state: self.unbound_state.clone(),
153            active_state: None,
154        }
155    }
156
157    pub fn is_active(&self) -> bool {
158        self.active_state.is_some()
159    }
160
161    /// Activate this RMCP connection with the provided username and password.
162    ///
163    /// If `rmcp_plus` is `true`, upgrade the connection to an RMCP+ connection
164    /// if the remote host supports it.
165    pub fn activate(
166        &mut self,
167        rmcp_plus: bool,
168        username: Option<&str>,
169        password: Option<&[u8]>,
170    ) -> Result<(), ActivationError> {
171        if self.active_state.take().is_some() {
172            // TODO: shut down currently active state.
173            log::info!("De-activating RMCP connection for re-activation");
174        }
175
176        let inactive = self
177            .unbound_state
178            .bind()
179            .map_err(ActivationError::BindSocket)?;
180
181        let activated = inactive.activate(rmcp_plus, username, password)?;
182        self.active_state = Some(activated);
183        Ok(())
184    }
185}
186
187impl IpmiConnection for Rmcp {
188    type SendError = RmcpIpmiError;
189
190    type RecvError = RmcpIpmiError;
191
192    type Error = RmcpIpmiError;
193
194    fn send(&mut self, request: &mut crate::connection::Request) -> Result<(), Self::SendError> {
195        let active = self.active_state.as_mut().ok_or(RmcpIpmiError::NotActive)?;
196        active.send(request)
197    }
198
199    fn recv(&mut self) -> Result<crate::connection::Response, Self::RecvError> {
200        let active = self.active_state.as_mut().ok_or(RmcpIpmiError::NotActive)?;
201        active.recv().map_err(RmcpIpmiError::Receive)
202    }
203
204    fn send_recv(
205        &mut self,
206        request: &mut crate::connection::Request,
207    ) -> Result<crate::connection::Response, Self::Error> {
208        let active = self.active_state.as_mut().ok_or(RmcpIpmiError::NotActive)?;
209        active.send_recv(request)
210    }
211}