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