xref: /openbmc/phosphor-net-ipmid/message_handler.cpp (revision 2085ae0789275da639fdb2fc888d8464d8750ad0)
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     return true;
41 }
42 
43 void Handler::updSessionData(std::shared_ptr<Message>& inMessage)
44 {
45     auto session = session::Manager::get().getSession(inMessage->bmcSessionID);
46 
47     sessionID = inMessage->bmcSessionID;
48     inMessage->rcSessionID = session->getRCSessionID();
49     session->updateLastTransactionTime();
50     session->channelPtr = channel;
51     session->remotePort(channel->getPort());
52     uint32_t ipAddr = 0;
53     channel->getRemoteAddress(ipAddr);
54     session->remoteIPAddr(ipAddr);
55 }
56 
57 Handler::~Handler()
58 {
59     try
60     {
61 #ifdef RMCP_PING
62         if (ClassOfMsg::ASF == inMessage->rmcpMsgClass)
63         {
64             sendASF();
65         }
66         else
67 #endif // RMCP_PING
68         {
69             if (outPayload)
70             {
71                 std::shared_ptr<Message> outMessage =
72                     inMessage->createResponse(*outPayload);
73                 if (!outMessage)
74                 {
75                     return;
76                 }
77                 send(outMessage);
78             }
79         }
80     }
81     catch (const std::exception& e)
82     {
83         // send failed, most likely due to a session closure
84         log<level::INFO>("Async RMCP+ reply failed",
85                          entry("EXCEPTION=%s", e.what()));
86     }
87 }
88 
89 void Handler::processIncoming()
90 {
91     // Read the incoming IPMI packet
92     if (!receive())
93     {
94         return;
95     }
96 
97 #ifdef RMCP_PING
98     // Execute the Command, possibly asynchronously
99     if (ClassOfMsg::ASF != inMessage->rmcpMsgClass)
100 #endif // RMCP_PING
101     {
102         updSessionData(inMessage);
103         executeCommand();
104     }
105 
106     // send happens during the destructor if a payload was set
107 }
108 
109 void Handler::executeCommand()
110 {
111 
112     // Get the CommandID to map into the command table
113     auto command = inMessage->getCommand();
114     if (inMessage->payloadType == PayloadType::IPMI)
115     {
116         auto session = session::Manager::get().getSession(sessionID);
117         // Process PayloadType::IPMI only if ipmi is enabled or for sessionless
118         // or for session establisbment command
119         if (this->sessionID == session::sessionZero ||
120             session->sessionUserPrivAccess.ipmiEnabled)
121         {
122             if (inMessage->payload.size() <
123                 (sizeof(LAN::header::Request) + sizeof(LAN::trailer::Request)))
124             {
125                 return;
126             }
127 
128             auto start =
129                 inMessage->payload.begin() + sizeof(LAN::header::Request);
130             auto end = inMessage->payload.end() - sizeof(LAN::trailer::Request);
131             std::vector<uint8_t> inPayload(start, end);
132             command::Table::get().executeCommand(command, inPayload,
133                                                  shared_from_this());
134         }
135         else
136         {
137             std::vector<uint8_t> payload{IPMI_CC_INSUFFICIENT_PRIVILEGE};
138             outPayload = std::move(payload);
139         }
140     }
141     else
142     {
143         command::Table::get().executeCommand(command, inMessage->payload,
144                                              shared_from_this());
145     }
146 }
147 
148 void Handler::writeData(const std::vector<uint8_t>& packet)
149 {
150     auto writeStatus = channel->write(packet);
151     if (writeStatus < 0)
152     {
153         throw std::runtime_error("Error in writing to socket");
154     }
155 }
156 
157 #ifdef RMCP_PING
158 void Handler::sendASF()
159 {
160     // Flatten the packet
161     auto packet = asfparser::flatten(inMessage->asfMsgTag);
162 
163     // Write the packet
164     writeData(packet);
165 }
166 #endif // RMCP_PING
167 
168 void Handler::send(std::shared_ptr<Message> outMessage)
169 {
170     auto session = session::Manager::get().getSession(sessionID);
171 
172     // Flatten the packet
173     auto packet = parser::flatten(outMessage, sessionHeader, session);
174 
175     // Write the packet
176     writeData(packet);
177 }
178 
179 void Handler::setChannelInSession() const
180 {
181     auto session = session::Manager::get().getSession(sessionID);
182 
183     session->channelPtr = channel;
184 }
185 
186 void Handler::sendSOLPayload(const std::vector<uint8_t>& input)
187 {
188     auto session = session::Manager::get().getSession(sessionID);
189 
190     auto outMessage = std::make_shared<Message>();
191     outMessage->payloadType = PayloadType::SOL;
192     outMessage->payload = input;
193     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
194     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
195     outMessage->rcSessionID = session->getRCSessionID();
196     outMessage->bmcSessionID = sessionID;
197 
198     send(outMessage);
199 }
200 
201 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn, uint8_t cmd,
202                                          const std::vector<uint8_t>& output)
203 {
204     auto session = session::Manager::get().getSession(sessionID);
205 
206     auto outMessage = std::make_shared<Message>();
207     outMessage->payloadType = PayloadType::IPMI;
208     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
209     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
210     outMessage->rcSessionID = session->getRCSessionID();
211     outMessage->bmcSessionID = sessionID;
212 
213     outMessage->payload.resize(sizeof(LAN::header::Request) + output.size() +
214                                sizeof(LAN::trailer::Request));
215 
216     auto respHeader =
217         reinterpret_cast<LAN::header::Request*>(outMessage->payload.data());
218 
219     // Add IPMI LAN Message Request Header
220     respHeader->rsaddr = LAN::requesterBMCAddress;
221     respHeader->netfn = (netfn << 0x02);
222     respHeader->cs = crc8bit(&(respHeader->rsaddr), 2);
223     respHeader->rqaddr = LAN::responderBMCAddress;
224     respHeader->rqseq = 0;
225     respHeader->cmd = cmd;
226 
227     auto assembledSize = sizeof(LAN::header::Request);
228 
229     // Copy the output by the execution of the command
230     std::copy(output.begin(), output.end(),
231               outMessage->payload.begin() + assembledSize);
232     assembledSize += output.size();
233 
234     // Add the IPMI LAN Message Trailer
235     auto trailer = reinterpret_cast<LAN::trailer::Request*>(
236         outMessage->payload.data() + assembledSize);
237 
238     // Calculate the checksum for the field rqaddr in the header to the
239     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
240     // netfn, cs).
241     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
242 
243     send(outMessage);
244 }
245 
246 } // namespace message
247