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