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