Command Router to handle commands
This class encapsulates the details regarding the execution of the
command and has routines for registering and execution of
the command.
Change-Id: Idf26e1f60f9f89aa5261fde89577ec7ddc26c096
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/command_table.cpp b/command_table.cpp
new file mode 100644
index 0000000..6550160
--- /dev/null
+++ b/command_table.cpp
@@ -0,0 +1,80 @@
+#include "command_table.hpp"
+
+#include <iomanip>
+#include <iostream>
+
+#include <host-ipmid/ipmid-api.h>
+#include "message_handler.hpp"
+#include "sessions_manager.hpp"
+
+namespace command
+{
+
+void Table::registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry)
+{
+ std::cout << "I> Registering Command" << std::hex
+ << inCommand.command << "\n";
+
+ commandTable[inCommand.command] = std::move(entry);
+}
+
+std::vector<uint8_t> Table::executeCommand(uint32_t inCommand,
+ std::vector<uint8_t>& commandData,
+ const message::Handler& handler)
+{
+ using namespace std::chrono_literals;
+
+ std::vector<uint8_t> response;
+
+ auto iterator = commandTable.find(inCommand);
+
+ if (iterator == commandTable.end())
+ {
+ std::cerr << "E> Table:: Command Not found: 0x" << std::hex << inCommand
+ << "\n";
+
+ response.resize(1);
+ response[0] = IPMI_CC_INVALID;
+ }
+ else
+ {
+ auto start = std::chrono::steady_clock::now();
+
+ response = iterator->second->executeCommand(commandData, handler);
+
+ auto end = std::chrono::steady_clock::now();
+
+ auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>
+ (end - start);
+
+ // If command time execution time exceeds 2 seconds, log a time
+ // exceeded message
+ if (elapsedSeconds > 2s)
+ {
+ std::cerr << "E> IPMI command timed out:Elapsed time = "
+ << elapsedSeconds.count() << "s" << "\n";
+ }
+ }
+ return response;
+}
+
+std::vector<uint8_t> NetIpmidEntry::executeCommand(
+ std::vector<uint8_t>& commandData,
+ const message::Handler& handler)
+{
+ std::vector<uint8_t> errResponse;
+
+ // Check if the command qualifies to be run prior to establishing a session
+ if (!sessionless && (handler.sessionID == session::SESSION_ZERO))
+ {
+ errResponse.resize(1);
+ errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE;
+ std::cerr << "E> Table::Not enough privileges for command 0x"
+ << std::hex << command.command << "\n";
+ return errResponse;
+ }
+
+ return functor(commandData, handler);
+}
+
+} // namespace command
diff --git a/command_table.hpp b/command_table.hpp
new file mode 100644
index 0000000..40e8020
--- /dev/null
+++ b/command_table.hpp
@@ -0,0 +1,255 @@
+#pragma once
+
+#include <functional>
+#include <map>
+
+#include "message_handler.hpp"
+
+namespace command
+{
+
+union CommandID
+{
+ uint32_t command;
+
+ uint8_t reserved;
+ message::PayloadType payloadType;
+
+ union
+ {
+ uint8_t netFn: 6;
+ uint8_t lun: 2;
+
+ uint8_t netFnLun;
+ } NetFnLun;
+
+ uint8_t cmd;
+} __attribute__((packed));
+
+/*
+ * CommandFunctor is the functor register for commands defined in
+ * phosphor-net-ipmid. This would take the request part of the command as a
+ * vector and a reference to the message handler. The response part of the
+ * command is returned as a vector.
+ */
+using CommandFunctor = std::function<std::vector<uint8_t>(
+ std::vector<uint8_t>&, const message::Handler&)>;
+
+/*
+ * @struct CmdDetails
+ *
+ * Command details is used to register commands supported in phosphor-net-ipmid.
+ */
+struct CmdDetails
+{
+ CommandID command;
+ CommandFunctor functor;
+ session::Privilege privilege;
+ bool sessionless;
+};
+
+/*
+ * @enum NetFns
+ *
+ * A field that identifies the functional class of the message. The Network
+ * Function clusters IPMI commands into different sets.
+ */
+enum class NetFns
+{
+ CHASSIS = (0x00 << 10),
+ CHASSIS_RESP = (0x01 << 10),
+
+ BRIDGE = (0x02 << 10),
+ BRIDGE_RESP = (0x03 << 10),
+
+ SENSOR = (0x04 << 10),
+ SENSOR_RESP = (0x05 << 10),
+ EVENT = (0x04 << 10),
+ EVENT_RESP = (0x05 << 10),
+
+ APP = (0x06 << 10),
+ APP_RESP = (0x07 << 10),
+
+ FIRMWARE = (0x08 << 10),
+ FIRMWARE_RESP = (0x09 << 10),
+
+ STORAGE = (0x0A << 10),
+ STORAGE_RESP = (0x0B << 10),
+
+ TRANSPORT = (0x0C << 10),
+ TRANSPORT_RESP = (0x0D << 10),
+
+ //>>
+ RESERVED_START = (0x0E << 10),
+ RESERVED_END = (0x2B << 10),
+ //<<
+
+ GROUP_EXTN = (0x2C << 10),
+ GROUP_EXTN_RESP = (0x2D << 10),
+
+ OEM = (0x2E << 10),
+ OEM_RESP = (0x2F << 10),
+};
+
+/*
+ * @class Entry
+ *
+ * This is the base class for registering IPMI commands. There are two ways of
+ * registering commands to phosphor-net-ipmid, the session related commands and
+ * provider commands
+ *
+ * Every commands has a privilege level which mentions the minimum session
+ * privilege level needed to execute the command
+ */
+
+class Entry
+{
+
+ public:
+ Entry(CommandID command, session::Privilege privilege):
+ command(command),
+ privilege(privilege) {}
+
+ /**
+ * @brief Execute the command
+ *
+ * Execute the command
+ *
+ * @param[in] commandData - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+ virtual std::vector<uint8_t> executeCommand(
+ std::vector<uint8_t>& commandData,
+ const message::Handler& handler) = 0;
+
+ auto getCommand() const
+ {
+ return command;
+ }
+
+ auto getPrivilege() const
+ {
+ return privilege;
+ }
+
+ virtual ~Entry() = default;
+ Entry(const Entry&) = default;
+ Entry& operator=(const Entry&) = default;
+ Entry(Entry&&) = default;
+ Entry& operator=(Entry&&) = default;
+
+ protected:
+ CommandID command;
+
+ //Specifies the minimum privilege level required to execute this command
+ session::Privilege privilege;
+};
+
+/*
+ * @class NetIpmidEntry
+ *
+ * NetIpmidEntry is used to register commands that are consumed only in
+ * phosphor-net-ipmid. The RAKP commands, session commands and user management
+ * commands are examples of this.
+ *
+ * There are certain IPMI commands that can be executed before session can be
+ * established like Get System GUID, Get Channel Authentication Capabilities
+ * and RAKP commands.
+ */
+class NetIpmidEntry final: public Entry
+{
+
+ public:
+ NetIpmidEntry(CommandID command,
+ CommandFunctor functor,
+ session::Privilege privilege,
+ bool sessionless):
+ Entry(command, privilege),
+ functor(functor),
+ sessionless(sessionless) {}
+
+ /**
+ * @brief Execute the command
+ *
+ * Execute the command
+ *
+ * @param[in] commandData - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+ std::vector<uint8_t> executeCommand(std::vector<uint8_t>& commandData,
+ const message::Handler& handler)
+ override;
+
+ virtual ~NetIpmidEntry() = default;
+ NetIpmidEntry(const NetIpmidEntry&) = default;
+ NetIpmidEntry& operator=(const NetIpmidEntry&) = default;
+ NetIpmidEntry(NetIpmidEntry&&) = default;
+ NetIpmidEntry& operator=(NetIpmidEntry&&) = default;
+
+ private:
+ CommandFunctor functor;
+
+ bool sessionless;
+};
+
+/*
+ * @class Table
+ *
+ * Table keeps the IPMI command entries as a sorted associative container with
+ * Command ID as the unique key. It has interfaces for registering commands
+ * and executing a command.
+ */
+class Table
+{
+ public:
+
+ Table() = default;
+ ~Table() = default;
+ // Command Table is a singleton so copy, copy-assignment, move and
+ // move assignment is deleted
+ Table(const Table&) = delete;
+ Table& operator=(const Table&) = delete;
+ Table(Table&&) = default;
+ Table& operator=(Table&&) = default;
+
+ using CommandTable = std::map<uint32_t, std::unique_ptr<Entry>>;
+
+ /**
+ * @brief Register a command
+ *
+ * Register a command with the command table
+ *
+ * @param[in] inCommand - Command ID
+ * @param[in] entry - Command Entry
+ *
+ * @return: None
+ *
+ * @note Registering an already registered command will overwrite the
+ * existing entry with the new one.
+ */
+ void registerCommand(CommandID inCommand,
+ std::unique_ptr<Entry>&& entry);
+
+ /**
+ * @brief Execute the command
+ *
+ * Execute the command for the corresponding CommandID
+ *
+ * @param[in] inCommand - Command ID to execute.
+ * @param[in] commandData - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+ std::vector<uint8_t> executeCommand(uint32_t inCommand,
+ std::vector<uint8_t>& commandData,
+ const message::Handler& handler);
+ private:
+ CommandTable commandTable;
+};
+
+}// namespace command