ipmi_rs/connection/impls/rmcp/
mod.rs

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