1 #include "command_table.hpp"
2 
3 #include "main.hpp"
4 #include "message_handler.hpp"
5 #include "message_parsers.hpp"
6 #include "sessions_manager.hpp"
7 
8 #include <ipmid/types.hpp>
9 #include <main.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/log.hpp>
12 #include <user_channel/user_layer.hpp>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 #include <iomanip>
16 
17 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
18 using namespace phosphor::logging;
19 
20 namespace command
21 {
22 
23 void Table::registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry)
24 {
25     auto& command = commandTable[inCommand.command];
26 
27     if (command)
28     {
29         log<level::DEBUG>(
30             "Already Registered",
31             phosphor::logging::entry("SKIPPED_ENTRY=0x%x", inCommand.command));
32         return;
33     }
34 
35     command = std::move(entry);
36 }
37 
38 void Table::executeCommand(uint32_t inCommand,
39                            std::vector<uint8_t>& commandData,
40                            std::shared_ptr<message::Handler> handler)
41 {
42     using namespace std::chrono_literals;
43 
44     auto iterator = commandTable.find(inCommand);
45 
46     if (iterator == commandTable.end())
47     {
48         CommandID command(inCommand);
49 
50         // Do not forward any session zero commands to ipmid
51         if (handler->sessionID == session::sessionZero)
52         {
53             log<level::INFO>("Table: refuse to forward session-zero command",
54                              entry("LUN=%x", command.lun()),
55                              entry("NETFN=%x", command.netFn()),
56                              entry("CMD=%x", command.cmd()));
57             return;
58         }
59         std::shared_ptr<session::Session> session =
60             session::Manager::get().getSession(handler->sessionID);
61 
62         // Ignore messages that are not part of an active session
63         auto state = static_cast<session::State>(session->state());
64         if (state != session::State::active)
65         {
66             return;
67         }
68 
69         auto bus = getSdBus();
70         // forward the request onto the main ipmi queue
71         using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
72                                            std::vector<uint8_t>>;
73         uint8_t lun = command.lun();
74         uint8_t netFn = command.netFn();
75         uint8_t cmd = command.cmd();
76 
77         std::map<std::string, ipmi::Value> options = {
78             {"userId", ipmi::Value(static_cast<int>(
79                            ipmi::ipmiUserGetUserId(session->userName)))},
80             {"privilege",
81              ipmi::Value(static_cast<int>(session->currentPrivilege()))},
82             {"currentSessionId",
83              ipmi::Value(static_cast<uint32_t>(session->getBMCSessionID()))},
84         };
85         bus->async_method_call(
86             [handler, this](const boost::system::error_code& ec,
87                             const IpmiDbusRspType& response) {
88                 if (!ec)
89                 {
90                     const uint8_t& cc = std::get<3>(response);
91                     const std::vector<uint8_t>& responseData =
92                         std::get<4>(response);
93                     std::vector<uint8_t> payload;
94                     payload.reserve(1 + responseData.size());
95                     payload.push_back(cc);
96                     payload.insert(payload.end(), responseData.begin(),
97                                    responseData.end());
98                     handler->outPayload = std::move(payload);
99                 }
100                 else
101                 {
102                     std::vector<uint8_t> payload;
103                     payload.push_back(IPMI_CC_UNSPECIFIED_ERROR);
104                     handler->outPayload = std::move(payload);
105                 }
106             },
107             "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
108             "xyz.openbmc_project.Ipmi.Server", "execute", netFn, lun, cmd,
109             commandData, options);
110     }
111     else
112     {
113         auto start = std::chrono::steady_clock::now();
114 
115         // Ignore messages that are not part of an active/pre-active session
116         if (handler->sessionID != session::sessionZero)
117         {
118             std::shared_ptr<session::Session> session =
119                 session::Manager::get().getSession(handler->sessionID);
120             auto state = static_cast<session::State>(session->state());
121             if ((state != session::State::setupInProgress) &&
122                 (state != session::State::active))
123             {
124                 return;
125             }
126         }
127 
128         handler->outPayload =
129             iterator->second->executeCommand(commandData, handler);
130 
131         auto end = std::chrono::steady_clock::now();
132 
133         std::chrono::duration<size_t> elapsedSeconds =
134             std::chrono::duration_cast<std::chrono::seconds>(end - start);
135 
136         // If command time execution time exceeds 2 seconds, log a time
137         // exceeded message
138         if (elapsedSeconds > 2s)
139         {
140             log<level::ERR>("IPMI command timed out",
141                             entry("DELAY=%zu", elapsedSeconds.count()));
142         }
143     }
144 }
145 
146 std::vector<uint8_t>
147     NetIpmidEntry::executeCommand(std::vector<uint8_t>& commandData,
148                                   std::shared_ptr<message::Handler> handler)
149 {
150     std::vector<uint8_t> errResponse;
151 
152     // Check if the command qualifies to be run prior to establishing a session
153     if (!sessionless && (handler->sessionID == session::sessionZero))
154     {
155         errResponse.resize(1);
156         errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE;
157         log<level::INFO>("Table: Insufficient privilege for command",
158                          entry("LUN=%x", command.lun()),
159                          entry("NETFN=%x", command.netFn()),
160                          entry("CMD=%x", command.cmd()));
161         return errResponse;
162     }
163 
164     return functor(commandData, handler);
165 }
166 
167 } // namespace command
168