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