diff options
-rw-r--r-- | user_channel/channel_mgmt.cpp | 389 | ||||
-rw-r--r-- | user_channel/channel_mgmt.hpp | 94 |
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 |