xref: /openbmc/phosphor-net-ipmid/message.hpp (revision 099fb097)
1 #pragma once
2 
3 #include <cstddef>
4 #include <memory>
5 #include <numeric>
6 #include <vector>
7 
8 namespace message
9 {
10 
11 enum class PayloadType : uint8_t
12 {
13     IPMI = 0x00,
14     SOL = 0x01,
15     OPEN_SESSION_REQUEST = 0x10,
16     OPEN_SESSION_RESPONSE = 0x11,
17     RAKP1 = 0x12,
18     RAKP2 = 0x13,
19     RAKP3 = 0x14,
20     RAKP4 = 0x15,
21     INVALID = 0xFF,
22 };
23 
24 // RMCP Classes of Message as per section 13.1.3.
25 enum 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.
35 enum class RmcpMsgType : uint8_t
36 {
37     PING = 0x80,
38     PONG = 0x40,
39 };
40 #endif // RMCP_PING
41 
42 namespace LAN
43 {
44 
45 constexpr uint8_t requesterBMCAddress = 0x20;
46 constexpr uint8_t responderBMCAddress = 0x81;
47 
48 namespace header
49 {
50 
51 /**
52  * @struct IPMI LAN Message Request Header
53  */
54 struct 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 
64 /**
65  * @struct IPMI LAN Message Response Header
66  */
67 struct 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 
79 namespace trailer
80 {
81 
82 /**
83  * @struct IPMI LAN Message Trailer
84  */
85 struct Request
86 {
87     uint8_t checksum;
88 } __attribute__((packed));
89 
90 using Response = Request;
91 
92 } // namespace trailer
93 
94 } // namespace LAN
95 
96 /**
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  */
crc8bit(const uint8_t * ptr,const size_t len)103 static 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  */
117 struct Message
118 {
119     static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
120 
Messagemessage::Message121     Message() :
122         payloadType(PayloadType::INVALID),
123         rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
124         bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
125         rmcpMsgClass(ClassOfMsg::RESERVED)
126     {}
127 
128     /**
129      * @brief Special behavior for copy constructor
130      *
131      * Based on incoming message state, the resulting message will have a
132      * pre-baked state. This is used to simplify the flows for creating a
133      * response message. For each pre-session state, the response message is
134      * actually a different type of message. Once the session has been
135      * established, the response type is the same as the request type.
136      */
Messagemessage::Message137     Message(const Message& other) :
138         isPacketEncrypted(other.isPacketEncrypted),
139         isPacketAuthenticated(other.isPacketAuthenticated),
140         payloadType(other.payloadType), rcSessionID(other.rcSessionID),
141         bmcSessionID(other.bmcSessionID), rmcpMsgClass(other.rmcpMsgClass)
142     {
143         // special behavior for rmcp+ session creation
144         if (PayloadType::OPEN_SESSION_REQUEST == other.payloadType)
145         {
146             payloadType = PayloadType::OPEN_SESSION_RESPONSE;
147         }
148         else if (PayloadType::RAKP1 == other.payloadType)
149         {
150             payloadType = PayloadType::RAKP2;
151         }
152         else if (PayloadType::RAKP3 == other.payloadType)
153         {
154             payloadType = PayloadType::RAKP4;
155         }
156     }
157     Message& operator=(const Message&) = default;
158     Message(Message&&) = default;
159     Message& operator=(Message&&) = default;
160     ~Message() = default;
161 
162     /**
163      * @brief Extract the command from the IPMI payload
164      *
165      * @return Command ID in the incoming message
166      */
getCommandmessage::Message167     uint32_t getCommand()
168     {
169         uint32_t command = 0;
170 
171         command |= (static_cast<uint32_t>(payloadType) << 16);
172         if (payloadType == PayloadType::IPMI)
173         {
174             auto request =
175                 reinterpret_cast<LAN::header::Request*>(payload.data());
176             command |= request->netfn << 8;
177             command |= static_cast<uint32_t>(request->cmd);
178         }
179         return command;
180     }
181 
182     /**
183      * @brief Create the response IPMI message
184      *
185      * The IPMI outgoing message is constructed out of payload and the
186      * corresponding fields are populated. For the payload type IPMI, the
187      * LAN message header and trailer are added.
188      *
189      * @param[in] output - Payload for outgoing message
190      *
191      * @return Outgoing message on success and nullptr on failure
192      */
createResponsemessage::Message193     std::shared_ptr<Message> createResponse(std::vector<uint8_t>& output)
194     {
195         // SOL packets don't reply; return NULL
196         if (payloadType == PayloadType::SOL)
197         {
198             return nullptr;
199         }
200         auto outMessage = std::make_shared<Message>(*this);
201 
202         if (payloadType == PayloadType::IPMI)
203         {
204             outMessage->payloadType = PayloadType::IPMI;
205 
206             outMessage->payload.resize(sizeof(LAN::header::Response) +
207                                        output.size() +
208                                        sizeof(LAN::trailer::Response));
209 
210             auto reqHeader =
211                 reinterpret_cast<LAN::header::Request*>(payload.data());
212             auto respHeader = reinterpret_cast<LAN::header::Response*>(
213                 outMessage->payload.data());
214 
215             // Add IPMI LAN Message Response Header
216             respHeader->rqaddr = reqHeader->rqaddr;
217             respHeader->netfn = reqHeader->netfn | 0x04;
218             respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
219             respHeader->rsaddr = reqHeader->rsaddr;
220             respHeader->rqseq = reqHeader->rqseq;
221             respHeader->cmd = reqHeader->cmd;
222 
223             auto assembledSize = sizeof(LAN::header::Response);
224 
225             // Copy the output by the execution of the command
226             std::copy(output.begin(), output.end(),
227                       outMessage->payload.begin() + assembledSize);
228             assembledSize += output.size();
229 
230             // Add the IPMI LAN Message Trailer
231             auto trailer = reinterpret_cast<LAN::trailer::Response*>(
232                 outMessage->payload.data() + assembledSize);
233             trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
234         }
235         else
236         {
237             outMessage->payload = output;
238         }
239         return outMessage;
240     }
241 
242     bool isPacketEncrypted;     // Message's Encryption Status
243     bool isPacketAuthenticated; // Message's Authentication Status
244     PayloadType payloadType;    // Type of message payload (IPMI,SOL ..etc)
245     uint32_t rcSessionID;       // Remote Client's Session ID
246     uint32_t bmcSessionID;      // BMC's session ID
247     uint32_t sessionSeqNum;     // Session Sequence Number
248     ClassOfMsg rmcpMsgClass;    // Class of Message
249 #ifdef RMCP_PING
250     uint8_t asfMsgTag;          // ASF Message Tag
251 #endif                          // RMCP_PING
252 
253     /** @brief Message payload
254      *
255      *  “Payloads” are a capability specified for RMCP+ that enable an IPMI
256      *  session to carry types of traffic that are in addition to IPMI Messages.
257      *  Payloads can be ‘standard’ or ‘OEM’.Standard payload types include IPMI
258      *  Messages, messages for session setup under RMCP+, and the payload for
259      *  the “Serial Over LAN” capability introduced in IPMI v2.0.
260      */
261     std::vector<uint8_t> payload;
262 };
263 
264 } // namespace message
265