diff options
Diffstat (limited to 'oemrouter.cpp')
-rw-r--r-- | oemrouter.cpp | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/oemrouter.cpp b/oemrouter.cpp new file mode 100644 index 0000000..273a679 --- /dev/null +++ b/oemrouter.cpp @@ -0,0 +1,149 @@ +#include <cstdio> +#include <cstring> +#include <map> +#include <utility> + +#include "host-ipmid/oemrouter.hpp" + +namespace oem +{ + +using Key = std::pair<Number, ipmi_cmd_t>; + +// Private implementation of OemRouter Interface. +class RouterImpl : public Router +{ + public: + RouterImpl() {} + + // Implement OemRouter Interface. + void activate() override; + void registerHandler(Number oen, ipmi_cmd_t cmd, + Handler handler) override; + + // Actual message routing function. + ipmi_ret_t routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf, + uint8_t* replyBuf, size_t* dataLen); + + private: + std::map<Key, Handler> handlers; +}; + +// Static global instance for simplicity. +static RouterImpl* globalRouterImpl; + +// TODO Refactor ipmid to avoid need for singleton here. +Router* mutableRouter() +{ + if (!globalRouterImpl) + { + globalRouterImpl = new RouterImpl; + } + return globalRouterImpl; +} + +ipmi_ret_t RouterImpl::routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf, + uint8_t* replyBuf, size_t* dataLen) +{ + // Not entirely clear we can route reply without complete OEM group. + // TODO: consider adding a way to suppress malformed replies. + if (*dataLen < groupMagicSize) + { + fprintf(stderr, "NetFn:[0x2E], OEM:[%zu bytes?], Cmd:[%#04X]\n", + *dataLen, cmd); + (*dataLen) = 0; + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + + // Find registered handler or reject request. + auto number = toOemNumber(reqBuf); + auto cmdKey = std::make_pair(number, cmd); + + auto iter = handlers.find(cmdKey); + if (iter == handlers.end()) + { + auto wildKey = std::make_pair(number, IPMI_CMD_WILDCARD); + iter = handlers.find(wildKey); + if (iter == handlers.end()) + { + fprintf(stderr, "No Registered handler for NetFn:[0x2E], " + "OEM:[%#08X], Cmd:[%#04X]\n", number, cmd); + *dataLen = groupMagicSize; + return IPMI_CC_INVALID; + } +#ifdef __IPMI_DEBUG__ + fprintf(stderr, "Wildcard NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n", + number, cmd); +#endif + } + else + { +#ifdef __IPMI_DEBUG__ + fprintf(stderr, "Match NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n", + number, cmd); +#endif + } + + // Copy OEMGroup here, by analogy to IPMI CC code at netfn router; + // OemHandler should deal only with optional following data bytes. + std::memcpy(replyBuf, reqBuf, groupMagicSize); + + size_t oemDataLen = *dataLen - groupMagicSize; + Handler& handler = iter->second; + + auto rc = handler(cmd, reqBuf + groupMagicSize, + replyBuf + groupMagicSize, &oemDataLen); + + // Add OEMGroup bytes to nominal reply. + *dataLen = oemDataLen + groupMagicSize; + return rc; +} + +// Function suitable for use as ipmi_netfn_router() call-back. +// Translates call-back pointer args to more specific types. +ipmi_ret_t ipmi_oem_wildcard_handler(ipmi_netfn_t /* netfn */, + ipmi_cmd_t cmd, ipmi_request_t request, + ipmi_response_t response, + ipmi_data_len_t dataLen, + ipmi_context_t context) +{ + // View requests & responses as byte sequences. + const uint8_t* reqBuf = static_cast<uint8_t*>(request); + uint8_t* replyBuf = static_cast<uint8_t*>(response); + + // View context as router object, defaulting nullptr to global object. + auto router = static_cast<RouterImpl*>( + context ? context : mutableRouter()); + + // Send message parameters to dispatcher. + return router->routeMsg(cmd, reqBuf, replyBuf, dataLen); +} + +// Enable message routing to begin. +void RouterImpl::activate() +{ + // Register netfn 0x2e OEM Group, any (wildcard) command. + printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", + NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD); + ipmi_register_callback(NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD, this, + ipmi_oem_wildcard_handler, PRIVILEGE_OEM); +} + +void RouterImpl::registerHandler(Number oen, ipmi_cmd_t cmd, + Handler handler) +{ + auto cmdKey = std::make_pair(oen, cmd); + auto iter = handlers.find(cmdKey); + if (iter == handlers.end()) + { + // Add handler if key not already taken. + handlers.emplace(cmdKey, handler); + } + else + { + fprintf(stderr, "ERROR : Duplicate registration for NetFn:[0x2E], " + "OEM:[%#08X], Cmd:[%#04X]\n", oen, cmd); + } +} + +} // namespace oem |