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](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 =
123 iterator->second->executeCommand(commandData, 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
executeCommand(std::vector<uint8_t> & commandData,std::shared_ptr<message::Handler> handler)140 std::vector<uint8_t> NetIpmidEntry::executeCommand(
141 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