blob: 738652c98b1a316610e116751ea31ee2df8ccff4 [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
Tom Joseph9b672192016-08-08 08:34:08 -050024namespace LAN
25{
26
Tom Joseph63d3e492017-03-31 11:01:08 +053027constexpr uint8_t requesterBMCAddress = 0x20;
28constexpr uint8_t responderBMCAddress = 0x81;
29
Tom Joseph9b672192016-08-08 08:34:08 -050030namespace header
31{
32
Tom Joseph3563f8f2017-05-08 15:42:54 +053033/**
34 * @struct IPMI LAN Message Request Header
35 */
Tom Joseph9b672192016-08-08 08:34:08 -050036struct Request
37{
38 uint8_t rsaddr;
39 uint8_t netfn;
40 uint8_t cs;
41 uint8_t rqaddr;
42 uint8_t rqseq;
43 uint8_t cmd;
44} __attribute__((packed));
45
Tom Joseph3563f8f2017-05-08 15:42:54 +053046/**
47 * @struct IPMI LAN Message Response Header
48 */
Tom Joseph9b672192016-08-08 08:34:08 -050049struct Response
50{
51 uint8_t rqaddr;
52 uint8_t netfn;
53 uint8_t cs;
54 uint8_t rsaddr;
55 uint8_t rqseq;
56 uint8_t cmd;
57} __attribute__((packed));
58
59} // namespace header
60
61namespace trailer
62{
63
Tom Joseph3563f8f2017-05-08 15:42:54 +053064/**
65 * @struct IPMI LAN Message Trailer
66 */
Tom Joseph9b672192016-08-08 08:34:08 -050067struct Request
68{
69 uint8_t checksum;
70} __attribute__((packed));
71
72using Response = Request;
73
74} // namespace trailer
75
76} // namespace LAN
77
Vernon Mauery7f268e42018-10-26 10:26:01 -070078/**
79 * @brief Calculate 8 bit 2's complement checksum
80 *
81 * Initialize checksum to 0. For each byte, checksum = (checksum + byte)
82 * modulo 256. Then checksum = - checksum. When the checksum and the
83 * bytes are added together, modulo 256, the result should be 0.
84 */
85static inline uint8_t crc8bit(const uint8_t* ptr, const size_t len)
86{
87 return (0x100 - std::accumulate(ptr, ptr + len, 0));
88}
89
90/**
91 * @struct Message
92 *
93 * IPMI message is data encapsulated in an IPMI Session packet. The IPMI
94 * Session packets are encapsulated in RMCP packets, which are encapsulated in
95 * UDP datagrams. Refer Section 13.5 of IPMI specification(IPMI Messages
96 * Encapsulation Under RMCP). IPMI payload is a special class of data
97 * encapsulated in an IPMI session packet.
98 */
99struct Message
100{
101 static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
102
103 Message() :
104 payloadType(PayloadType::INVALID),
105 rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
106 bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID)
107 {
108 }
109
110 /**
111 * @brief Special behavior for copy constructor
112 *
113 * Based on incoming message state, the resulting message will have a
114 * pre-baked state. This is used to simplify the flows for creating a
115 * response message. For each pre-session state, the response message is
116 * actually a different type of message. Once the session has been
117 * established, the response type is the same as the request type.
118 */
119 Message(const Message& other) :
120 isPacketEncrypted(other.isPacketEncrypted),
121 isPacketAuthenticated(other.isPacketAuthenticated),
122 payloadType(other.payloadType), rcSessionID(other.rcSessionID),
123 bmcSessionID(other.bmcSessionID)
124 {
125 // special behavior for rmcp+ session creation
126 if (PayloadType::OPEN_SESSION_REQUEST == other.payloadType)
127 {
128 payloadType = PayloadType::OPEN_SESSION_RESPONSE;
129 }
130 else if (PayloadType::RAKP1 == other.payloadType)
131 {
132 payloadType = PayloadType::RAKP2;
133 }
134 else if (PayloadType::RAKP3 == other.payloadType)
135 {
136 payloadType = PayloadType::RAKP4;
137 }
138 }
139 Message& operator=(const Message&) = default;
140 Message(Message&&) = default;
141 Message& operator=(Message&&) = default;
142 ~Message() = default;
143
144 /**
145 * @brief Extract the command from the IPMI payload
146 *
147 * @return Command ID in the incoming message
148 */
149 uint32_t getCommand()
150 {
151 uint32_t command = 0;
152
153 command |= (static_cast<uint8_t>(payloadType) << 16);
154 if (payloadType == PayloadType::IPMI)
155 {
156 auto request =
157 reinterpret_cast<LAN::header::Request*>(payload.data());
158 command |= request->netfn << 8;
159 command |= request->cmd;
160 }
161 return command;
162 }
163
164 /**
165 * @brief Create the response IPMI message
166 *
167 * The IPMI outgoing message is constructed out of payload and the
168 * corresponding fields are populated. For the payload type IPMI, the
169 * LAN message header and trailer are added.
170 *
171 * @param[in] output - Payload for outgoing message
172 *
173 * @return Outgoing message on success and nullptr on failure
174 */
175 std::shared_ptr<Message> createResponse(std::vector<uint8_t>& output)
176 {
177 // SOL packets don't reply; return NULL
178 if (payloadType == PayloadType::SOL)
179 {
180 return nullptr;
181 }
182 auto outMessage = std::make_shared<Message>(*this);
183
184 if (payloadType == PayloadType::IPMI)
185 {
186 outMessage->payloadType = PayloadType::IPMI;
187
188 outMessage->payload.resize(sizeof(LAN::header::Response) +
189 output.size() +
190 sizeof(LAN::trailer::Response));
191
192 auto reqHeader =
193 reinterpret_cast<LAN::header::Request*>(payload.data());
194 auto respHeader = reinterpret_cast<LAN::header::Response*>(
195 outMessage->payload.data());
196
197 // Add IPMI LAN Message Response Header
198 respHeader->rqaddr = reqHeader->rqaddr;
199 respHeader->netfn = reqHeader->netfn | 0x04;
200 respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
201 respHeader->rsaddr = reqHeader->rsaddr;
202 respHeader->rqseq = reqHeader->rqseq;
203 respHeader->cmd = reqHeader->cmd;
204
205 auto assembledSize = sizeof(LAN::header::Response);
206
207 // Copy the output by the execution of the command
208 std::copy(output.begin(), output.end(),
209 outMessage->payload.begin() + assembledSize);
210 assembledSize += output.size();
211
212 // Add the IPMI LAN Message Trailer
213 auto trailer = reinterpret_cast<LAN::trailer::Response*>(
214 outMessage->payload.data() + assembledSize);
215 trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
216 }
217 else
218 {
219 outMessage->payload = output;
220 }
221 return outMessage;
222 }
223
224 bool isPacketEncrypted; // Message's Encryption Status
225 bool isPacketAuthenticated; // Message's Authentication Status
226 PayloadType payloadType; // Type of message payload (IPMI,SOL ..etc)
227 uint32_t rcSessionID; // Remote Client's Session ID
228 uint32_t bmcSessionID; // BMC's session ID
229 uint32_t sessionSeqNum; // Session Sequence Number
230
231 /** @brief Message payload
232 *
233 * “Payloads” are a capability specified for RMCP+ that enable an IPMI
234 * session to carry types of traffic that are in addition to IPMI Messages.
235 * Payloads can be ‘standard’ or ‘OEM’.Standard payload types include IPMI
236 * Messages, messages for session setup under RMCP+, and the payload for
237 * the “Serial Over LAN” capability introduced in IPMI v2.0.
238 */
239 std::vector<uint8_t> payload;
240};
241
Tom Joseph9b672192016-08-08 08:34:08 -0500242} // namespace message