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         if (inMessage->payload.size() <
94             (sizeof(LAN::header::Request) + sizeof(LAN::trailer::Request)))
95         {
96             return;
97         }
98 
99         auto start = inMessage->payload.begin() + sizeof(LAN::header::Request);
100         auto end = inMessage->payload.end() - sizeof(LAN::trailer::Request);
101         std::vector<uint8_t> inPayload(start, end);
102         std::get<command::Table&>(singletonPool)
103             .executeCommand(command, inPayload, shared_from_this());
104     }
105     else
106     {
107         std::get<command::Table&>(singletonPool)
108             .executeCommand(command, inMessage->payload, shared_from_this());
109     }
110 }
111 
112 void Handler::send(std::shared_ptr<Message> outMessage)
113 {
114     auto session =
115         std::get<session::Manager&>(singletonPool).getSession(sessionID);
116 
117     // Flatten the packet
118     auto packet = parser::flatten(outMessage, sessionHeader, session);
119 
120     // Write the packet
121     auto writeStatus = channel->write(packet);
122     if (writeStatus < 0)
123     {
124         throw std::runtime_error("Error in writing to socket");
125     }
126 }
127 
128 void Handler::setChannelInSession() const
129 {
130     auto session =
131         std::get<session::Manager&>(singletonPool).getSession(sessionID);
132 
133     session->channelPtr = channel;
134 }
135 
136 void Handler::sendSOLPayload(const std::vector<uint8_t>& input)
137 {
138     auto session =
139         std::get<session::Manager&>(singletonPool).getSession(sessionID);
140 
141     auto outMessage = std::make_shared<Message>();
142     outMessage->payloadType = PayloadType::SOL;
143     outMessage->payload = input;
144     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
145     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
146     outMessage->rcSessionID = session->getRCSessionID();
147     outMessage->bmcSessionID = sessionID;
148 
149     send(outMessage);
150 }
151 
152 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn, uint8_t cmd,
153                                          const std::vector<uint8_t>& output)
154 {
155     auto session =
156         std::get<session::Manager&>(singletonPool).getSession(sessionID);
157 
158     auto outMessage = std::make_shared<Message>();
159     outMessage->payloadType = PayloadType::IPMI;
160     outMessage->isPacketEncrypted = session->isCryptAlgoEnabled();
161     outMessage->isPacketAuthenticated = session->isIntegrityAlgoEnabled();
162     outMessage->rcSessionID = session->getRCSessionID();
163     outMessage->bmcSessionID = sessionID;
164 
165     outMessage->payload.resize(sizeof(LAN::header::Request) + output.size() +
166                                sizeof(LAN::trailer::Request));
167 
168     auto respHeader =
169         reinterpret_cast<LAN::header::Request*>(outMessage->payload.data());
170 
171     // Add IPMI LAN Message Request Header
172     respHeader->rsaddr = LAN::requesterBMCAddress;
173     respHeader->netfn = (netfn << 0x02);
174     respHeader->cs = crc8bit(&(respHeader->rsaddr), 2);
175     respHeader->rqaddr = LAN::responderBMCAddress;
176     respHeader->rqseq = 0;
177     respHeader->cmd = cmd;
178 
179     auto assembledSize = sizeof(LAN::header::Request);
180 
181     // Copy the output by the execution of the command
182     std::copy(output.begin(), output.end(),
183               outMessage->payload.begin() + assembledSize);
184     assembledSize += output.size();
185 
186     // Add the IPMI LAN Message Trailer
187     auto trailer = reinterpret_cast<LAN::trailer::Request*>(
188         outMessage->payload.data() + assembledSize);
189 
190     // Calculate the checksum for the field rqaddr in the header to the
191     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
192     // netfn, cs).
193     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
194 
195     send(outMessage);
196 }
197 
198 } // namespace message
199