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