diff options
-rw-r--r-- | include/Makefile.am | 1 | ||||
-rw-r--r-- | include/ipmid/filter.hpp | 94 | ||||
-rw-r--r-- | include/ipmid/registration.hpp | 36 | ||||
-rw-r--r-- | ipmid-new.cpp | 49 |
4 files changed, 180 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index 4c0e899..852b26b 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,6 @@ nobase_include_HEADERS = \ ipmid/api.hpp \ + ipmid/filter.hpp \ ipmid/handler.hpp \ ipmid/message.hpp \ ipmid/message/pack.hpp \ diff --git a/include/ipmid/filter.hpp b/include/ipmid/filter.hpp new file mode 100644 index 0000000..3cd5d21 --- /dev/null +++ b/include/ipmid/filter.hpp @@ -0,0 +1,94 @@ +/** + * Copyright © 2018 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include <algorithm> +#include <boost/callable_traits.hpp> +#include <cstdint> +#include <ipmid/api.hpp> +#include <ipmid/message.hpp> +#include <memory> +#include <tuple> +#include <utility> + +namespace ipmi +{ + +using FilterFunction = ipmi::Cc(ipmi::message::Request::ptr); + +/** + * @brief Filter base class for dealing with IPMI request/response + * + * The subclasses are all templated so they can provide access to any type of + * command callback functions. + */ +class FilterBase +{ + public: + using ptr = std::shared_ptr<FilterBase>; + + virtual ipmi::Cc call(message::Request::ptr request) = 0; +}; + +/** + * @brief filter concrete class + * + * This is the base template that ipmi filters will resolve into. This is + * essentially just a wrapper to hold the filter callback so it can be stored in + * the filter list. + * + * Filters are called with a ipmi::message::Request shared_ptr on all IPMI + * commands in priority order and each filter has the opportunity to reject the + * command (by returning an IPMI error competion code.) If all the filters + * return success, the actual IPMI command will be executed. Filters can reject + * the command for any reason, based on system state, the context, the command + * payload, etc. + */ +template <typename Filter> +class IpmiFilter : public FilterBase +{ + public: + IpmiFilter(Filter&& filter) : filter_(std::move(filter)) + { + } + + ipmi::Cc call(message::Request::ptr request) override + { + return filter_(request); + } + + private: + Filter filter_; +}; + +/** + * @brief helper function to construct a filter object + * + * This is called internally by the ipmi::registerFilter function. + */ +template <typename Filter> +static inline auto makeFilter(Filter&& filter) +{ + FilterBase::ptr ptr(new IpmiFilter<Filter>(std::forward<Filter>(filter))); + return ptr; +} +template <typename Filter> +static inline auto makeFilter(const Filter& filter) +{ + Filter lFilter = filter; + return makeFilter(std::forward<Filter>(lFilter)); +} + +} // namespace ipmi diff --git a/include/ipmid/registration.hpp b/include/ipmid/registration.hpp index af89bc8..45391ec 100644 --- a/include/ipmid/registration.hpp +++ b/include/ipmid/registration.hpp @@ -16,6 +16,7 @@ #pragma once #include <ipmid/api.hpp> +#include <ipmid/filter.hpp> #include <ipmid/handler.hpp> namespace ipmi @@ -32,6 +33,9 @@ bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv, bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, ::ipmi::HandlerBase::ptr handler); +// IPMI command filter registration implementation +void registerFilter(int prio, ::ipmi::FilterBase::ptr filter); + } // namespace impl /** @@ -132,6 +136,38 @@ void registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, impl::registerOemHandler(prio, iana, cmd, priv, h); } +/** + * @brief IPMI command filter registration function + * + * This function should be used to register IPMI command filter functions. + * This function just passes the callback to makeFilter, which creates a + * wrapper functor object that ultimately calls the callback. + * + * Filters are called with a ipmi::message::Request shared_ptr on all IPMI + * commands in priority order and each filter has the opportunity to reject the + * command (by returning an IPMI error competion code.) If all the filters + * return success, the actual IPMI command will be executed. Filters can reject + * the command for any reason, based on system state, the context, the command + * payload, etc. + * + * @param prio - priority at which to register; see api.hpp + * @param filter - the callback function that will handle this request + * + * @return bool - success of registering the handler + */ +template <typename Filter> +void registerFilter(int prio, Filter&& filter) +{ + auto f = ipmi::makeFilter(std::forward<Filter>(filter)); + impl::registerFilter(prio, f); +} + +template <typename Filter> +void registerFilter(int prio, const Filter& filter) +{ + auto f = ipmi::makeFilter(filter); + impl::registerFilter(prio, f); +} } // namespace ipmi #ifdef ALLOW_DEPRECATED_API diff --git a/ipmid-new.cpp b/ipmid-new.cpp index a9aff36..4986e49 100644 --- a/ipmid-new.cpp +++ b/ipmid-new.cpp @@ -135,6 +135,13 @@ static std::unordered_map<unsigned int, /* key is Iana/Cmd (NetFn is 2Eh) */ HandlerTuple> oemHandlerMap; +using FilterTuple = std::tuple<int, /* prio */ + FilterBase::ptr /* filter */ + >; + +/* list to hold all registered ipmi command filters */ +static std::forward_list<FilterTuple> filterList; + namespace impl { /* common function to register all standard IPMI handlers */ @@ -198,12 +205,54 @@ bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, return false; } +/* common function to register all IPMI filter handlers */ +void registerFilter(int prio, FilterBase::ptr filter) +{ + // check for initial placement + if (filterList.empty() || std::get<int>(filterList.front()) < prio) + { + filterList.emplace_front(std::make_tuple(prio, filter)); + } + // walk the list and put it in the right place + auto j = filterList.begin(); + for (auto i = j; i != filterList.end() && std::get<int>(*i) > prio; i++) + { + j = i; + } + filterList.emplace_after(j, std::make_tuple(prio, filter)); +} + } // namespace impl +message::Response::ptr filterIpmiCommand(message::Request::ptr request) +{ + // pass the command through the filter mechanism + // This can be the firmware firewall or any OEM mechanism like + // whitelist filtering based on operational mode + for (auto& item : filterList) + { + FilterBase::ptr filter = std::get<FilterBase::ptr>(item); + ipmi::Cc cc = filter->call(request); + if (ipmi::ccSuccess != cc) + { + return errorResponse(request, cc); + } + } + return message::Response::ptr(); +} + message::Response::ptr executeIpmiCommandCommon( std::unordered_map<unsigned int, HandlerTuple>& handlers, unsigned int keyCommon, message::Request::ptr request) { + // filter the command first; a non-null message::Response::ptr + // means that the message has been rejected for some reason + message::Response::ptr response = filterIpmiCommand(request); + if (response) + { + return response; + } + Cmd cmd = request->ctx->cmd; unsigned int key = makeCmdKey(keyCommon, cmd); auto cmdIter = handlers.find(key); |