xref: /openbmc/phosphor-net-ipmid/message_handler.cpp (revision d999ffc12211f5d5c6cd2196dd525e69e2fc0f58)
1 #include "message_handler.hpp"
2 
3 #include "command_table.hpp"
4 #include "main.hpp"
5 #include "message.hpp"
6 #include "message_parsers.hpp"
7 #include "sessions_manager.hpp"
8 
9 #include <sys/socket.h>
10 
11 #include <iostream>
12 #include <memory>
13 #include <string>
14 #include <vector>
15 
16 namespace message
17 {
18 
19 std::shared_ptr<Message> Handler::receive()
20 {
21     std::vector<uint8_t> packet;
22     auto readStatus = 0;
23 
24     // Read the packet
25     std::tie(readStatus, packet) = channel->read();
26 
27     // Read of the packet failed
28     if (readStatus < 0)
29     {
30         std::cerr << "E> Error in Read : " << std::hex << readStatus << "\n";
31         return nullptr;
32     }
33 
34     // Unflatten the packet
35     std::shared_ptr<Message> message;
36     std::tie(message, sessionHeader) = parser::unflatten(packet);
37 
38     auto session = std::get<session::Manager&>(singletonPool)
39                        .getSession(message->bmcSessionID);
40 
41     sessionID = message->bmcSessionID;
42     message->rcSessionID = session->getRCSessionID();
43     session->updateLastTransactionTime();
44 
45     return message;
46 }
47 
48 template <>
49 std::shared_ptr<Message> Handler::createResponse<PayloadType::IPMI>(
50     std::vector<uint8_t>& output, std::shared_ptr<Message> inMessage)
51 {
52     auto outMessage = std::make_shared<Message>();
53     outMessage->payloadType = PayloadType::IPMI;
54 
55     outMessage->payload.resize(sizeof(LAN::header::Response) + output.size() +
56                                sizeof(LAN::trailer::Response));
57 
58     auto reqHeader =
59         reinterpret_cast<LAN::header::Request*>(inMessage->payload.data());
60     auto respHeader =
61         reinterpret_cast<LAN::header::Response*>(outMessage->payload.data());
62 
63     // Add IPMI LAN Message Response Header
64     respHeader->rqaddr = reqHeader->rqaddr;
65     respHeader->netfn = reqHeader->netfn | 0x04;
66     respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
67     respHeader->rsaddr = reqHeader->rsaddr;
68     respHeader->rqseq = reqHeader->rqseq;
69     respHeader->cmd = reqHeader->cmd;
70 
71     auto assembledSize = sizeof(LAN::header::Response);
72 
73     // Copy the output by the execution of the command
74     std::copy(output.begin(), output.end(),
75               outMessage->payload.begin() + assembledSize);
76     assembledSize += output.size();
77 
78     // Add the IPMI LAN Message Trailer
79     auto trailer = reinterpret_cast<LAN::trailer::Response*>(
80         outMessage->payload.data() + assembledSize);
81     trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
82 
83     return outMessage;
84 }
85 
86 std::shared_ptr<Message>
87     Handler::executeCommand(std::shared_ptr<Message> inMessage)
88 {
89     // Get the CommandID to map into the command table
90     auto command = getCommand(inMessage);
91     std::vector<uint8_t> output{};
92 
93     if (inMessage->payloadType == PayloadType::IPMI)
94     {
95         if (inMessage->payload.size() <
96             (sizeof(LAN::header::Request) + sizeof(LAN::trailer::Request)))
97         {
98             return nullptr;
99         }
100 
101         auto start = inMessage->payload.begin() + sizeof(LAN::header::Request);
102         auto end = inMessage->payload.end() - sizeof(LAN::trailer::Request);
103         std::vector<uint8_t> inPayload(start, end);
104 
105         output = std::get<command::Table&>(singletonPool)
106                      .executeCommand(command, inPayload, *this);
107     }
108     else
109     {
110         output = std::get<command::Table&>(singletonPool)
111                      .executeCommand(command, inMessage->payload, *this);
112     }
113 
114     std::shared_ptr<Message> outMessage = nullptr;
115 
116     switch (inMessage->payloadType)
117     {
118         case PayloadType::IPMI:
119             outMessage = createResponse<PayloadType::IPMI>(output, inMessage);
120             break;
121         case PayloadType::OPEN_SESSION_REQUEST:
122             outMessage = createResponse<PayloadType::OPEN_SESSION_RESPONSE>(
123                 output, inMessage);
124             break;
125         case PayloadType::RAKP1:
126             outMessage = createResponse<PayloadType::RAKP2>(output, inMessage);
127             break;
128         case PayloadType::RAKP3:
129             outMessage = createResponse<PayloadType::RAKP4>(output, inMessage);
130             break;
131         case PayloadType::SOL:
132             return outMessage;
133             break;
134         default:
135             break;
136     }
137 
138     outMessage->isPacketEncrypted = inMessage->isPacketEncrypted;
139     outMessage->isPacketAuthenticated = inMessage->isPacketAuthenticated;
140     outMessage->rcSessionID = inMessage->rcSessionID;
141     outMessage->bmcSessionID = inMessage->bmcSessionID;
142 
143     return outMessage;
144 }
145 
146 uint32_t Handler::getCommand(std::shared_ptr<Message> message)
147 {
148     uint32_t command = 0;
149 
150     command |= (static_cast<uint8_t>(message->payloadType) << 16);
151     if (message->payloadType == PayloadType::IPMI)
152     {
153         command |=
154             ((reinterpret_cast<LAN::header::Request*>(message->payload.data()))
155                  ->netfn)
156             << 8;
157         command |=
158             (reinterpret_cast<LAN::header::Request*>(message->payload.data()))
159                 ->cmd;
160     }
161 
162     return command;
163 }
164 
165 void Handler::send(std::shared_ptr<Message> outMessage)
166 {
167     auto session =
168         std::get<session::Manager&>(singletonPool).getSession(sessionID);
169 
170     // Flatten the packet
171     auto packet = parser::flatten(outMessage, sessionHeader, *session);
172 
173     // Write the packet
174     auto writeStatus = channel->write(packet);
175     if (writeStatus < 0)
176     {
177         throw std::runtime_error("Error in writing to socket");
178     }
179 }
180 
181 void Handler::setChannelInSession() const
182 {
183     auto session =
184         std::get<session::Manager&>(singletonPool).getSession(sessionID);
185 
186     session->channelPtr = channel;
187 }
188 
189 void Handler::sendSOLPayload(const std::vector<uint8_t>& input)
190 {
191     auto session =
192         std::get<session::Manager&>(singletonPool).getSession(sessionID);
193 
194     auto outMessage = std::make_shared<Message>();
195     outMessage->payloadType = PayloadType::SOL;
196     outMessage->payload = input;
197     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
198     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
199     outMessage->rcSessionID = session->getRCSessionID();
200     outMessage->bmcSessionID = sessionID;
201 
202     send(outMessage);
203 }
204 
205 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn, uint8_t cmd,
206                                          const std::vector<uint8_t>& output)
207 {
208     auto session =
209         std::get<session::Manager&>(singletonPool).getSession(sessionID);
210 
211     auto outMessage = std::make_shared<Message>();
212     outMessage->payloadType = PayloadType::IPMI;
213     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
214     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
215     outMessage->rcSessionID = session->getRCSessionID();
216     outMessage->bmcSessionID = sessionID;
217 
218     outMessage->payload.resize(sizeof(LAN::header::Request) + output.size() +
219                                sizeof(LAN::trailer::Request));
220 
221     auto respHeader =
222         reinterpret_cast<LAN::header::Request*>(outMessage->payload.data());
223 
224     // Add IPMI LAN Message Request Header
225     respHeader->rsaddr = LAN::requesterBMCAddress;
226     respHeader->netfn = (netfn << 0x02);
227     respHeader->cs = crc8bit(&(respHeader->rsaddr), 2);
228     respHeader->rqaddr = LAN::responderBMCAddress;
229     respHeader->rqseq = 0;
230     respHeader->cmd = cmd;
231 
232     auto assembledSize = sizeof(LAN::header::Request);
233 
234     // Copy the output by the execution of the command
235     std::copy(output.begin(), output.end(),
236               outMessage->payload.begin() + assembledSize);
237     assembledSize += output.size();
238 
239     // Add the IPMI LAN Message Trailer
240     auto trailer = reinterpret_cast<LAN::trailer::Request*>(
241         outMessage->payload.data() + assembledSize);
242 
243     // Calculate the checksum for the field rqaddr in the header to the
244     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
245     // netfn, cs).
246     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
247 
248     send(outMessage);
249 }
250 
251 } // namespace message
252