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