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