summaryrefslogtreecommitdiffstats
path: root/user_channel/channel_mgmt.cpp
diff options
context:
space:
mode:
authorAppaRao Puli <apparao.puli@linux.intel.com>2018-09-01 23:46:44 +0530
committerRichard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>2018-11-20 10:09:26 +0530
commit9613ed76ba9c60232ee2064a36c10f5bd7b81b84 (patch)
treee4a7a0305e45ee325ff2f7cdc9de9b1ca499dadc /user_channel/channel_mgmt.cpp
parent071f3f2f298ab4a4799b7fcdba3255dfd999452d (diff)
downloadphosphor-host-ipmid-9613ed76ba9c60232ee2064a36c10f5bd7b81b84.tar.gz
phosphor-host-ipmid-9613ed76ba9c60232ee2064a36c10f5bd7b81b84.zip
Synchronize channel info between network and ipmi
Synchronizing the channel privilege config between network interface(generic) and channel managament(IPMI). 1) During start-up get the network interface privilege and update the IPMI data base(NV). 2) Catch the signal for network interface channel privilege and update the ipmi data base(Volatile and NV data). 3) During ipmi privilege update(NV), send it to network interface over DBUS. Unit Test: Verified the changes by modifying MaxPrivilege in network interfaces and signal catching logic and vise versa as specified in description. Change-Id: Iaa7e9e248f1cac6ab560137c8c136abdd44e190e Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
Diffstat (limited to 'user_channel/channel_mgmt.cpp')
-rw-r--r--user_channel/channel_mgmt.cpp389
1 files changed, 389 insertions, 0 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;
}
OpenPOWER on IntegriCloud