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 <phosphor-logging/log.hpp>
12 
13 #include <memory>
14 #include <string>
15 #include <vector>
16 
17 using namespace phosphor::logging;
18 
19 namespace message
20 {
21 using namespace phosphor::logging;
22 
23 bool Handler::receive()
24 {
25     std::vector<uint8_t> packet;
26     auto readStatus = 0;
27 
28     // Read the packet
29     std::tie(readStatus, packet) = channel->read();
30 
31     // Read of the packet failed
32     if (readStatus < 0)
33     {
34         log<level::ERR>("Error in Read", entry("STATUS=%x", readStatus));
35         return false;
36     }
37 
38     // Unflatten the packet
39     std::tie(inMessage, sessionHeader) = parser::unflatten(packet);
40 
41     return true;
42 }
43 
44 void Handler::updSessionData(std::shared_ptr<Message>& inMessage)
45 {
46     session = session::Manager::get().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     // Get the CommandID to map into the command table
113     auto command = inMessage->getCommand();
114     if (inMessage->payloadType == PayloadType::IPMI)
115     {
116         // Process PayloadType::IPMI only if ipmi is enabled or for sessionless
117         // or for session establisbment command
118         if (this->sessionID == session::sessionZero ||
119             session->sessionUserPrivAccess.ipmiEnabled)
120         {
121             if (inMessage->payload.size() <
122                 (sizeof(LAN::header::Request) + sizeof(LAN::trailer::Request)))
123             {
124                 return;
125             }
126 
127             auto start =
128                 inMessage->payload.begin() + sizeof(LAN::header::Request);
129             auto end = inMessage->payload.end() - sizeof(LAN::trailer::Request);
130             std::vector<uint8_t> inPayload(start, end);
131             command::Table::get().executeCommand(command, inPayload,
132                                                  shared_from_this());
133         }
134         else
135         {
136             std::vector<uint8_t> payload{IPMI_CC_INSUFFICIENT_PRIVILEGE};
137             outPayload = std::move(payload);
138         }
139     }
140     else
141     {
142         command::Table::get().executeCommand(command, inMessage->payload,
143                                              shared_from_this());
144     }
145 }
146 
147 void Handler::writeData(const std::vector<uint8_t>& packet)
148 {
149     auto writeStatus = channel->write(packet);
150     if (writeStatus < 0)
151     {
152         throw std::runtime_error("Error in writing to socket");
153     }
154 }
155 
156 #ifdef RMCP_PING
157 void Handler::sendASF()
158 {
159     // Flatten the packet
160     auto packet = asfparser::flatten(inMessage->asfMsgTag);
161 
162     // Write the packet
163     writeData(packet);
164 }
165 #endif // RMCP_PING
166 
167 void Handler::send(std::shared_ptr<Message> outMessage)
168 {
169     // Flatten the packet
170     auto packet = parser::flatten(outMessage, sessionHeader, session);
171 
172     // Write the packet
173     writeData(packet);
174 }
175 
176 void Handler::setChannelInSession() const
177 {
178     session->channelPtr = channel;
179 }
180 
181 void Handler::sendSOLPayload(const std::vector<uint8_t>& input)
182 {
183     auto outMessage = std::make_shared<Message>();
184     outMessage->payloadType = PayloadType::SOL;
185     outMessage->payload = input;
186     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
187     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
188     outMessage->rcSessionID = session->getRCSessionID();
189     outMessage->bmcSessionID = sessionID;
190 
191     send(outMessage);
192 }
193 
194 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn, uint8_t cmd,
195                                          const std::vector<uint8_t>& output)
196 {
197     auto outMessage = std::make_shared<Message>();
198     outMessage->payloadType = PayloadType::IPMI;
199     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
200     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
201     outMessage->rcSessionID = session->getRCSessionID();
202     outMessage->bmcSessionID = sessionID;
203 
204     outMessage->payload.resize(sizeof(LAN::header::Request) + output.size() +
205                                sizeof(LAN::trailer::Request));
206 
207     auto respHeader =
208         reinterpret_cast<LAN::header::Request*>(outMessage->payload.data());
209 
210     // Add IPMI LAN Message Request Header
211     respHeader->rsaddr = LAN::requesterBMCAddress;
212     respHeader->netfn = (netfn << 0x02);
213     respHeader->cs = crc8bit(&(respHeader->rsaddr), 2);
214     respHeader->rqaddr = LAN::responderBMCAddress;
215     respHeader->rqseq = 0;
216     respHeader->cmd = cmd;
217 
218     auto assembledSize = sizeof(LAN::header::Request);
219 
220     // Copy the output by the execution of the command
221     std::copy(output.begin(), output.end(),
222               outMessage->payload.begin() + assembledSize);
223     assembledSize += output.size();
224 
225     // Add the IPMI LAN Message Trailer
226     auto trailer = reinterpret_cast<LAN::trailer::Request*>(
227         outMessage->payload.data() + assembledSize);
228 
229     // Calculate the checksum for the field rqaddr in the header to the
230     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
231     // netfn, cs).
232     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
233 
234     send(outMessage);
235 }
236 
237 } // namespace message
238