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