xref: /openbmc/phosphor-net-ipmid/message_handler.cpp (revision f8a34fc47183c553b9e78301e6f55170dbb7976d)
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 <memory>
12 #include <phosphor-logging/log.hpp>
13 #include <string>
14 #include <vector>
15 
16 using namespace phosphor::logging;
17 
18 namespace message
19 {
20 using namespace phosphor::logging;
21 
22 bool Handler::receive()
23 {
24     std::vector<uint8_t> packet;
25     auto readStatus = 0;
26 
27     // Read the packet
28     std::tie(readStatus, packet) = channel->read();
29 
30     // Read of the packet failed
31     if (readStatus < 0)
32     {
33         log<level::ERR>("Error in Read", entry("STATUS=%x", readStatus));
34         return false;
35     }
36 
37     // Unflatten the packet
38     std::tie(inMessage, sessionHeader) = parser::unflatten(packet);
39 
40     auto session = std::get<session::Manager&>(singletonPool)
41                        .getSession(inMessage->bmcSessionID);
42 
43     sessionID = inMessage->bmcSessionID;
44     inMessage->rcSessionID = session->getRCSessionID();
45     session->updateLastTransactionTime();
46     session->channelPtr = channel;
47     session->remotePort(channel->getPort());
48 
49     return true;
50 }
51 
52 Handler::~Handler()
53 {
54     if (outPayload)
55     {
56         std::shared_ptr<Message> outMessage =
57             inMessage->createResponse(*outPayload);
58         if (!outMessage)
59         {
60             return;
61         }
62         try
63         {
64             send(outMessage);
65         }
66         catch (const std::exception& e)
67         {
68             // send failed, most likely due to a session closure
69             log<level::INFO>("Async RMCP+ reply failed",
70                              entry("EXCEPTION=%s", e.what()));
71         }
72     }
73 }
74 
75 void Handler::processIncoming()
76 {
77     // Read the incoming IPMI packet
78     if (!receive())
79     {
80         return;
81     }
82 
83     // Execute the Command, possibly asynchronously
84     executeCommand();
85 
86     // send happens during the destructor if a payload was set
87 }
88 
89 void Handler::executeCommand()
90 {
91     // Get the CommandID to map into the command table
92     auto command = inMessage->getCommand();
93     if (inMessage->payloadType == PayloadType::IPMI)
94     {
95         auto session =
96             std::get<session::Manager&>(singletonPool).getSession(sessionID);
97         // Process PayloadType::IPMI only if ipmi is enabled or for sessionless
98         // or for session establisbment command
99         if (this->sessionID == session::sessionZero ||
100             session->sessionUserPrivAccess.ipmiEnabled)
101         {
102             if (inMessage->payload.size() <
103                 (sizeof(LAN::header::Request) + sizeof(LAN::trailer::Request)))
104             {
105                 return;
106             }
107 
108             auto start =
109                 inMessage->payload.begin() + sizeof(LAN::header::Request);
110             auto end = inMessage->payload.end() - sizeof(LAN::trailer::Request);
111             std::vector<uint8_t> inPayload(start, end);
112             std::get<command::Table&>(singletonPool)
113                 .executeCommand(command, inPayload, shared_from_this());
114         }
115         else
116         {
117             std::vector<uint8_t> payload{IPMI_CC_INSUFFICIENT_PRIVILEGE};
118             outPayload = std::move(payload);
119         }
120     }
121     else
122     {
123         std::get<command::Table&>(singletonPool)
124             .executeCommand(command, inMessage->payload, shared_from_this());
125     }
126 }
127 
128 void Handler::send(std::shared_ptr<Message> outMessage)
129 {
130     auto session =
131         std::get<session::Manager&>(singletonPool).getSession(sessionID);
132 
133     // Flatten the packet
134     auto packet = parser::flatten(outMessage, sessionHeader, session);
135 
136     // Write the packet
137     auto writeStatus = channel->write(packet);
138     if (writeStatus < 0)
139     {
140         throw std::runtime_error("Error in writing to socket");
141     }
142 }
143 
144 void Handler::setChannelInSession() const
145 {
146     auto session =
147         std::get<session::Manager&>(singletonPool).getSession(sessionID);
148 
149     session->channelPtr = channel;
150 }
151 
152 void Handler::sendSOLPayload(const std::vector<uint8_t>& input)
153 {
154     auto session =
155         std::get<session::Manager&>(singletonPool).getSession(sessionID);
156 
157     auto outMessage = std::make_shared<Message>();
158     outMessage->payloadType = PayloadType::SOL;
159     outMessage->payload = input;
160     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
161     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
162     outMessage->rcSessionID = session->getRCSessionID();
163     outMessage->bmcSessionID = sessionID;
164 
165     send(outMessage);
166 }
167 
168 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn, uint8_t cmd,
169                                          const std::vector<uint8_t>& output)
170 {
171     auto session =
172         std::get<session::Manager&>(singletonPool).getSession(sessionID);
173 
174     auto outMessage = std::make_shared<Message>();
175     outMessage->payloadType = PayloadType::IPMI;
176     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
177     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
178     outMessage->rcSessionID = session->getRCSessionID();
179     outMessage->bmcSessionID = sessionID;
180 
181     outMessage->payload.resize(sizeof(LAN::header::Request) + output.size() +
182                                sizeof(LAN::trailer::Request));
183 
184     auto respHeader =
185         reinterpret_cast<LAN::header::Request*>(outMessage->payload.data());
186 
187     // Add IPMI LAN Message Request Header
188     respHeader->rsaddr = LAN::requesterBMCAddress;
189     respHeader->netfn = (netfn << 0x02);
190     respHeader->cs = crc8bit(&(respHeader->rsaddr), 2);
191     respHeader->rqaddr = LAN::responderBMCAddress;
192     respHeader->rqseq = 0;
193     respHeader->cmd = cmd;
194 
195     auto assembledSize = sizeof(LAN::header::Request);
196 
197     // Copy the output by the execution of the command
198     std::copy(output.begin(), output.end(),
199               outMessage->payload.begin() + assembledSize);
200     assembledSize += output.size();
201 
202     // Add the IPMI LAN Message Trailer
203     auto trailer = reinterpret_cast<LAN::trailer::Request*>(
204         outMessage->payload.data() + assembledSize);
205 
206     // Calculate the checksum for the field rqaddr in the header to the
207     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
208     // netfn, cs).
209     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
210 
211     send(outMessage);
212 }
213 
214 } // namespace message
215