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