blob: 04dac2705955c032b27d4567a28a6f18aef47448 [file] [log] [blame]
Tom Joseph9b672192016-08-08 08:34:08 -05001#pragma once
2
Andrew Geissleracb8d902024-07-30 10:05:04 -03003#include "config.h"
4
Andrew Geissler9d9b7632020-05-17 09:18:05 -05005#include <cstddef>
Tom Joseph9b672192016-08-08 08:34:08 -05006#include <memory>
Vernon Mauery7f268e42018-10-26 10:26:01 -07007#include <numeric>
Tom Joseph9b672192016-08-08 08:34:08 -05008#include <vector>
9
10namespace message
11{
12
13enum class PayloadType : uint8_t
14{
Vernon Mauery9e801a22018-10-12 13:20:49 -070015 IPMI = 0x00,
16 SOL = 0x01,
17 OPEN_SESSION_REQUEST = 0x10,
Tom Joseph9b672192016-08-08 08:34:08 -050018 OPEN_SESSION_RESPONSE = 0x11,
Vernon Mauery9e801a22018-10-12 13:20:49 -070019 RAKP1 = 0x12,
20 RAKP2 = 0x13,
21 RAKP3 = 0x14,
22 RAKP4 = 0x15,
23 INVALID = 0xFF,
Tom Joseph9b672192016-08-08 08:34:08 -050024};
25
Kirill Pakhomovde7dd5c2021-02-27 18:45:22 +030026// RMCP Classes of Message as per section 13.1.3.
27enum class ClassOfMsg : uint8_t
28{
29 RESERVED = 0x05,
30 ASF = 0x06,
31 IPMI = 0x07,
32 OEM = 0x08,
33};
34
35#ifdef RMCP_PING
36// RMCP Message Type as per section 13.1.3.
37enum class RmcpMsgType : uint8_t
38{
39 PING = 0x80,
40 PONG = 0x40,
41};
42#endif // RMCP_PING
43
Tom Joseph9b672192016-08-08 08:34:08 -050044namespace LAN
45{
46
Tom Joseph63d3e492017-03-31 11:01:08 +053047constexpr uint8_t requesterBMCAddress = 0x20;
48constexpr uint8_t responderBMCAddress = 0x81;
49
Tom Joseph9b672192016-08-08 08:34:08 -050050namespace header
51{
52
Tom Joseph3563f8f2017-05-08 15:42:54 +053053/**
54 * @struct IPMI LAN Message Request Header
55 */
Tom Joseph9b672192016-08-08 08:34:08 -050056struct Request
57{
58 uint8_t rsaddr;
59 uint8_t netfn;
60 uint8_t cs;
61 uint8_t rqaddr;
62 uint8_t rqseq;
63 uint8_t cmd;
64} __attribute__((packed));
65
Tom Joseph3563f8f2017-05-08 15:42:54 +053066/**
67 * @struct IPMI LAN Message Response Header
68 */
Tom Joseph9b672192016-08-08 08:34:08 -050069struct Response
70{
71 uint8_t rqaddr;
72 uint8_t netfn;
73 uint8_t cs;
74 uint8_t rsaddr;
75 uint8_t rqseq;
76 uint8_t cmd;
77} __attribute__((packed));
78
79} // namespace header
80
81namespace trailer
82{
83
Tom Joseph3563f8f2017-05-08 15:42:54 +053084/**
85 * @struct IPMI LAN Message Trailer
86 */
Tom Joseph9b672192016-08-08 08:34:08 -050087struct Request
88{
89 uint8_t checksum;
90} __attribute__((packed));
91
92using Response = Request;
93
94} // namespace trailer
95
96} // namespace LAN
97
Vernon Mauery7f268e42018-10-26 10:26:01 -070098/**
99 * @brief Calculate 8 bit 2's complement checksum
100 *
101 * Initialize checksum to 0. For each byte, checksum = (checksum + byte)
102 * modulo 256. Then checksum = - checksum. When the checksum and the
103 * bytes are added together, modulo 256, the result should be 0.
104 */
105static inline uint8_t crc8bit(const uint8_t* ptr, const size_t len)
106{
107 return (0x100 - std::accumulate(ptr, ptr + len, 0));
108}
109
110/**
111 * @struct Message
112 *
113 * IPMI message is data encapsulated in an IPMI Session packet. The IPMI
114 * Session packets are encapsulated in RMCP packets, which are encapsulated in
115 * UDP datagrams. Refer Section 13.5 of IPMI specification(IPMI Messages
116 * Encapsulation Under RMCP). IPMI payload is a special class of data
117 * encapsulated in an IPMI session packet.
118 */
119struct Message
120{
121 static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
122
123 Message() :
124 payloadType(PayloadType::INVALID),
125 rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
Kirill Pakhomovde7dd5c2021-02-27 18:45:22 +0300126 bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
127 rmcpMsgClass(ClassOfMsg::RESERVED)
George Liubc8958f2022-07-04 09:29:49 +0800128 {}
Vernon Mauery7f268e42018-10-26 10:26:01 -0700129
130 /**
131 * @brief Special behavior for copy constructor
132 *
133 * Based on incoming message state, the resulting message will have a
134 * pre-baked state. This is used to simplify the flows for creating a
135 * response message. For each pre-session state, the response message is
136 * actually a different type of message. Once the session has been
137 * established, the response type is the same as the request type.
138 */
139 Message(const Message& other) :
140 isPacketEncrypted(other.isPacketEncrypted),
141 isPacketAuthenticated(other.isPacketAuthenticated),
142 payloadType(other.payloadType), rcSessionID(other.rcSessionID),
Kirill Pakhomovde7dd5c2021-02-27 18:45:22 +0300143 bmcSessionID(other.bmcSessionID), rmcpMsgClass(other.rmcpMsgClass)
Vernon Mauery7f268e42018-10-26 10:26:01 -0700144 {
145 // special behavior for rmcp+ session creation
146 if (PayloadType::OPEN_SESSION_REQUEST == other.payloadType)
147 {
148 payloadType = PayloadType::OPEN_SESSION_RESPONSE;
149 }
150 else if (PayloadType::RAKP1 == other.payloadType)
151 {
152 payloadType = PayloadType::RAKP2;
153 }
154 else if (PayloadType::RAKP3 == other.payloadType)
155 {
156 payloadType = PayloadType::RAKP4;
157 }
158 }
159 Message& operator=(const Message&) = default;
160 Message(Message&&) = default;
161 Message& operator=(Message&&) = default;
162 ~Message() = default;
163
164 /**
165 * @brief Extract the command from the IPMI payload
166 *
167 * @return Command ID in the incoming message
168 */
169 uint32_t getCommand()
170 {
171 uint32_t command = 0;
172
Vernon Maueryec5ddaf2022-08-03 11:00:50 -0700173 command |= (static_cast<uint32_t>(payloadType) << 16);
Vernon Mauery7f268e42018-10-26 10:26:01 -0700174 if (payloadType == PayloadType::IPMI)
175 {
176 auto request =
177 reinterpret_cast<LAN::header::Request*>(payload.data());
178 command |= request->netfn << 8;
P Dheeraj Srujan Kumarb88599a2021-07-20 05:18:30 +0530179 command |= static_cast<uint32_t>(request->cmd);
Vernon Mauery7f268e42018-10-26 10:26:01 -0700180 }
181 return command;
182 }
183
184 /**
185 * @brief Create the response IPMI message
186 *
187 * The IPMI outgoing message is constructed out of payload and the
188 * corresponding fields are populated. For the payload type IPMI, the
189 * LAN message header and trailer are added.
190 *
191 * @param[in] output - Payload for outgoing message
192 *
193 * @return Outgoing message on success and nullptr on failure
194 */
195 std::shared_ptr<Message> createResponse(std::vector<uint8_t>& output)
196 {
197 // SOL packets don't reply; return NULL
198 if (payloadType == PayloadType::SOL)
199 {
200 return nullptr;
201 }
202 auto outMessage = std::make_shared<Message>(*this);
203
204 if (payloadType == PayloadType::IPMI)
205 {
206 outMessage->payloadType = PayloadType::IPMI;
207
Patrick Williams84256242024-08-16 15:20:21 -0400208 outMessage->payload.resize(
209 sizeof(LAN::header::Response) + output.size() +
210 sizeof(LAN::trailer::Response));
Vernon Mauery7f268e42018-10-26 10:26:01 -0700211
212 auto reqHeader =
213 reinterpret_cast<LAN::header::Request*>(payload.data());
214 auto respHeader = reinterpret_cast<LAN::header::Response*>(
215 outMessage->payload.data());
216
217 // Add IPMI LAN Message Response Header
218 respHeader->rqaddr = reqHeader->rqaddr;
219 respHeader->netfn = reqHeader->netfn | 0x04;
220 respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
221 respHeader->rsaddr = reqHeader->rsaddr;
222 respHeader->rqseq = reqHeader->rqseq;
223 respHeader->cmd = reqHeader->cmd;
224
225 auto assembledSize = sizeof(LAN::header::Response);
226
227 // Copy the output by the execution of the command
228 std::copy(output.begin(), output.end(),
229 outMessage->payload.begin() + assembledSize);
230 assembledSize += output.size();
231
232 // Add the IPMI LAN Message Trailer
233 auto trailer = reinterpret_cast<LAN::trailer::Response*>(
234 outMessage->payload.data() + assembledSize);
235 trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
236 }
237 else
238 {
239 outMessage->payload = output;
240 }
241 return outMessage;
242 }
243
244 bool isPacketEncrypted; // Message's Encryption Status
245 bool isPacketAuthenticated; // Message's Authentication Status
246 PayloadType payloadType; // Type of message payload (IPMI,SOL ..etc)
247 uint32_t rcSessionID; // Remote Client's Session ID
248 uint32_t bmcSessionID; // BMC's session ID
249 uint32_t sessionSeqNum; // Session Sequence Number
Kirill Pakhomovde7dd5c2021-02-27 18:45:22 +0300250 ClassOfMsg rmcpMsgClass; // Class of Message
251#ifdef RMCP_PING
Patrick Williams099fb092023-05-10 07:50:31 -0500252 uint8_t asfMsgTag; // ASF Message Tag
253#endif // RMCP_PING
Vernon Mauery7f268e42018-10-26 10:26:01 -0700254
255 /** @brief Message payload
256 *
257 * “Payloads” are a capability specified for RMCP+ that enable an IPMI
258 * session to carry types of traffic that are in addition to IPMI Messages.
259 * Payloads can be ‘standard’ or ‘OEM’.Standard payload types include IPMI
260 * Messages, messages for session setup under RMCP+, and the payload for
261 * the “Serial Over LAN” capability introduced in IPMI v2.0.
262 */
263 std::vector<uint8_t> payload;
264};
265
Tom Joseph9b672192016-08-08 08:34:08 -0500266} // namespace message