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