summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--user_channel/channel_mgmt.cpp389
-rw-r--r--user_channel/channel_mgmt.hpp94
2 files changed, 464 insertions, 19 deletions
diff --git a/user_channel/channel_mgmt.cpp b/user_channel/channel_mgmt.cpp
index 0db54b6..4cae923 100644
--- a/user_channel/channel_mgmt.cpp
+++ b/user_channel/channel_mgmt.cpp
@@ -23,9 +23,12 @@
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cerrno>
+#include <exception>
#include <experimental/filesystem>
#include <fstream>
#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/server/object.hpp>
#include <unordered_map>
namespace ipmi
@@ -42,6 +45,18 @@ static constexpr const char* channelNvDataFilename =
static constexpr const char* channelVolatileDataFilename =
"/run/ipmi/channel_access_volatile.json";
+// TODO: Get the service name dynamically..
+static constexpr const char* networkIntfServiceName =
+ "xyz.openbmc_project.Network";
+static constexpr const char* networkIntfObjectBasePath =
+ "/xyz/openbmc_project/network";
+static constexpr const char* networkChConfigIntfName =
+ "xyz.openbmc_project.Channel.ChannelAccess";
+static constexpr const char* privilegePropertyString = "MaxPrivilege";
+static constexpr const char* dBusPropertiesInterface =
+ "org.freedesktop.DBus.Properties";
+static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
+
// STRING DEFINES: Should sync with key's in JSON
static constexpr const char* nameString = "name";
static constexpr const char* isValidString = "is_valid";
@@ -71,6 +86,8 @@ static constexpr const uint8_t defaultAuthType =
static_cast<uint8_t>(EAuthType::none);
static constexpr const bool defaultIsIpmiState = false;
+std::unique_ptr<sdbusplus::bus::match_t> chPropertiesSignal(nullptr);
+
// String mappings use in JSON config file
static std::unordered_map<std::string, EChannelMediumType> mediumTypeMap = {
{"reserved", EChannelMediumType::reserved},
@@ -112,12 +129,172 @@ static std::array<std::string, PRIVILEGE_OEM + 1> privList = {
"priv-reserved", "priv-callback", "priv-user",
"priv-operator", "priv-admin", "priv-oem"};
+static constexpr const char* LAN1_STR = "LAN1";
+static constexpr const char* LAN2_STR = "LAN2";
+static constexpr const char* LAN3_STR = "LAN3";
+static constexpr const char* ETH0_STR = "eth0";
+static constexpr const char* ETH1_STR = "eth1";
+static constexpr const char* ETH2_STR = "eth2";
+
+static std::unordered_map<std::string, std::string> channelToInterfaceMap = {
+ {LAN1_STR, ETH0_STR}, {LAN2_STR, ETH1_STR}, {LAN3_STR, ETH2_STR}};
+
+static std::unordered_map<std::string, std::string> interfaceToChannelMap = {
+ {ETH0_STR, LAN1_STR}, {ETH1_STR, LAN2_STR}, {ETH2_STR, LAN3_STR}};
+
+std::string convertToChannelName(const std::string& intfName)
+{
+
+ auto it = interfaceToChannelMap.find(intfName);
+ if (it == interfaceToChannelMap.end())
+ {
+ log<level::ERR>("Invalid network interface.",
+ entry("INTF:%s", intfName.c_str()));
+ throw std::invalid_argument("Invalid network interface");
+ }
+
+ return it->second;
+}
+
+std::string getNetIntfFromPath(const std::string& path)
+{
+ std::size_t pos = path.find(networkIntfObjectBasePath);
+ if (pos == std::string::npos)
+ {
+ log<level::ERR>("Invalid interface path.",
+ entry("PATH:%s", path.c_str()));
+ throw std::invalid_argument("Invalid interface path");
+ }
+ std::string intfName =
+ path.substr(pos + strlen(networkIntfObjectBasePath) + 1);
+ return intfName;
+}
+
+void processChAccessPropChange(ChannelConfig& chConfig, const std::string& path,
+ const DbusChObjProperties& chProperties)
+{
+ // Get interface name from path. ex: '/xyz/openbmc_project/network/eth0'
+ std::string intfName;
+ try
+ {
+ intfName = getNetIntfFromPath(path);
+ }
+ catch (const std::invalid_argument& e)
+ {
+ log<level::ERR>("Exception: ", entry("MSG: %s", e.what()));
+ return;
+ }
+
+ // Get the MaxPrivilege property value from the signal
+ std::string intfPrivStr;
+ std::string propName;
+ for (const auto& prop : chProperties)
+ {
+ if (prop.first == privilegePropertyString)
+ {
+ propName = privilegePropertyString;
+ intfPrivStr = prop.second.get<std::string>();
+ break;
+ }
+ }
+
+ if (propName != privilegePropertyString)
+ {
+ log<level::ERR>("Unknown signal caught.");
+ return;
+ }
+
+ if (intfPrivStr.empty())
+ {
+ log<level::ERR>("Invalid privilege string.",
+ entry("INTF:%s", intfName.c_str()));
+ return;
+ }
+
+ uint8_t intfPriv = 0;
+ std::string channelName;
+ try
+ {
+ intfPriv =
+ static_cast<uint8_t>(chConfig.convertToPrivLimitIndex(intfPrivStr));
+ channelName = convertToChannelName(intfName);
+ }
+ catch (const std::invalid_argument& e)
+ {
+ log<level::ERR>("Exception: ", entry("MSG: %s", e.what()));
+ return;
+ }
+
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ channelLock{*chConfig.channelMutex};
+ uint8_t chNum = 0;
+ ChannelData* chData;
+ // Get the channel number based on the channel name.
+ for (chNum = 0; chNum < maxIpmiChannels; chNum++)
+ {
+ chData = chConfig.getChannelDataPtr(chNum);
+ if (chData->chName == channelName)
+ {
+ break;
+ }
+ }
+ if (chNum >= maxIpmiChannels)
+ {
+ log<level::ERR>("Invalid interface in signal path");
+ return;
+ }
+
+ // skip updating the values, if this property change originated from IPMI.
+ if (chConfig.signalFlag & (1 << chNum))
+ {
+ chConfig.signalFlag &= ~(1 << chNum);
+ log<level::DEBUG>("Request originated from IPMI so ignoring signal");
+ return;
+ }
+
+ // Update both volatile & Non-volatile, if there is mismatch.
+ // as property change other than IPMI, has to update both volatile &
+ // non-volatile data.
+ if (chData->chAccess.chNonVolatileData.privLimit != intfPriv)
+ {
+ // Update NV data
+ chData->chAccess.chNonVolatileData.privLimit = intfPriv;
+ if (chConfig.writeChannelPersistData() != 0)
+ {
+ log<level::ERR>("Failed to update the persist data file");
+ return;
+ }
+
+ // Update Volatile data
+ if (chData->chAccess.chVolatileData.privLimit != intfPriv)
+ {
+ chData->chAccess.chVolatileData.privLimit = intfPriv;
+ if (chConfig.writeChannelVolatileData() != 0)
+ {
+ log<level::ERR>("Failed to update the volatile data file");
+ return;
+ }
+ }
+ }
+
+ return;
+}
+
ChannelConfig& getChannelConfigObject()
{
static ChannelConfig channelConfig;
return channelConfig;
}
+ChannelConfig::~ChannelConfig()
+{
+ if (signalHndlrObjectState)
+ {
+ chPropertiesSignal.reset();
+ sigHndlrLock.unlock();
+ }
+}
+
ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection())
{
std::ofstream mutexCleanUpFile;
@@ -148,6 +325,38 @@ ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection())
}
initChannelPersistData();
+
+ sigHndlrLock = boost::interprocess::file_lock(channelNvDataFilename);
+ // Register it for single object and single process either netipimd /
+ // host-ipmid
+ if (chPropertiesSignal == nullptr && sigHndlrLock.try_lock())
+ {
+ log<level::DEBUG>("Registering channel signal handler.");
+ chPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::path_namespace(
+ networkIntfObjectBasePath) +
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
+ sdbusplus::bus::match::rules::interface(
+ dBusPropertiesInterface) +
+ sdbusplus::bus::match::rules::argN(0, networkChConfigIntfName),
+ [&](sdbusplus::message::message& msg) {
+ DbusChObjProperties props;
+ std::string iface;
+ std::string path = msg.get_path();
+ msg.read(iface, props);
+ processChAccessPropChange(*this, path, props);
+ });
+ signalHndlrObjectState = true;
+ }
+}
+
+ChannelData* ChannelConfig::getChannelDataPtr(const uint8_t& chNum)
+{
+ // reload data before using it.
+ checkAndReloadVolatileData();
+ return &channelData[chNum];
}
bool ChannelConfig::isValidChannel(const uint8_t& chNum)
@@ -397,6 +606,28 @@ ipmi_ret_t ChannelConfig::setChannelAccessPersistData(
}
if (setFlag & setPrivLimit)
{
+ // Send Update to network channel config interfaces over dbus
+ std::string intfName = convertToNetInterface(channelData[chNum].chName);
+ std::string privStr = convertToPrivLimitString(chAccessData.privLimit);
+ std::string networkIntfObj =
+ std::string(networkIntfObjectBasePath) + "/" + intfName;
+ try
+ {
+ if (0 != setDbusProperty(bus, networkIntfServiceName,
+ networkIntfObj, networkChConfigIntfName,
+ privilegePropertyString, privStr))
+ {
+ log<level::DEBUG>("Network interface does not exist",
+ entry("INTERFACE:%s", intfName.c_str()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Exception: Network interface does not exist");
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ signalFlag |= (1 << chNum);
channelData[chNum].chAccess.chNonVolatileData.privLimit =
chAccessData.privLimit;
}
@@ -560,6 +791,19 @@ EChannelProtocolType
return static_cast<EChannelProtocolType>(it->second);
}
+std::string ChannelConfig::convertToNetInterface(const std::string& value)
+{
+ auto it = channelToInterfaceMap.find(value);
+ if (it == channelToInterfaceMap.end())
+ {
+ log<level::DEBUG>("Invalid channel name.",
+ entry("NAME:%s", value.c_str()));
+ throw std::invalid_argument("Invalid channel name.");
+ }
+
+ return it->second;
+}
+
Json ChannelConfig::readJsonFile(const std::string& configFile)
{
std::ifstream jsonFile(configFile);
@@ -979,6 +1223,139 @@ int ChannelConfig::checkAndReloadVolatileData()
return ret;
}
+int ChannelConfig::setDbusProperty(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ const std::string& property,
+ const DbusVariant& value)
+{
+ try
+ {
+ auto method =
+ bus.new_method_call(service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Set");
+
+ method.append(interface, property, value);
+
+ auto reply = bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("set-property failed",
+ entry("SERVICE:%s", service.c_str()),
+ entry("OBJPATH:%s", objPath.c_str()),
+ entry("INTERFACE:%s", interface.c_str()),
+ entry("PROP:%s", property.c_str()));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int ChannelConfig::getDbusProperty(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ const std::string& property,
+ DbusVariant& value)
+{
+ try
+ {
+ auto method =
+ bus.new_method_call(service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+
+ method.append(interface, property);
+
+ auto reply = bus.call(method);
+ reply.read(value);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>("get-property failed",
+ entry("SERVICE:%s", service.c_str()),
+ entry("OBJPATH:%s", objPath.c_str()),
+ entry("INTERFACE:%s", interface.c_str()),
+ entry("PROP:%s", property.c_str()));
+ return -EIO;
+ }
+ return 0;
+}
+
+int ChannelConfig::syncNetworkChannelConfig()
+{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ channelLock{*channelMutex};
+ bool isUpdated = false;
+ for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
+ {
+ if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
+ {
+ std::string intfPrivStr;
+ try
+ {
+ std::string intfName =
+ convertToNetInterface(channelData[chNum].chName);
+ std::string networkIntfObj =
+ std::string(networkIntfObjectBasePath) + "/" + intfName;
+ DbusVariant variant;
+ if (0 != getDbusProperty(bus, networkIntfServiceName,
+ networkIntfObj,
+ networkChConfigIntfName,
+ privilegePropertyString, variant))
+ {
+ log<level::DEBUG>("Network interface does not exist",
+ entry("INTERFACE:%s", intfName.c_str()));
+ continue;
+ }
+ intfPrivStr = variant.get<std::string>();
+ }
+ catch (const mapbox::util::bad_variant_access& e)
+ {
+ log<level::DEBUG>(
+ "exception: Network interface does not exist");
+ continue;
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::DEBUG>(
+ "exception: Network interface does not exist");
+ continue;
+ }
+
+ uint8_t intfPriv =
+ static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
+ if (channelData[chNum].chAccess.chNonVolatileData.privLimit !=
+ intfPriv)
+ {
+ isUpdated = true;
+ channelData[chNum].chAccess.chNonVolatileData.privLimit =
+ intfPriv;
+ channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
+ }
+ }
+ }
+
+ if (isUpdated)
+ {
+ // Write persistent data to file
+ if (writeChannelPersistData() != 0)
+ {
+ log<level::DEBUG>("Failed to update the persistent data file");
+ return -EIO;
+ }
+ // Write Volatile data to file
+ if (writeChannelVolatileData() != 0)
+ {
+ log<level::DEBUG>("Failed to update the channel volatile data");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
void ChannelConfig::initChannelPersistData()
{
/* Always read the channel config */
@@ -1021,6 +1398,18 @@ void ChannelConfig::initChannelPersistData()
"Failed to read channel access volatile configuration");
}
}
+
+ // Synchronize the channel config(priv) with network channel
+ // configuration(priv) over dbus
+ if (syncNetworkChannelConfig() != 0)
+ {
+ log<level::ERR>(
+ "Failed to synchronize data with network channel config over dbus");
+ throw std::ios_base::failure(
+ "Failed to synchronize data with network channel config over dbus");
+ }
+
+ log<level::DEBUG>("Successfully completed channel data initialization.");
return;
}
diff --git a/user_channel/channel_mgmt.hpp b/user_channel/channel_mgmt.hpp
index 053fa39..5c35041 100644
--- a/user_channel/channel_mgmt.hpp
+++ b/user_channel/channel_mgmt.hpp
@@ -29,6 +29,11 @@ namespace ipmi
using Json = nlohmann::json;
+using DbusVariant =
+ sdbusplus::message::variant<std::vector<std::string>, std::string, bool>;
+
+using DbusChObjProperties = std::vector<std::pair<std::string, DbusVariant>>;
+
static constexpr const char* ipmiChannelMutex = "ipmi_channel_mutex";
static constexpr const char* ipmiChMutexCleanupLockFile =
"/var/lib/ipmi/ipmi_channel_mutex_cleanup";
@@ -61,7 +66,7 @@ class ChannelConfig
ChannelConfig(ChannelConfig&&) = delete;
ChannelConfig& operator=(ChannelConfig&&) = delete;
- ~ChannelConfig() = default;
+ ~ChannelConfig();
ChannelConfig();
/** @brief determines valid channel
@@ -172,6 +177,36 @@ class ChannelConfig
const uint8_t& priv,
EAuthType& authType);
+ /** @brief conver to channel privilege from system privilege
+ *
+ * @param[in] value - privilege value
+ *
+ * @return Channel privilege
+ */
+ CommandPrivilege convertToPrivLimitIndex(const std::string& value);
+
+ /** @brief function to write persistent channel configuration to config file
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ int writeChannelPersistData();
+
+ /** @brief function to write volatile channel configuration to config file
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ int writeChannelVolatileData();
+
+ /** @brief function to get channel data based on channel number
+ *
+ * @param[in] chNum - channel number
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ ChannelData* getChannelDataPtr(const uint8_t& chNum);
+
+ uint32_t signalFlag = 0;
+
std::unique_ptr<boost::interprocess::named_recursive_mutex> channelMutex{
nullptr};
@@ -182,6 +217,8 @@ class ChannelConfig
std::time_t getUpdatedFileTime(const std::string& fileName);
boost::interprocess::file_lock mutexCleanupLock;
sdbusplus::bus::bus bus;
+ bool signalHndlrObjectState = false;
+ boost::interprocess::file_lock sigHndlrLock;
/** @brief function to initialize persistent channel configuration
*
@@ -209,35 +246,62 @@ class ChannelConfig
*/
int readChannelPersistData();
- /** @brief function to write persistent channel configuration to config file
+ /** @brief function to read volatile channel data
*
* @return 0 for success, -errno for failure.
*/
- int writeChannelPersistData();
+ int readChannelVolatileData();
- /** @brief function to read volatile channel data
+ /** @brief function to check and reload persistent channel data
*
* @return 0 for success, -errno for failure.
*/
- int readChannelVolatileData();
+ int checkAndReloadNVData();
- /** @brief function to write volatile channel configuration to config file
+ /** @brief function to check and reload volatile channel data
*
* @return 0 for success, -errno for failure.
*/
- int writeChannelVolatileData();
+ int checkAndReloadVolatileData();
- /** @brief function to check and reload persistent channel data
+ /** @brief function to sync channel privilege with system network channel
+ * privilege
*
* @return 0 for success, -errno for failure.
*/
- int checkAndReloadNVData();
+ int syncNetworkChannelConfig();
- /** @brief function to check and reload volatile channel data
+ /** @brief function to set D-Bus property value
+ *
+ * @param[in] bus - bus
+ * @param[in] service - service name
+ * @param[in] objPath - object path
+ * @param[in] interface - interface
+ * @param[in] property - property name
+ * @param[in] value - property value
*
* @return 0 for success, -errno for failure.
*/
- int checkAndReloadVolatileData();
+ int setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ const std::string& property, const DbusVariant& value);
+
+ /** @brief function to get D-Bus property value
+ *
+ * @param[in] bus - bus
+ * @param[in] service - service name
+ * @param[in] objPath - object path
+ * @param[in] interface - interface
+ * @param[in] property - property name
+ * @param[out] value - property value
+ *
+ * @return 0 for success, -errno for failure.
+ */
+ int getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ const std::string& property, DbusVariant& value);
/** @brief function to read json config file
*
@@ -273,14 +337,6 @@ class ChannelConfig
*/
std::string convertToAccessModeString(const uint8_t& value);
- /** @brief conver to channel privilege from system privilege
- *
- * @param[in] value - privilege value
- *
- * @return Channel privilege
- */
- CommandPrivilege convertToPrivLimitIndex(const std::string& value);
-
/** @brief function to convert privilege value to string
*
* @param[in] value - privilege value
OpenPOWER on IntegriCloud