1 #include "message_handler.hpp"
2 
3 #include <sys/socket.h>
4 
5 #include <iostream>
6 #include <memory>
7 #include <string>
8 #include <vector>
9 
10 #include "command_table.hpp"
11 #include "main.hpp"
12 #include "message.hpp"
13 #include "message_parsers.hpp"
14 #include "sessions_manager.hpp"
15 
16 namespace message
17 {
18 
19 std::unique_ptr<Message> Handler::receive()
20 {
21     std::vector<uint8_t> packet;
22     auto readStatus = 0;
23 
24     // Read the packet
25     std::tie(readStatus, packet) = channel->read();
26 
27     // Read of the packet failed
28     if (readStatus < 0)
29     {
30         std::cerr << "E> Error in Read : " << std::hex << readStatus << "\n";
31         return nullptr;
32     }
33 
34     // Unflatten the packet
35     std::unique_ptr<Message> message;
36     std::tie(message, sessionHeader) = parser::unflatten(packet);
37 
38     auto session = (std::get<session::Manager&>(singletonPool).getSession(
39                    message->bmcSessionID)).lock();
40 
41     sessionID = message->bmcSessionID;
42     message->rcSessionID = session->getRCSessionID();
43     session->updateLastTransactionTime();
44 
45     return message;
46 }
47 
48 template<>
49 std::unique_ptr<Message> Handler::createResponse<PayloadType::IPMI>(
50         std::vector<uint8_t>& output, Message& inMessage)
51 {
52     auto outMessage = std::make_unique<Message>();
53     outMessage->payloadType = PayloadType::IPMI;
54 
55     outMessage->payload.resize(sizeof(LAN::header::Response) +
56                                output.size() +
57                                sizeof(LAN::trailer::Response));
58 
59     auto reqHeader = reinterpret_cast<LAN::header::Request*>
60                      (inMessage.payload.data());
61     auto respHeader = reinterpret_cast<LAN::header::Response*>
62                       (outMessage->payload.data());
63 
64     // Add IPMI LAN Message Response Header
65     respHeader->rqaddr = reqHeader->rqaddr;
66     respHeader->netfn  = reqHeader->netfn | 0x04;
67     respHeader->cs     = crc8bit(&(respHeader->rqaddr), 2);
68     respHeader->rsaddr = reqHeader->rsaddr;
69     respHeader->rqseq  = reqHeader->rqseq;
70     respHeader->cmd    = reqHeader->cmd;
71 
72     auto assembledSize = sizeof(LAN::header::Response);
73 
74     // Copy the output by the execution of the command
75     std::copy(output.begin(), output.end(),
76               outMessage->payload.begin() + assembledSize);
77     assembledSize += output.size();
78 
79     // Add the IPMI LAN Message Trailer
80     auto trailer = reinterpret_cast<LAN::trailer::Response*>
81                    (outMessage->payload.data() + assembledSize);
82     trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
83 
84     return outMessage;
85 }
86 
87 std::unique_ptr<Message> Handler::executeCommand(Message& inMessage)
88 {
89     // Get the CommandID to map into the command table
90     auto command = getCommand(inMessage);
91     std::vector<uint8_t> output{};
92 
93     if (inMessage.payloadType == PayloadType::IPMI)
94     {
95         if (inMessage.payload.size() < (sizeof(LAN::header::Request) +
96                                         sizeof(LAN::trailer::Request)))
97         {
98             return nullptr;
99         }
100 
101         auto start = inMessage.payload.begin() + sizeof(LAN::header::Request);
102         auto end = inMessage.payload.end() - sizeof(LAN::trailer::Request);
103         std::vector<uint8_t> inPayload(start, end);
104 
105         output = std::get<command::Table&>(singletonPool).executeCommand(
106                      command,
107                      inPayload,
108                      *this);
109     }
110     else
111     {
112         output = std::get<command::Table&>(singletonPool).executeCommand(
113                      command,
114                      inMessage.payload,
115                      *this);
116     }
117 
118     std::unique_ptr<Message> outMessage = nullptr;
119 
120     switch (inMessage.payloadType)
121     {
122         case PayloadType::IPMI:
123             outMessage = createResponse<PayloadType::IPMI>(output, inMessage);
124             break;
125         case PayloadType::OPEN_SESSION_REQUEST:
126             outMessage = createResponse<PayloadType::OPEN_SESSION_RESPONSE>(
127                              output, inMessage);
128             break;
129         case PayloadType::RAKP1:
130             outMessage = createResponse<PayloadType::RAKP2>(output, inMessage);
131             break;
132         case PayloadType::RAKP3:
133             outMessage = createResponse<PayloadType::RAKP4>(output, inMessage);
134             break;
135         case PayloadType::SOL:
136             return outMessage;
137             break;
138         default:
139             break;
140     }
141 
142     outMessage->isPacketEncrypted = inMessage.isPacketEncrypted;
143     outMessage->isPacketAuthenticated = inMessage.isPacketAuthenticated;
144     outMessage->rcSessionID = inMessage.rcSessionID;
145     outMessage->bmcSessionID = inMessage.bmcSessionID;
146 
147     return outMessage;
148 }
149 
150 uint32_t Handler::getCommand(Message& message)
151 {
152     uint32_t command = 0 ;
153 
154     command |= (static_cast<uint8_t>(message.payloadType) << 16);
155     if (message.payloadType == PayloadType::IPMI)
156     {
157         command |= ((reinterpret_cast<LAN::header::Request*>
158                      (message.payload.data()))->netfn) << 8;
159         command |= (reinterpret_cast<LAN::header::Request*>
160                     (message.payload.data()))->cmd;
161     }
162 
163     return command;
164 }
165 
166 int Handler::send(Message& outMessage)
167 {
168     auto session = (std::get<session::Manager&>(singletonPool).getSession(
169                     sessionID)).lock();
170 
171     // Flatten the packet
172     auto packet = parser::flatten(outMessage, sessionHeader, *session);
173 
174     // Read the packet
175     auto writeStatus = channel->write(packet);
176     if (writeStatus < 0)
177     {
178         std::cerr << "E> Error in writing : " << std::hex << writeStatus
179                   << "\n";
180     }
181 
182     return writeStatus;
183 }
184 
185 void Handler::setChannelInSession() const
186 {
187     auto session = (std::get<session::Manager&>(singletonPool).getSession(
188                     sessionID)).lock();
189 
190     session->channelPtr = channel;
191 }
192 
193 void Handler::sendSOLPayload(const sol::Buffer& input)
194 {
195     Message outMessage;
196 
197     auto session = (std::get<session::Manager&>(singletonPool).getSession(
198                     sessionID)).lock();
199 
200     outMessage.payloadType = PayloadType::SOL;
201     outMessage.payload = input;
202     outMessage.isPacketEncrypted = session->isCryptAlgoEnabled();
203     outMessage.isPacketAuthenticated = session->isIntegrityAlgoEnabled();
204     outMessage.rcSessionID = session->getRCSessionID();
205     outMessage.bmcSessionID = sessionID;
206 
207     send(outMessage);
208 }
209 
210 void Handler::sendUnsolicitedIPMIPayload(uint8_t netfn,
211                                          uint8_t cmd,
212                                          const std::vector<uint8_t>& output)
213 {
214     Message outMessage;
215 
216     auto session = (std::get<session::Manager&>(singletonPool).getSession(
217                     sessionID)).lock();
218 
219     outMessage.payloadType = PayloadType::IPMI;
220     outMessage.isPacketEncrypted = session->isCryptAlgoEnabled();
221     outMessage.isPacketAuthenticated = session->isIntegrityAlgoEnabled();
222     outMessage.rcSessionID = session->getRCSessionID();
223     outMessage.bmcSessionID = sessionID;
224 
225     outMessage.payload.resize(sizeof(LAN::header::Request) +
226                               output.size() +
227                               sizeof(LAN::trailer::Request));
228 
229     auto respHeader = reinterpret_cast<LAN::header::Request*>
230                       (outMessage.payload.data());
231 
232     // Add IPMI LAN Message Request Header
233     respHeader->rsaddr = LAN::requesterBMCAddress;
234     respHeader->netfn  = (netfn << 0x02);
235     respHeader->cs     = crc8bit(&(respHeader->rsaddr), 2);
236     respHeader->rqaddr = LAN::responderBMCAddress;
237     respHeader->rqseq  = 0;
238     respHeader->cmd    = cmd;
239 
240     auto assembledSize = sizeof(LAN::header::Request);
241 
242     // Copy the output by the execution of the command
243     std::copy(output.begin(),
244               output.end(),
245               outMessage.payload.begin() + assembledSize);
246     assembledSize += output.size();
247 
248     // Add the IPMI LAN Message Trailer
249     auto trailer = reinterpret_cast<LAN::trailer::Request*>
250                    (outMessage.payload.data() + assembledSize);
251 
252     // Calculate the checksum for the field rqaddr in the header to the
253     // command data, 3 corresponds to size of the fields before rqaddr( rsaddr,
254     // netfn, cs).
255     trailer->checksum = crc8bit(&respHeader->rqaddr, assembledSize - 3);
256 
257     send(outMessage);
258 }
259 
260 } //namespace message
261 
262