xref: /openbmc/phosphor-net-ipmid/message.hpp (revision 8425624a)
1 #pragma once
2 
3 #include "config.h"
4 
5 #include <cstddef>
6 #include <memory>
7 #include <numeric>
8 #include <vector>
9 
10 namespace message
11 {
12 
13 enum class PayloadType : uint8_t
14 {
15     IPMI = 0x00,
16     SOL = 0x01,
17     OPEN_SESSION_REQUEST = 0x10,
18     OPEN_SESSION_RESPONSE = 0x11,
19     RAKP1 = 0x12,
20     RAKP2 = 0x13,
21     RAKP3 = 0x14,
22     RAKP4 = 0x15,
23     INVALID = 0xFF,
24 };
25 
26 // RMCP Classes of Message as per section 13.1.3.
27 enum 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.
37 enum class RmcpMsgType : uint8_t
38 {
39     PING = 0x80,
40     PONG = 0x40,
41 };
42 #endif // RMCP_PING
43 
44 namespace LAN
45 {
46 
47 constexpr uint8_t requesterBMCAddress = 0x20;
48 constexpr uint8_t responderBMCAddress = 0x81;
49 
50 namespace header
51 {
52 
53 /**
54  * @struct IPMI LAN Message Request Header
55  */
56 struct 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 
66 /**
67  * @struct IPMI LAN Message Response Header
68  */
69 struct 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 
81 namespace trailer
82 {
83 
84 /**
85  * @struct IPMI LAN Message Trailer
86  */
87 struct Request
88 {
89     uint8_t checksum;
90 } __attribute__((packed));
91 
92 using Response = Request;
93 
94 } // namespace trailer
95 
96 } // namespace LAN
97 
98 /**
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  */
crc8bit(const uint8_t * ptr,const size_t len)105 static 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  */
119 struct Message
120 {
121     static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
122 
Messagemessage::Message123     Message() :
124         payloadType(PayloadType::INVALID),
125         rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
126         bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
127         rmcpMsgClass(ClassOfMsg::RESERVED)
128     {}
129 
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      */
Messagemessage::Message139     Message(const Message& other) :
140         isPacketEncrypted(other.isPacketEncrypted),
141         isPacketAuthenticated(other.isPacketAuthenticated),
142         payloadType(other.payloadType), rcSessionID(other.rcSessionID),
143         bmcSessionID(other.bmcSessionID), rmcpMsgClass(other.rmcpMsgClass)
144     {
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      */
getCommandmessage::Message169     uint32_t getCommand()
170     {
171         uint32_t command = 0;
172 
173         command |= (static_cast<uint32_t>(payloadType) << 16);
174         if (payloadType == PayloadType::IPMI)
175         {
176             auto request =
177                 reinterpret_cast<LAN::header::Request*>(payload.data());
178             command |= request->netfn << 8;
179             command |= static_cast<uint32_t>(request->cmd);
180         }
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      */
createResponsemessage::Message195     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 
208             outMessage->payload.resize(
209                 sizeof(LAN::header::Response) + output.size() +
210                 sizeof(LAN::trailer::Response));
211 
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
250     ClassOfMsg rmcpMsgClass;    // Class of Message
251 #ifdef RMCP_PING
252     uint8_t asfMsgTag;          // ASF Message Tag
253 #endif                          // RMCP_PING
254 
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 
266 } // namespace message
267