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