summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipmid-new.cpp218
-rw-r--r--user_channel/channel_layer.cpp4
-rw-r--r--user_channel/channel_layer.hpp9
-rw-r--r--user_channel/channel_mgmt.hpp12
4 files changed, 236 insertions, 7 deletions
diff --git a/ipmid-new.cpp b/ipmid-new.cpp
index 2a2d4a6..403d4d1 100644
--- a/ipmid-new.cpp
+++ b/ipmid-new.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <any>
+#include <boost/algorithm/string.hpp>
#include <dcmihandler.hpp>
#include <exception>
#include <filesystem>
@@ -320,21 +321,215 @@ message::Response::ptr executeIpmiCommand(message::Request::ptr request)
return executeIpmiCommandCommon(handlerMap, netFn, request);
}
+namespace utils
+{
+template <typename AssocContainer, typename UnaryPredicate>
+void assoc_erase_if(AssocContainer& c, UnaryPredicate p)
+{
+ typename AssocContainer::iterator next = c.begin();
+ typename AssocContainer::iterator last = c.end();
+ while ((next = std::find_if(next, last, p)) != last)
+ {
+ c.erase(next++);
+ }
+}
+} // namespace utils
+
+namespace
+{
+std::unordered_map<std::string, uint8_t> uniqueNameToChannelNumber;
+
+// sdbusplus::bus::match::rules::arg0namespace() wants the prefix
+// to match without any trailing '.'
+constexpr const char ipmiDbusChannelMatch[] =
+ "xyz.openbmc_project.Ipmi.Channel";
+void updateOwners(sdbusplus::asio::connection& conn, const std::string& name)
+{
+ conn.async_method_call(
+ [name](const boost::system::error_code ec,
+ const std::string& nameOwner) {
+ if (ec)
+ {
+ log<level::ERR>("Error getting dbus owner",
+ entry("INTERFACE=%s", name.c_str()));
+ return;
+ }
+ // start after ipmiDbusChannelPrefix (after the '.')
+ std::string chName =
+ name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
+ try
+ {
+ uint8_t channel = getChannelByName(chName);
+ uniqueNameToChannelNumber[nameOwner] = channel;
+ log<level::INFO>("New interface mapping",
+ entry("INTERFACE=%s", name.c_str()),
+ entry("CHANNEL=%u", channel));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::INFO>("Failed interface mapping, no such name",
+ entry("INTERFACE=%s", name.c_str()));
+ }
+ },
+ "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
+ name);
+}
+
+void doListNames(boost::asio::io_service& io, sdbusplus::asio::connection& conn)
+{
+ conn.async_method_call(
+ [&io, &conn](const boost::system::error_code ec,
+ std::vector<std::string> busNames) {
+ if (ec)
+ {
+ log<level::ERR>("Error getting dbus names");
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+ // Try to make startup consistent
+ std::sort(busNames.begin(), busNames.end());
+
+ const std::string channelPrefix =
+ std::string(ipmiDbusChannelMatch) + ".";
+ for (const std::string& busName : busNames)
+ {
+ if (busName.find(channelPrefix) == 0)
+ {
+ updateOwners(conn, busName);
+ }
+ }
+ },
+ "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
+ "ListNames");
+}
+
+void nameChangeHandler(sdbusplus::message::message& message)
+{
+ std::string name;
+ std::string oldOwner;
+ std::string newOwner;
+
+ message.read(name, oldOwner, newOwner);
+
+ if (!oldOwner.empty())
+ {
+ if (boost::starts_with(oldOwner, ":"))
+ {
+ // Connection removed
+ auto it = uniqueNameToChannelNumber.find(oldOwner);
+ if (it != uniqueNameToChannelNumber.end())
+ {
+ uniqueNameToChannelNumber.erase(it);
+ }
+ }
+ }
+ if (!newOwner.empty())
+ {
+ // start after ipmiDbusChannelMatch (and after the '.')
+ std::string chName = name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
+ try
+ {
+ uint8_t channel = getChannelByName(chName);
+ uniqueNameToChannelNumber[newOwner] = channel;
+ log<level::INFO>("New interface mapping",
+ entry("INTERFACE=%s", name.c_str()),
+ entry("CHANNEL=%u", channel));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::INFO>("Failed interface mapping, no such name",
+ entry("INTERFACE=%s", name.c_str()));
+ }
+ }
+};
+
+} // anonymous namespace
+
+static constexpr const char intraBmcName[] = "INTRABMC";
+uint8_t channelFromMessage(sdbusplus::message::message& msg)
+{
+ // channel name for ipmitool to resolve to
+ std::string sender = msg.get_sender();
+ auto chIter = uniqueNameToChannelNumber.find(sender);
+ if (chIter != uniqueNameToChannelNumber.end())
+ {
+ return chIter->second;
+ }
+ // FIXME: currently internal connections are ephemeral and hard to pin down
+ try
+ {
+ return getChannelByName(intraBmcName);
+ }
+ catch (const std::exception& e)
+ {
+ return invalidChannel;
+ }
+} // namespace ipmi
+
/* called from sdbus async server context */
-auto executionEntry(boost::asio::yield_context yield, NetFn netFn, uint8_t lun,
+auto executionEntry(boost::asio::yield_context yield,
+ sdbusplus::message::message& m, NetFn netFn, uint8_t lun,
Cmd cmd, std::vector<uint8_t>& data,
std::map<std::string, ipmi::Value>& options)
{
- auto ctx = std::make_shared<ipmi::Context>(netFn, cmd, 0, 0,
- ipmi::Privilege::Admin, &yield);
+ const auto dbusResponse =
+ [netFn, lun, cmd](Cc cc, const std::vector<uint8_t>& data = {}) {
+ constexpr uint8_t netFnResponse = 0x01;
+ uint8_t retNetFn = netFn | netFnResponse;
+ return std::make_tuple(retNetFn, lun, cmd, cc, data);
+ };
+ std::string sender = m.get_sender();
+ Privilege privilege = Privilege::None;
+ uint8_t userId = 0; // undefined user
+
+ // figure out what channel the request came in on
+ uint8_t channel = channelFromMessage(m);
+ if (channel == invalidChannel)
+ {
+ // unknown sender channel; refuse to service the request
+ log<level::ERR>("ERROR determining source IPMI channel",
+ entry("SENDER=%s", sender.c_str()),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd));
+ return dbusResponse(ipmi::ccDestinationUnavailable);
+ }
+
+ // session-based channels are required to provide userId/privilege
+ if (getChannelSessionSupport(channel) != EChannelSessSupported::none)
+ {
+ try
+ {
+ Value requestPriv = options.at("privilege");
+ Value requestUserId = options.at("userId");
+ privilege = static_cast<Privilege>(std::get<int>(requestPriv));
+ userId = static_cast<uint8_t>(std::get<int>(requestUserId));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("ERROR determining IPMI session credentials",
+ entry("CHANNEL=%u", channel),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd));
+ return dbusResponse(ipmi::ccUnspecifiedError);
+ }
+ }
+ else
+ {
+ // get max privilege for session-less channels
+ // For now, there is not a way to configure this, default to Admin
+ privilege = Privilege::Admin;
+ }
+ // check to see if the requested priv/username is valid
+ log<level::DEBUG>("Set up ipmi context", entry("SENDER=%s", sender.c_str()),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd),
+ entry("CHANNEL=%u", channel), entry("USERID=%u", userId),
+ entry("PRIVILEGE=%u", static_cast<uint8_t>(privilege)));
+
+ auto ctx = std::make_shared<ipmi::Context>(netFn, cmd, channel, userId,
+ privilege, &yield);
auto request = std::make_shared<ipmi::message::Request>(
ctx, std::forward<std::vector<uint8_t>>(data));
message::Response::ptr response = executeIpmiCommand(request);
- // Responses in IPMI require a bit set. So there ya go...
- netFn |= 0x01;
- return std::make_tuple(netFn, lun, cmd, response->cc,
- response->payload.raw);
+ return dbusResponse(response->cc, response->payload.raw);
}
/** @struct IpmiProvider
@@ -623,6 +818,15 @@ int main(int argc, char* argv[])
handleLegacyIpmiCommand);
#endif /* ALLOW_DEPRECATED_API */
+ // set up bus name watching to match channels with bus names
+ sdbusplus::bus::match::match nameOwnerChanged(
+ *sdbusp,
+ sdbusplus::bus::match::rules::nameOwnerChanged() +
+ sdbusplus::bus::match::rules::arg0namespace(
+ ipmi::ipmiDbusChannelMatch),
+ ipmi::nameChangeHandler);
+ ipmi::doListNames(*io, *sdbusp);
+
// set up boost::asio signal handling
std::function<SignalResponse(int)> stopAsioRunLoop =
[&io](int signalNumber) {
diff --git a/user_channel/channel_layer.cpp b/user_channel/channel_layer.cpp
index 34a596d..da9c613 100644
--- a/user_channel/channel_layer.cpp
+++ b/user_channel/channel_layer.cpp
@@ -142,4 +142,8 @@ std::string getChannelName(const uint8_t chNum)
return getChannelConfigObject().getChannelName(chNum);
}
+uint8_t getChannelByName(const std::string& chName)
+{
+ return getChannelConfigObject().getChannelByName(chName);
+}
} // namespace ipmi
diff --git a/user_channel/channel_layer.hpp b/user_channel/channel_layer.hpp
index 1a8d64c..4308794 100644
--- a/user_channel/channel_layer.hpp
+++ b/user_channel/channel_layer.hpp
@@ -24,6 +24,7 @@ namespace ipmi
static constexpr uint8_t maxIpmiChannels = 16;
static constexpr uint8_t currentChNum = 0xE;
+static constexpr uint8_t invalidChannel = 0xff;
/**
* @enum IPMI return codes specific to channel (refer spec se 22.22 response
@@ -367,4 +368,12 @@ ipmi_ret_t getChannelEnabledAuthType(const uint8_t chNum, const uint8_t priv,
*/
std::string getChannelName(const uint8_t chNum);
+/** @brief Retrieves the LAN channel number from the IPMI channel name
+ *
+ * @param[in] chName - IPMI channel name (i.e. eth0)
+ *
+ * @return the LAN channel number
+ */
+uint8_t getChannelByName(const std::string& chName);
+
} // namespace ipmi
diff --git a/user_channel/channel_mgmt.hpp b/user_channel/channel_mgmt.hpp
index 35bb494..baeb8c7 100644
--- a/user_channel/channel_mgmt.hpp
+++ b/user_channel/channel_mgmt.hpp
@@ -105,6 +105,18 @@ class ChannelConfig
*/
std::string getChannelName(const uint8_t chNum);
+ /** @brief function to get channel number from channel name
+ *
+ * @param[in] chName - channel name
+ *
+ * @return network channel interface number
+ */
+
+ uint8_t getChannelByName(const std::string& chName)
+ {
+ return convertToChannelNumberFromChannelName(chName);
+ }
+
/** @brief determines supported session type of a channel
*
* @param[in] chNum - channel number
OpenPOWER on IntegriCloud