xref: /openbmc/phosphor-net-ipmid/message.hpp (revision b88599a2cef6b4fd2272f065a37ce0f70ca8dd38)
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  */
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 
121     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     /**
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),
142         bmcSessionID(other.bmcSessionID), rmcpMsgClass(other.rmcpMsgClass)
143     {
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 |= static_cast<uint32_t>(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
249     ClassOfMsg rmcpMsgClass;    // Class of Message
250 #ifdef RMCP_PING
251     uint8_t asfMsgTag; // ASF Message Tag
252 #endif                 // RMCP_PING
253 
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 
265 } // namespace message
266