diff options
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | apphandler.cpp | 11 | ||||
-rw-r--r-- | user_channel/channel_layer.cpp | 131 | ||||
-rw-r--r-- | user_channel/channel_layer.hpp | 275 | ||||
-rw-r--r-- | user_channel/channel_mgmt.cpp | 1027 | ||||
-rw-r--r-- | user_channel/channel_mgmt.hpp | 334 | ||||
-rw-r--r-- | user_channel/channelcommands.cpp | 391 | ||||
-rw-r--r-- | user_channel/channelcommands.hpp | 32 |
8 files changed, 2205 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index 5e03648..966dbe4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,7 +67,9 @@ libuserlayer_LTLIBRARIES = libuserlayer.la libuserlayer_la_SOURCES = \ user_channel/user_layer.cpp \ user_channel/user_mgmt.cpp \ - user_channel/passwd_mgr.cpp + user_channel/passwd_mgr.cpp \ + user_channel/channel_mgmt.cpp \ + user_channel/channel_layer.cpp libuserlayer_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) \ $(PHOSPHOR_LOGGING_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS) -lstdc++fs \ @@ -99,6 +101,7 @@ libipmi20_la_SOURCES = \ read_fru_data.cpp \ sensordatahandler.cpp \ user_channel/usercommands.cpp \ + user_channel/channelcommands.cpp \ $(libipmi20_BUILT_LIST) @CODE_COVERAGE_RULES@ @@ -138,6 +141,7 @@ libsysintfcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \ nobase_include_HEADERS = \ host-ipmid/iana.hpp \ user_channel/user_layer.hpp \ + user_channel/channel_layer.hpp \ host-ipmid/ipmid-api.h \ host-ipmid/ipmid-host-cmd.hpp \ host-ipmid/ipmid-host-cmd-utils.hpp \ diff --git a/apphandler.cpp b/apphandler.cpp index 370e0c3..bd4b5c3 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -6,6 +6,7 @@ #include "sys_info_param.hpp" #include "transporthandler.hpp" #include "types.hpp" +#include "user_channel/channelcommands.hpp" #include "user_channel/usercommands.hpp" #include "utils.hpp" @@ -1122,6 +1123,11 @@ void register_netfn_app_functions() ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL, ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN); +// TODO: Below code and associated api's need to be removed later. +// Its commented for now to avoid merge conflicts with upstream +// changes and smooth upstream upgrades. +#if 0 +>>>>>>> IPMI Channel commands implementation // <Get Channel Access> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, ipmi_get_channel_access, PRIVILEGE_USER); @@ -1129,6 +1135,7 @@ void register_netfn_app_functions() // <Get Channel Info Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, ipmi_app_channel_info, PRIVILEGE_USER); +#endif // <Get System GUID Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, @@ -1137,13 +1144,15 @@ void register_netfn_app_functions() // <Get Channel Cipher Suites Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL, getChannelCipherSuites, PRIVILEGE_CALLBACK); +#if 0 // <Set Channel Access Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL, ipmi_set_channel_access, PRIVILEGE_ADMIN); - +#endif // <Get System Info Command> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL, ipmi_app_get_system_info, PRIVILEGE_USER); ipmi::registerUserIpmiFunctions(); + ipmi::registerChannelFunctions(); return; } diff --git a/user_channel/channel_layer.cpp b/user_channel/channel_layer.cpp new file mode 100644 index 0000000..32f4ded --- /dev/null +++ b/user_channel/channel_layer.cpp @@ -0,0 +1,131 @@ +/* +// Copyright (c) 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. +*/ + +#include "channel_layer.hpp" + +#include "channel_mgmt.hpp" + +#include <phosphor-logging/log.hpp> + +namespace ipmi +{ + +bool doesDeviceExist(const uint8_t& chNum) +{ + // TODO: This is not the reliable way to find the device + // associated with ethernet interface as the channel number to + // eth association is not done. Need to revisit later + struct stat fileStat; + std::string devName("/sys/class/net/eth"); + devName += std::to_string(chNum - 1); + + if (stat(devName.data(), &fileStat) != 0) + { + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "Ethernet device not found"); + return false; + } + + return true; +} + +bool isValidPrivLimit(const uint8_t& privLimit) +{ + return ((privLimit >= PRIVILEGE_CALLBACK) && (privLimit <= PRIVILEGE_OEM)); +} + +bool isValidAccessMode(const uint8_t& accessMode) +{ + return ( + (accessMode >= static_cast<uint8_t>(EChannelAccessMode::disabled)) && + (accessMode <= static_cast<uint8_t>(EChannelAccessMode::shared))); +} + +bool isValidChannel(const uint8_t& chNum) +{ + return getChannelConfigObject().isValidChannel(chNum); +} + +bool isValidAuthType(const uint8_t& chNum, const EAuthType& authType) +{ + return getChannelConfigObject().isValidAuthType(chNum, authType); +} + +EChannelSessSupported getChannelSessionSupport(const uint8_t& chNum) +{ + return getChannelConfigObject().getChannelSessionSupport(chNum); +} + +int getChannelActiveSessions(const uint8_t& chNum) +{ + return getChannelConfigObject().getChannelActiveSessions(chNum); +} + +ipmi_ret_t ipmiChannelInit() +{ + getChannelConfigObject(); + return IPMI_CC_OK; +} + +ipmi_ret_t getChannelInfo(const uint8_t& chNum, ChannelInfo& chInfo) +{ + return getChannelConfigObject().getChannelInfo(chNum, chInfo); +} + +ipmi_ret_t getChannelAccessData(const uint8_t& chNum, + ChannelAccess& chAccessData) +{ + return getChannelConfigObject().getChannelAccessData(chNum, chAccessData); +} + +ipmi_ret_t setChannelAccessData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag) +{ + return getChannelConfigObject().setChannelAccessData(chNum, chAccessData, + setFlag); +} + +ipmi_ret_t getChannelAccessPersistData(const uint8_t& chNum, + ChannelAccess& chAccessData) +{ + return getChannelConfigObject().getChannelAccessPersistData(chNum, + chAccessData); +} + +ipmi_ret_t setChannelAccessPersistData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag) +{ + return getChannelConfigObject().setChannelAccessPersistData( + chNum, chAccessData, setFlag); +} + +ipmi_ret_t getChannelAuthTypeSupported(const uint8_t& chNum, + uint8_t& authTypeSupported) +{ + return getChannelConfigObject().getChannelAuthTypeSupported( + chNum, authTypeSupported); +} + +ipmi_ret_t getChannelEnabledAuthType(const uint8_t& chNum, const uint8_t& priv, + EAuthType& authType) +{ + return getChannelConfigObject().getChannelEnabledAuthType(chNum, priv, + authType); +} + +} // namespace ipmi diff --git a/user_channel/channel_layer.hpp b/user_channel/channel_layer.hpp new file mode 100644 index 0000000..4c931df --- /dev/null +++ b/user_channel/channel_layer.hpp @@ -0,0 +1,275 @@ +/* +// Copyright (c) 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 <host-ipmid/ipmid-api.h> + +#include <string> + +namespace ipmi +{ + +static constexpr uint8_t maxIpmiChannels = 16; + +// IPMI return codes specific to channel +enum ipmi_channel_return_codes +{ + IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL = 0x82, + IPMI_CC_ACCESS_MODE_NOT_SUPPORTED_FOR_CHANEL = 0x83 +}; + +// IPMI Spec: Channel Protocol Type +enum class EChannelProtocolType : uint8_t +{ + na = 0x00, + ipmbV10 = 0x01, + icmbV11 = 0x02, + reserved = 0x03, + ipmiSmbus = 0x04, + kcs = 0x05, + smic = 0x06, + bt10 = 0x07, + bt15 = 0x08, + tMode = 0x09, + oem = 0x1C, +}; + +// IPMI Spec: Channel Medium Type +enum class EChannelMediumType : uint8_t +{ + reserved = 0x00, + ipmb = 0x01, + icmbV10 = 0x02, + icmbV09 = 0x03, + lan8032 = 0x04, + serial = 0x05, + otherLan = 0x06, + pciSmbus = 0x07, + smbusV11 = 0x08, + smbusV20 = 0x09, + usbV1x = 0x0A, + usbV2x = 0x0B, + systemInterface = 0x0C, + oem = 0x60, + unknown = 0x82, +}; + +// IPMI Spec: Channel Session Type +enum class EChannelSessSupported : uint8_t +{ + none = 0, + single = 1, + multi = 2, + any = 3, +}; + +// IPMI Spec: Channel Access Mode +enum class EChannelAccessMode : uint8_t +{ + disabled = 0, + preboot = 1, + alwaysAvail = 2, + shared = 3, +}; + +// IPMI Spec 2.0 : Authentication Types +enum class EAuthType : uint8_t +{ + none = (1 << 0x0), + md2 = (1 << 0x1), + md5 = (1 << 0x2), + reserved = (1 << 0x3), + straightPasswd = (1 << 0x4), + oem = (1 << 0x5), +}; + +// IPMI Spec: Access mode for channel access set/get +typedef enum +{ + doNotSet = 0x00, + nvData = 0x01, + activeData = 0x02, + reserved = 0x03, +} EChannelActionType; + +enum AccessSetFlag +{ + setAccessMode = (1 << 0), + setUserAuthEnabled = (1 << 1), + setMsgAuthEnabled = (1 << 2), + setAlertingEnabled = (1 << 3), + setPrivLimit = (1 << 4), +}; + +// Struct to store channel access data +struct ChannelAccess +{ + uint8_t accessMode; + bool userAuthDisabled; + bool perMsgAuthDisabled; + bool alertingDisabled; + uint8_t privLimit; +}; + +// Struct store channel info data +struct ChannelInfo +{ + uint8_t mediumType; + uint8_t protocolType; + uint8_t sessionSupported; + bool isIpmi; // Is session IPMI + // This is used in Get LAN Configuration parameter. + // This holds the supported AuthTypes for a given channel. + uint8_t authTypeSupported; +}; + +/** @brief determines valid channel + * + * @param[in] chNum- channel number + * + * @return true if valid, false otherwise + */ +bool isValidChannel(const uint8_t& chNum); + +/** @brief determines whether channel device exist + * + * @param[in] chNum - channel number + * + * @return true if valid, false otherwise + */ +bool doesDeviceExist(const uint8_t& chNum); + +/** @brief determines whether privilege limit is valid + * + * @param[in] privLimit - Privilege limit + * + * @return true if valid, false otherwise + */ +bool isValidPrivLimit(const uint8_t& privLimit); + +/** @brief determines whether access mode is valid + * + * @param[in] accessMode - Access mode + * + * @return true if valid, false otherwise + */ +bool isValidAccessMode(const uint8_t& accessMode); + +/** @brief determines valid authentication type based on channel number + * + * @param[in] chNum - channel number + * @param[in] authType - authentication type + * + * @return true if valid, false otherwise + */ +bool isValidAuthType(const uint8_t& chNum, const EAuthType& authType); + +/** @brief determines supported session type of a channel + * + * @param[in] chNum - channel number + * + * @return EChannelSessSupported - supported session type + */ +EChannelSessSupported getChannelSessionSupport(const uint8_t& chNum); + +/** @brief determines number of active sessions on a channel + * + * @param[in] chNum - channel number + * + * @return numer of active sessions + */ +int getChannelActiveSessions(const uint8_t& chNum); + +/** @brief initializes channel management + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t ipmiChannelInit(); + +/** @brief provides channel info details + * + * @param[in] chNum - channel number + * @param[out] chInfo - channel info details + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t getChannelInfo(const uint8_t& chNum, ChannelInfo& chInfo); + +/** @brief provides channel access data + * + * @param[in] chNum - channel number + * @param[out] chAccessData -channel access data + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t getChannelAccessData(const uint8_t& chNum, + ChannelAccess& chAccessData); + +/** @brief to set channel access data + * + * @param[in] chNum - channel number + * @param[in] chAccessData - channel access data + * @param[in] setFlag - flag to indicate updatable fields + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t setChannelAccessData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag); + +/** @brief to get channel access data persistent data + * + * @param[in] chNum - channel number + * @param[out] chAccessData - channel access data + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t getChannelAccessPersistData(const uint8_t& chNum, + ChannelAccess& chAccessData); + +/** @brief to set channel access data persistent data + * + * @param[in] chNum - channel number + * @param[in] chAccessData - channel access data + * @param[in] setFlag - flag to indicate updatable fields + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t setChannelAccessPersistData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag); + +/** @brief provides supported authentication type for the channel + * + * @param[in] chNum - channel number + * @param[out] authTypeSupported - supported authentication type + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t getChannelAuthTypeSupported(const uint8_t& chNum, + uint8_t& authTypeSupported); + +/** @brief provides enabled authentication type for the channel + * + * @param[in] chNum - channel number + * @param[in] priv - privilege + * @param[out] authType - enabled authentication type + * + * @return IPMI_CC_OK for success, others for failure. + */ +ipmi_ret_t getChannelEnabledAuthType(const uint8_t& chNum, const uint8_t& priv, + EAuthType& authType); + +} // namespace ipmi diff --git a/user_channel/channel_mgmt.cpp b/user_channel/channel_mgmt.cpp new file mode 100644 index 0000000..0db54b6 --- /dev/null +++ b/user_channel/channel_mgmt.cpp @@ -0,0 +1,1027 @@ +/* +// Copyright (c) 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. +*/ + +#include "channel_mgmt.hpp" + +#include "apphandler.hpp" + +#include <sys/stat.h> +#include <unistd.h> + +#include <boost/interprocess/sync/scoped_lock.hpp> +#include <cerrno> +#include <experimental/filesystem> +#include <fstream> +#include <phosphor-logging/log.hpp> +#include <unordered_map> + +namespace ipmi +{ + +using namespace phosphor::logging; + +static constexpr const char* channelAccessDefaultFilename = + "/usr/share/ipmi-providers/channel_access.json"; +static constexpr const char* channelConfigDefaultFilename = + "/usr/share/ipmi-providers/channel_config.json"; +static constexpr const char* channelNvDataFilename = + "/var/lib/ipmi/channel_access_nv.json"; +static constexpr const char* channelVolatileDataFilename = + "/run/ipmi/channel_access_volatile.json"; + +// STRING DEFINES: Should sync with key's in JSON +static constexpr const char* nameString = "name"; +static constexpr const char* isValidString = "is_valid"; +static constexpr const char* activeSessionsString = "active_sessions"; +static constexpr const char* channelInfoString = "channel_info"; +static constexpr const char* mediumTypeString = "medium_type"; +static constexpr const char* protocolTypeString = "protocol_type"; +static constexpr const char* sessionSupportedString = "session_supported"; +static constexpr const char* isIpmiString = "is_ipmi"; +static constexpr const char* authTypeSupportedString = "auth_type_supported"; +static constexpr const char* accessModeString = "access_mode"; +static constexpr const char* userAuthDisabledString = "user_auth_disabled"; +static constexpr const char* perMsgAuthDisabledString = "per_msg_auth_disabled"; +static constexpr const char* alertingDisabledString = "alerting_disabled"; +static constexpr const char* privLimitString = "priv_limit"; +static constexpr const char* authTypeEnabledString = "auth_type_enabled"; + +// Default values +static constexpr const char* defaultChannelName = "RESERVED"; +static constexpr const uint8_t defaultMediumType = + static_cast<uint8_t>(EChannelMediumType::reserved); +static constexpr const uint8_t defaultProtocolType = + static_cast<uint8_t>(EChannelProtocolType::reserved); +static constexpr const uint8_t defaultSessionSupported = + static_cast<uint8_t>(EChannelSessSupported::none); +static constexpr const uint8_t defaultAuthType = + static_cast<uint8_t>(EAuthType::none); +static constexpr const bool defaultIsIpmiState = false; + +// String mappings use in JSON config file +static std::unordered_map<std::string, EChannelMediumType> mediumTypeMap = { + {"reserved", EChannelMediumType::reserved}, + {"ipmb", EChannelMediumType::ipmb}, + {"icmb-v1.0", EChannelMediumType::icmbV10}, + {"icmb-v0.9", EChannelMediumType::icmbV09}, + {"lan-802.3", EChannelMediumType::lan8032}, + {"serial", EChannelMediumType::serial}, + {"other-lan", EChannelMediumType::otherLan}, + {"pci-smbus", EChannelMediumType::pciSmbus}, + {"smbus-v1.0", EChannelMediumType::smbusV11}, + {"smbus-v2.0", EChannelMediumType::smbusV20}, + {"usb-1x", EChannelMediumType::usbV1x}, + {"usb-2x", EChannelMediumType::usbV2x}, + {"system-interface", EChannelMediumType::systemInterface}, + {"oem", EChannelMediumType::oem}, + {"unknown", EChannelMediumType::unknown}}; + +static std::unordered_map<std::string, EChannelProtocolType> protocolTypeMap = { + {"na", EChannelProtocolType::na}, + {"ipmb-1.0", EChannelProtocolType::ipmbV10}, + {"icmb-2.0", EChannelProtocolType::icmbV11}, + {"reserved", EChannelProtocolType::reserved}, + {"ipmi-smbus", EChannelProtocolType::ipmiSmbus}, + {"kcs", EChannelProtocolType::kcs}, + {"smic", EChannelProtocolType::smic}, + {"bt-10", EChannelProtocolType::bt10}, + {"bt-15", EChannelProtocolType::bt15}, + {"tmode", EChannelProtocolType::tMode}, + {"oem", EChannelProtocolType::oem}}; + +static std::array<std::string, 4> accessModeList = { + "disabled", "pre-boot", "always_available", "shared"}; + +static std::array<std::string, 4> sessionSupportList = { + "session-less", "single-session", "multi-session", "session-based"}; + +static std::array<std::string, PRIVILEGE_OEM + 1> privList = { + "priv-reserved", "priv-callback", "priv-user", + "priv-operator", "priv-admin", "priv-oem"}; + +ChannelConfig& getChannelConfigObject() +{ + static ChannelConfig channelConfig; + return channelConfig; +} + +ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection()) +{ + std::ofstream mutexCleanUpFile; + mutexCleanUpFile.open(ipmiChMutexCleanupLockFile, + std::ofstream::out | std::ofstream::app); + if (!mutexCleanUpFile.good()) + { + log<level::DEBUG>("Unable to open mutex cleanup file"); + return; + } + mutexCleanUpFile.close(); + mutexCleanupLock = + boost::interprocess::file_lock(ipmiChMutexCleanupLockFile); + if (mutexCleanupLock.try_lock()) + { + boost::interprocess::named_recursive_mutex::remove(ipmiChannelMutex); + channelMutex = + std::make_unique<boost::interprocess::named_recursive_mutex>( + boost::interprocess::open_or_create, ipmiChannelMutex); + mutexCleanupLock.lock_sharable(); + } + else + { + mutexCleanupLock.lock_sharable(); + channelMutex = + std::make_unique<boost::interprocess::named_recursive_mutex>( + boost::interprocess::open_or_create, ipmiChannelMutex); + } + + initChannelPersistData(); +} + +bool ChannelConfig::isValidChannel(const uint8_t& chNum) +{ + if (chNum > maxIpmiChannels) + { + log<level::DEBUG>("Invalid channel ID - Out of range"); + return false; + } + + if (channelData[chNum].isChValid == false) + { + log<level::DEBUG>("Channel is not valid"); + return false; + } + + return true; +} + +EChannelSessSupported + ChannelConfig::getChannelSessionSupport(const uint8_t& chNum) +{ + EChannelSessSupported chSessSupport = + (EChannelSessSupported)channelData[chNum].chInfo.sessionSupported; + return chSessSupport; +} + +bool ChannelConfig::isValidAuthType(const uint8_t& chNum, + const EAuthType& authType) +{ + if ((authType < EAuthType::md2) || (authType > EAuthType::oem)) + { + log<level::DEBUG>("Invalid authentication type"); + return false; + } + + uint8_t authTypeSupported = channelData[chNum].chInfo.authTypeSupported; + if (!(authTypeSupported & (1 << static_cast<uint8_t>(authType)))) + { + log<level::DEBUG>("Authentication type is not supported."); + return false; + } + + return true; +} + +int ChannelConfig::getChannelActiveSessions(const uint8_t& chNum) +{ + // TODO: TEMPORARY FIX + // Channels active session count is managed separatly + // by monitoring channel session which includes LAN and + // RAKP layer changes. This will be updated, once the + // authentication part is implemented. + return channelData[chNum].activeSessCount; +} + +ipmi_ret_t ChannelConfig::getChannelInfo(const uint8_t& chNum, + ChannelInfo& chInfo) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + std::copy_n(reinterpret_cast<uint8_t*>(&channelData[chNum].chInfo), + sizeof(channelData[chNum].chInfo), + reinterpret_cast<uint8_t*>(&chInfo)); + + return IPMI_CC_OK; +} + +ipmi_ret_t ChannelConfig::getChannelAccessData(const uint8_t& chNum, + ChannelAccess& chAccessData) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) + { + log<level::DEBUG>("Session-less channel doesn't have access data."); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + if (checkAndReloadVolatileData() != 0) + { + return IPMI_CC_UNSPECIFIED_ERROR; + } + + std::copy_n( + reinterpret_cast<uint8_t*>(&channelData[chNum].chAccess.chVolatileData), + sizeof(channelData[chNum].chAccess.chVolatileData), + reinterpret_cast<uint8_t*>(&chAccessData)); + + return IPMI_CC_OK; +} + +ipmi_ret_t + ChannelConfig::setChannelAccessData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) + { + log<level::DEBUG>("Session-less channel doesn't have access data."); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + if ((setFlag & setAccessMode) && + (!isValidAccessMode(chAccessData.accessMode))) + { + log<level::DEBUG>("Invalid access mode specified"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + + if (checkAndReloadVolatileData() != 0) + { + return IPMI_CC_UNSPECIFIED_ERROR; + } + + if (setFlag & setAccessMode) + { + channelData[chNum].chAccess.chVolatileData.accessMode = + chAccessData.accessMode; + } + if (setFlag & setUserAuthEnabled) + { + channelData[chNum].chAccess.chVolatileData.userAuthDisabled = + chAccessData.userAuthDisabled; + } + if (setFlag & setMsgAuthEnabled) + { + channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled = + chAccessData.perMsgAuthDisabled; + } + if (setFlag & setAlertingEnabled) + { + channelData[chNum].chAccess.chVolatileData.alertingDisabled = + chAccessData.alertingDisabled; + } + if (setFlag & setPrivLimit) + { + channelData[chNum].chAccess.chVolatileData.privLimit = + chAccessData.privLimit; + } + + // Write Volatile data to file + if (writeChannelVolatileData() != 0) + { + log<level::DEBUG>("Failed to update the channel volatile data"); + return IPMI_CC_UNSPECIFIED_ERROR; + } + return IPMI_CC_OK; +} + +ipmi_ret_t + ChannelConfig::getChannelAccessPersistData(const uint8_t& chNum, + ChannelAccess& chAccessData) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) + { + log<level::DEBUG>("Session-less channel doesn't have access data."); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + if (checkAndReloadNVData() != 0) + { + return IPMI_CC_UNSPECIFIED_ERROR; + } + + std::copy_n(reinterpret_cast<uint8_t*>( + &channelData[chNum].chAccess.chNonVolatileData), + sizeof(channelData[chNum].chAccess.chNonVolatileData), + reinterpret_cast<uint8_t*>(&chAccessData)); + + return IPMI_CC_OK; +} + +ipmi_ret_t ChannelConfig::setChannelAccessPersistData( + const uint8_t& chNum, const ChannelAccess& chAccessData, + const uint8_t& setFlag) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) + { + log<level::DEBUG>("Session-less channel doesn't have access data."); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + if ((setFlag & setAccessMode) && + (!isValidAccessMode(chAccessData.accessMode))) + { + log<level::DEBUG>("Invalid access mode specified"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + + if (checkAndReloadNVData() != 0) + { + return IPMI_CC_UNSPECIFIED_ERROR; + } + + if (setFlag & setAccessMode) + { + channelData[chNum].chAccess.chNonVolatileData.accessMode = + chAccessData.accessMode; + } + if (setFlag & setUserAuthEnabled) + { + channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled = + chAccessData.userAuthDisabled; + } + if (setFlag & setMsgAuthEnabled) + { + channelData[chNum].chAccess.chNonVolatileData.perMsgAuthDisabled = + chAccessData.perMsgAuthDisabled; + } + if (setFlag & setAlertingEnabled) + { + channelData[chNum].chAccess.chNonVolatileData.alertingDisabled = + chAccessData.alertingDisabled; + } + if (setFlag & setPrivLimit) + { + channelData[chNum].chAccess.chNonVolatileData.privLimit = + chAccessData.privLimit; + } + + // Write persistent data to file + if (writeChannelPersistData() != 0) + { + log<level::DEBUG>("Failed to update the presist data file"); + return IPMI_CC_UNSPECIFIED_ERROR; + } + return IPMI_CC_OK; +} + +ipmi_ret_t + ChannelConfig::getChannelAuthTypeSupported(const uint8_t& chNum, + uint8_t& authTypeSupported) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + authTypeSupported = channelData[chNum].chInfo.authTypeSupported; + return IPMI_CC_OK; +} + +ipmi_ret_t ChannelConfig::getChannelEnabledAuthType(const uint8_t& chNum, + const uint8_t& priv, + EAuthType& authType) +{ + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Invalid channel"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (getChannelSessionSupport(chNum) == EChannelSessSupported::none) + { + log<level::DEBUG>("Sessionless channel doesn't have access data."); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (!isValidPrivLimit(priv)) + { + log<level::DEBUG>("Invalid privilege specified."); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + // TODO: Hardcoded for now. Need to implement. + authType = EAuthType::none; + + return IPMI_CC_OK; +} + +std::time_t ChannelConfig::getUpdatedFileTime(const std::string& fileName) +{ + struct stat fileStat; + if (stat(fileName.c_str(), &fileStat) != 0) + { + log<level::DEBUG>("Error in getting last updated time stamp"); + return -EIO; + } + return fileStat.st_mtime; +} + +EChannelAccessMode + ChannelConfig::convertToAccessModeIndex(const std::string& mode) +{ + auto iter = std::find(accessModeList.begin(), accessModeList.end(), mode); + if (iter == accessModeList.end()) + { + log<level::ERR>("Invalid access mode.", + entry("MODE_STR=%s", mode.c_str())); + throw std::invalid_argument("Invalid access mode."); + } + + return static_cast<EChannelAccessMode>( + std::distance(accessModeList.begin(), iter)); +} + +std::string ChannelConfig::convertToAccessModeString(const uint8_t& value) +{ + if (accessModeList.size() <= value) + { + log<level::ERR>("Invalid access mode.", entry("MODE_IDX=%d", value)); + throw std::invalid_argument("Invalid access mode."); + } + + return accessModeList.at(value); +} + +CommandPrivilege + ChannelConfig::convertToPrivLimitIndex(const std::string& value) +{ + auto iter = std::find(privList.begin(), privList.end(), value); + if (iter == privList.end()) + { + log<level::ERR>("Invalid privilege.", + entry("PRIV_STR=%s", value.c_str())); + throw std::invalid_argument("Invalid privilege."); + } + + return static_cast<CommandPrivilege>(std::distance(privList.begin(), iter)); +} + +std::string ChannelConfig::convertToPrivLimitString(const uint8_t& value) +{ + if (privList.size() <= value) + { + log<level::ERR>("Invalid privilege.", entry("PRIV_IDX=%d", value)); + throw std::invalid_argument("Invalid privilege."); + } + + return privList.at(value); +} + +EChannelSessSupported + ChannelConfig::convertToSessionSupportIndex(const std::string& value) +{ + auto iter = + std::find(sessionSupportList.begin(), sessionSupportList.end(), value); + if (iter == sessionSupportList.end()) + { + log<level::ERR>("Invalid session supported.", + entry("SESS_STR=%s", value.c_str())); + throw std::invalid_argument("Invalid session supported."); + } + + return static_cast<EChannelSessSupported>( + std::distance(sessionSupportList.begin(), iter)); +} + +EChannelMediumType + ChannelConfig::convertToMediumTypeIndex(const std::string& value) +{ + std::unordered_map<std::string, EChannelMediumType>::iterator it = + mediumTypeMap.find(value); + if (it == mediumTypeMap.end()) + { + log<level::ERR>("Invalid medium type.", + entry("MEDIUM_STR=%s", value.c_str())); + throw std::invalid_argument("Invalid medium type."); + } + + return static_cast<EChannelMediumType>(it->second); +} + +EChannelProtocolType + ChannelConfig::convertToProtocolTypeIndex(const std::string& value) +{ + std::unordered_map<std::string, EChannelProtocolType>::iterator it = + protocolTypeMap.find(value); + if (it == protocolTypeMap.end()) + { + log<level::ERR>("Invalid protocol type.", + entry("PROTO_STR=%s", value.c_str())); + throw std::invalid_argument("Invalid protocol type."); + } + + return static_cast<EChannelProtocolType>(it->second); +} + +Json ChannelConfig::readJsonFile(const std::string& configFile) +{ + std::ifstream jsonFile(configFile); + if (!jsonFile.good()) + { + log<level::ERR>("JSON file not found"); + return nullptr; + } + + Json data = nullptr; + try + { + data = Json::parse(jsonFile, nullptr, false); + } + catch (Json::parse_error& e) + { + log<level::DEBUG>("Corrupted channel config.", + entry("MSG: %s", e.what())); + throw std::runtime_error("Corrupted channel config file"); + } + + return data; +} + +int ChannelConfig::writeJsonFile(const std::string& configFile, + const Json& jsonData) +{ + std::ofstream jsonFile(configFile); + if (!jsonFile.good()) + { + log<level::ERR>("JSON file not found"); + return -EIO; + } + + // Write JSON to file + jsonFile << jsonData; + + jsonFile.flush(); + return 0; +} + +void ChannelConfig::setDefaultChannelConfig(const uint8_t& chNum, + const std::string& chName) +{ + channelData[chNum].chName = chName; + channelData[chNum].chID = chNum; + channelData[chNum].isChValid = false; + channelData[chNum].activeSessCount = 0; + + channelData[chNum].chInfo.mediumType = defaultMediumType; + channelData[chNum].chInfo.protocolType = defaultProtocolType; + channelData[chNum].chInfo.sessionSupported = defaultSessionSupported; + channelData[chNum].chInfo.isIpmi = defaultIsIpmiState; + channelData[chNum].chInfo.authTypeSupported = defaultAuthType; +} + +int ChannelConfig::loadChannelConfig() +{ + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + + Json data = readJsonFile(channelConfigDefaultFilename); + if (data == nullptr) + { + log<level::DEBUG>("Error in opening IPMI Channel data file"); + return -EIO; + } + + try + { + // Fill in global structure + for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) + { + std::fill(reinterpret_cast<uint8_t*>(&channelData[chNum]), + reinterpret_cast<uint8_t*>(&channelData[chNum]) + + sizeof(ChannelData), + 0); + std::string chKey = std::to_string(chNum); + Json jsonChData = data[chKey].get<Json>(); + if (jsonChData.is_null()) + { + log<level::WARNING>( + "Channel not configured so loading default.", + entry("CHANNEL_NUM:%d", chNum)); + // If user didn't want to configure specific channel (say + // reserved channel), then load that index with default values. + std::string chName(defaultChannelName); + setDefaultChannelConfig(chNum, chName); + } + else + { + std::string chName = jsonChData[nameString].get<std::string>(); + channelData[chNum].chName = chName; + channelData[chNum].chID = chNum; + channelData[chNum].isChValid = + jsonChData[isValidString].get<bool>(); + channelData[chNum].activeSessCount = + jsonChData.value(activeSessionsString, 0); + Json jsonChInfo = jsonChData[channelInfoString].get<Json>(); + if (jsonChInfo.is_null()) + { + log<level::ERR>("Invalid/corrupted channel config file"); + return -EBADMSG; + } + else + { + std::string medTypeStr = + jsonChInfo[mediumTypeString].get<std::string>(); + channelData[chNum].chInfo.mediumType = static_cast<uint8_t>( + convertToMediumTypeIndex(medTypeStr)); + std::string protoTypeStr = + jsonChInfo[protocolTypeString].get<std::string>(); + channelData[chNum].chInfo.protocolType = + static_cast<uint8_t>( + convertToProtocolTypeIndex(protoTypeStr)); + std::string sessStr = + jsonChInfo[sessionSupportedString].get<std::string>(); + channelData[chNum].chInfo.sessionSupported = + static_cast<uint8_t>( + convertToSessionSupportIndex(sessStr)); + channelData[chNum].chInfo.isIpmi = + jsonChInfo[isIpmiString].get<bool>(); + channelData[chNum].chInfo.authTypeSupported = + defaultAuthType; + } + } + } + } + catch (const Json::exception& e) + { + log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what())); + return -EBADMSG; + } + catch (const std::invalid_argument& e) + { + log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what())); + return -EBADMSG; + } + + return 0; +} + +int ChannelConfig::readChannelVolatileData() +{ + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + + Json data = readJsonFile(channelVolatileDataFilename); + if (data == nullptr) + { + log<level::DEBUG>("Error in opening IPMI Channel data file"); + return -EIO; + } + + try + { + // Fill in global structure + for (auto it = data.begin(); it != data.end(); ++it) + { + std::string chKey = it.key(); + uint8_t chNum = std::stoi(chKey, nullptr, 10); + if ((chNum < 0) || (chNum > maxIpmiChannels)) + { + log<level::DEBUG>( + "Invalid channel access entry in config file"); + throw std::out_of_range("Out of range - channel number"); + } + Json jsonChData = it.value(); + if (!jsonChData.is_null()) + { + std::string accModeStr = + jsonChData[accessModeString].get<std::string>(); + channelData[chNum].chAccess.chVolatileData.accessMode = + static_cast<uint8_t>(convertToAccessModeIndex(accModeStr)); + channelData[chNum].chAccess.chVolatileData.userAuthDisabled = + jsonChData[userAuthDisabledString].get<bool>(); + channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled = + jsonChData[perMsgAuthDisabledString].get<bool>(); + channelData[chNum].chAccess.chVolatileData.alertingDisabled = + jsonChData[alertingDisabledString].get<bool>(); + std::string privStr = + jsonChData[privLimitString].get<std::string>(); + channelData[chNum].chAccess.chVolatileData.privLimit = + static_cast<uint8_t>(convertToPrivLimitIndex(privStr)); + } + else + { + log<level::ERR>( + "Invalid/corrupted volatile channel access file", + entry("FILE: %s", channelVolatileDataFilename)); + throw std::runtime_error( + "Corrupted volatile channel access file"); + } + } + } + catch (const Json::exception& e) + { + log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what())); + throw std::runtime_error("Corrupted volatile channel access file"); + } + catch (const std::invalid_argument& e) + { + log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what())); + throw std::runtime_error("Corrupted volatile channel access file"); + } + + // Update the timestamp + voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename); + return 0; +} + +int ChannelConfig::readChannelPersistData() +{ + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + + Json data = readJsonFile(channelNvDataFilename); + if (data == nullptr) + { + log<level::DEBUG>("Error in opening IPMI Channel data file"); + return -EIO; + } + + try + { + // Fill in global structure + for (auto it = data.begin(); it != data.end(); ++it) + { + std::string chKey = it.key(); + uint8_t chNum = std::stoi(chKey, nullptr, 10); + if ((chNum < 0) || (chNum > maxIpmiChannels)) + { + log<level::DEBUG>( + "Invalid channel access entry in config file"); + throw std::out_of_range("Out of range - channel number"); + } + Json jsonChData = it.value(); + if (!jsonChData.is_null()) + { + std::string accModeStr = + jsonChData[accessModeString].get<std::string>(); + channelData[chNum].chAccess.chNonVolatileData.accessMode = + static_cast<uint8_t>(convertToAccessModeIndex(accModeStr)); + channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled = + jsonChData[userAuthDisabledString].get<bool>(); + channelData[chNum] + .chAccess.chNonVolatileData.perMsgAuthDisabled = + jsonChData[perMsgAuthDisabledString].get<bool>(); + channelData[chNum].chAccess.chNonVolatileData.alertingDisabled = + jsonChData[alertingDisabledString].get<bool>(); + std::string privStr = + jsonChData[privLimitString].get<std::string>(); + channelData[chNum].chAccess.chNonVolatileData.privLimit = + static_cast<uint8_t>(convertToPrivLimitIndex(privStr)); + } + else + { + log<level::ERR>("Invalid/corrupted nv channel access file", + entry("FILE:%s", channelNvDataFilename)); + throw std::runtime_error("Corrupted nv channel access file"); + } + } + } + catch (const Json::exception& e) + { + log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what())); + throw std::runtime_error("Corrupted nv channel access file"); + } + catch (const std::invalid_argument& e) + { + log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + throw std::runtime_error("Corrupted nv channel access file"); + } + + // Update the timestamp + nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename); + return 0; +} + +int ChannelConfig::writeChannelVolatileData() +{ + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + Json outData; + + try + { + for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) + { + if (getChannelSessionSupport(chNum) != EChannelSessSupported::none) + { + Json jsonObj; + std::string chKey = std::to_string(chNum); + std::string accModeStr = convertToAccessModeString( + channelData[chNum].chAccess.chVolatileData.accessMode); + jsonObj[accessModeString] = accModeStr; + jsonObj[userAuthDisabledString] = + channelData[chNum].chAccess.chVolatileData.userAuthDisabled; + jsonObj[perMsgAuthDisabledString] = + channelData[chNum] + .chAccess.chVolatileData.perMsgAuthDisabled; + jsonObj[alertingDisabledString] = + channelData[chNum].chAccess.chVolatileData.alertingDisabled; + std::string privStr = convertToPrivLimitString( + channelData[chNum].chAccess.chVolatileData.privLimit); + jsonObj[privLimitString] = privStr; + + outData[chKey] = jsonObj; + } + } + } + catch (const std::invalid_argument& e) + { + log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + return -EINVAL; + } + + if (writeJsonFile(channelVolatileDataFilename, outData) != 0) + { + log<level::DEBUG>("Error in write JSON data to file"); + return -EIO; + } + + // Update the timestamp + voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename); + return 0; +} + +int ChannelConfig::writeChannelPersistData() +{ + boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex> + channelLock{*channelMutex}; + Json outData; + + try + { + for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++) + { + if (getChannelSessionSupport(chNum) != EChannelSessSupported::none) + { + Json jsonObj; + std::string chKey = std::to_string(chNum); + std::string accModeStr = convertToAccessModeString( + channelData[chNum].chAccess.chNonVolatileData.accessMode); + jsonObj[accessModeString] = accModeStr; + jsonObj[userAuthDisabledString] = + channelData[chNum] + .chAccess.chNonVolatileData.userAuthDisabled; + jsonObj[perMsgAuthDisabledString] = + channelData[chNum] + .chAccess.chNonVolatileData.perMsgAuthDisabled; + jsonObj[alertingDisabledString] = + channelData[chNum] + .chAccess.chNonVolatileData.alertingDisabled; + std::string privStr = convertToPrivLimitString( + channelData[chNum].chAccess.chNonVolatileData.privLimit); + jsonObj[privLimitString] = privStr; + + outData[chKey] = jsonObj; + } + } + } + catch (const std::invalid_argument& e) + { + log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what())); + return -EINVAL; + } + + if (writeJsonFile(channelNvDataFilename, outData) != 0) + { + log<level::DEBUG>("Error in write JSON data to file"); + return -EIO; + } + + // Update the timestamp + nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename); + return 0; +} + +int ChannelConfig::checkAndReloadNVData() +{ + std::time_t updateTime = getUpdatedFileTime(channelNvDataFilename); + int ret = 0; + if (updateTime != nvFileLastUpdatedTime || updateTime == -EIO) + { + try + { + ret = readChannelPersistData(); + } + catch (const std::exception& e) + { + log<level::ERR>("Exception caught in readChannelPersistData.", + entry("MSG=%s", e.what())); + ret = -EIO; + } + } + return ret; +} + +int ChannelConfig::checkAndReloadVolatileData() +{ + std::time_t updateTime = getUpdatedFileTime(channelVolatileDataFilename); + int ret = 0; + if (updateTime != voltFileLastUpdatedTime || updateTime == -EIO) + { + try + { + ret = readChannelVolatileData(); + } + catch (const std::exception& e) + { + log<level::ERR>("Exception caught in readChannelVolatileData.", + entry("MSG=%s", e.what())); + ret = -EIO; + } + } + return ret; +} + +void ChannelConfig::initChannelPersistData() +{ + /* Always read the channel config */ + if (loadChannelConfig() != 0) + { + log<level::ERR>("Failed to read channel config file"); + throw std::ios_base::failure("Failed to load channel configuration"); + } + + /* Populate the channel persist data */ + if (readChannelPersistData() != 0) + { + // Copy default NV data to RW location + std::experimental::filesystem::copy_file(channelAccessDefaultFilename, + channelNvDataFilename); + + // Load the channel access NV data + if (readChannelPersistData() != 0) + { + log<level::ERR>("Failed to read channel access NV data"); + throw std::ios_base::failure( + "Failed to read channel access NV configuration"); + } + } + + // First check the volatile data file + // If not present, load the default values + if (readChannelVolatileData() != 0) + { + // Copy default volatile data to temporary location + // NV file(channelNvDataFilename) must have created by now. + std::experimental::filesystem::copy_file(channelNvDataFilename, + channelVolatileDataFilename); + + // Load the channel access volatile data + if (readChannelVolatileData() != 0) + { + log<level::ERR>("Failed to read channel access volatile data"); + throw std::ios_base::failure( + "Failed to read channel access volatile configuration"); + } + } + return; +} + +} // namespace ipmi diff --git a/user_channel/channel_mgmt.hpp b/user_channel/channel_mgmt.hpp new file mode 100644 index 0000000..053fa39 --- /dev/null +++ b/user_channel/channel_mgmt.hpp @@ -0,0 +1,334 @@ +/* +// Copyright (c) 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 "channel_layer.hpp" + +#include <boost/interprocess/sync/file_lock.hpp> +#include <boost/interprocess/sync/named_recursive_mutex.hpp> +#include <cstdint> +#include <ctime> +#include <nlohmann/json.hpp> +#include <sdbusplus/bus.hpp> + +namespace ipmi +{ + +using Json = nlohmann::json; + +static constexpr const char* ipmiChannelMutex = "ipmi_channel_mutex"; +static constexpr const char* ipmiChMutexCleanupLockFile = + "/var/lib/ipmi/ipmi_channel_mutex_cleanup"; + +struct ChannelAccessData +{ + ChannelAccess chNonVolatileData; + ChannelAccess chVolatileData; +}; + +struct ChannelData +{ + std::string chName; + uint8_t chID; + bool isChValid; + uint8_t activeSessCount; + ChannelInfo chInfo; + ChannelAccessData chAccess; +}; + +class ChannelConfig; + +ChannelConfig& getChannelConfigObject(); + +class ChannelConfig +{ + public: + ChannelConfig(const ChannelConfig&) = delete; + ChannelConfig& operator=(const ChannelConfig&) = delete; + ChannelConfig(ChannelConfig&&) = delete; + ChannelConfig& operator=(ChannelConfig&&) = delete; + + ~ChannelConfig() = default; + ChannelConfig(); + + /** @brief determines valid channel + * + * @param[in] chNum - channel number + * + * @return true if valid, false otherwise + */ + bool isValidChannel(const uint8_t& chNum); + + /** @brief determines valid authentication type + * + * @param[in] chNum - channel number + * @param[in] authType - authentication type + * + * @return true if valid, false otherwise + */ + bool isValidAuthType(const uint8_t& chNum, const EAuthType& authType); + + /** @brief determines supported session type of a channel + * + * @param[in] chNum - channel number + * + * @return EChannelSessSupported - supported session type + */ + EChannelSessSupported getChannelSessionSupport(const uint8_t& chNum); + + /** @brief determines number of active sessions on a channel + * + * @param[in] chNum - channel number + * + * @return numer of active sessions + */ + int getChannelActiveSessions(const uint8_t& chNum); + + /** @brief provides channel info details + * + * @param[in] chNum - channel number + * @param[out] chInfo - channel info details + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t getChannelInfo(const uint8_t& chNum, ChannelInfo& chInfo); + + /** @brief provides channel access data + * + * @param[in] chNum - channel number + * @param[out] chAccessData - channel access data + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t getChannelAccessData(const uint8_t& chNum, + ChannelAccess& chAccessData); + + /** @brief to set channel access data + * + * @param[in] chNum - channel number + * @param[in] chAccessData - channel access data + * @param[in] setFlag - flag to indicate updatable fields + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t setChannelAccessData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag); + + /** @brief to get channel access data persistent data + * + * @param[in] chNum - channel number + * @param[out] chAccessData - channel access data + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t getChannelAccessPersistData(const uint8_t& chNum, + ChannelAccess& chAccessData); + + /** @brief to set channel access data persistent data + * + * @param[in] chNum - channel number + * @param[in] chAccessData - channel access data + * @param[in] setFlag - flag to indicate updatable fields + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t setChannelAccessPersistData(const uint8_t& chNum, + const ChannelAccess& chAccessData, + const uint8_t& setFlag); + + /** @brief provides supported authentication type for the channel + * + * @param[in] chNum - channel number + * @param[out] authTypeSupported - supported authentication type + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t getChannelAuthTypeSupported(const uint8_t& chNum, + uint8_t& authTypeSupported); + + /** @brief provides enabled authentication type for the channel + * + * @param[in] chNum - channel number + * @param[in] priv - privilege + * @param[out] authType - enabled authentication type + * + * @return IPMI_CC_OK for success, others for failure. + */ + ipmi_ret_t getChannelEnabledAuthType(const uint8_t& chNum, + const uint8_t& priv, + EAuthType& authType); + + std::unique_ptr<boost::interprocess::named_recursive_mutex> channelMutex{ + nullptr}; + + private: + ChannelData channelData[maxIpmiChannels]; + std::time_t nvFileLastUpdatedTime; + std::time_t voltFileLastUpdatedTime; + std::time_t getUpdatedFileTime(const std::string& fileName); + boost::interprocess::file_lock mutexCleanupLock; + sdbusplus::bus::bus bus; + + /** @brief function to initialize persistent channel configuration + * + */ + void initChannelPersistData(); + + /** @brief function to set default channel configuration based on channel + * number + * + * @param[in] chNum - channel number + * @param[in] chName - channel name + */ + void setDefaultChannelConfig(const uint8_t& chNum, + const std::string& chName); + + /** @brief function to load all channel configuration + * + * @return 0 for success, -errno for failure. + */ + int loadChannelConfig(); + + /** @brief function to read persistent channel data + * + * @return 0 for success, -errno for failure. + */ + int readChannelPersistData(); + + /** @brief function to write persistent channel configuration to config file + * + * @return 0 for success, -errno for failure. + */ + int writeChannelPersistData(); + + /** @brief function to read volatile channel data + * + * @return 0 for success, -errno for failure. + */ + int readChannelVolatileData(); + + /** @brief function to write volatile channel configuration to config file + * + * @return 0 for success, -errno for failure. + */ + int writeChannelVolatileData(); + + /** @brief function to check and reload persistent channel data + * + * @return 0 for success, -errno for failure. + */ + int checkAndReloadNVData(); + + /** @brief function to check and reload volatile channel data + * + * @return 0 for success, -errno for failure. + */ + int checkAndReloadVolatileData(); + + /** @brief function to read json config file + * + * @param[in] configFile - configuration file name + * + * @return Json object + */ + Json readJsonFile(const std::string& configFile); + + /** @brief function to write json config file + * + * @param[in] configFile - configuration file name + * @param[in] jsonData - json object + * + * @return 0 for success, -errno for failure. + */ + int writeJsonFile(const std::string& configFile, const Json& jsonData); + + /** @brief function to convert system access mode to Channel access mode + * type + * + * @param[in] mode - access mode in string + * + * @return Channel access mode. + */ + EChannelAccessMode convertToAccessModeIndex(const std::string& mode); + + /** @brief function to convert access mode value to string + * + * @param[in] value - acess mode value + * + * @return access mode in string + */ + 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 + * + * @return privilege in string + */ + std::string convertToPrivLimitString(const uint8_t& value); + + /** @brief function to convert session support string to value type + * + * @param[in] value - session support type in string + * + * @return support session type + */ + EChannelSessSupported + convertToSessionSupportIndex(const std::string& value); + + /** @brief function to convert medium type string to value type + * + * @param[in] value - medium type in string + * + * @return channel medium type + */ + EChannelMediumType convertToMediumTypeIndex(const std::string& value); + + /** @brief function to convert protocol type string to value type + * + * @param[in] value - protocol type in string + * + * @return channel protocol type + */ + EChannelProtocolType convertToProtocolTypeIndex(const std::string& value); + + /** @brief function to convert channel number to channel index + * + * @param[in] chNum - channel number + * + * @return channel index + */ + uint8_t convertToChannelIndexNumber(const uint8_t& chNum); + + /** @brief function to convert channel name to network interface name + * + * @param[in] value - channel interface name - ipmi centric + * + * @return network channel interface name + */ + std::string convertToNetInterface(const std::string& value); +}; + +} // namespace ipmi diff --git a/user_channel/channelcommands.cpp b/user_channel/channelcommands.cpp new file mode 100644 index 0000000..c5ef8b7 --- /dev/null +++ b/user_channel/channelcommands.cpp @@ -0,0 +1,391 @@ +/* +// Copyright (c) 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. +*/ + +#include "channelcommands.hpp" + +#include "apphandler.hpp" +#include "channel_layer.hpp" + +#include <phosphor-logging/log.hpp> +#include <regex> + +using namespace phosphor::logging; + +namespace ipmi +{ + +struct setChannelAccessReq +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t chNum : 4; + uint8_t reserved_1 : 4; + uint8_t accessMode : 3; + uint8_t usrAuthDisabled : 1; + uint8_t msgAuthDisabled : 1; + uint8_t alertDisabled : 1; + uint8_t accessSetMode : 2; + uint8_t privLimit : 4; + uint8_t reserved_2 : 2; + uint8_t privSetMode : 2; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t reserved_1 : 4; + uint8_t chNum : 4; + uint8_t accessSetMode : 2; + uint8_t alertDisabled : 1; + uint8_t msgAuthDisabled : 1; + uint8_t usrAuthDisabled : 1; + uint8_t accessMode : 3; + uint8_t privSetMode : 2; + uint8_t reserved_2 : 2; + uint8_t privLimit : 4; +#endif + +} __attribute__((packed)); + +struct getChannelAccessReq +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t chNum : 4; + uint8_t reserved_1 : 4; + uint8_t reserved_2 : 6; + uint8_t accessSetMode : 2; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t reserved_1 : 4; + uint8_t chNum : 4; + uint8_t accessSetMode : 2; + uint8_t reserved_2 : 6; +#endif +} __attribute__((packed)); + +struct getChannelAccessResp +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t accessMode : 3; + uint8_t usrAuthDisabled : 1; + uint8_t msgAuthDisabled : 1; + uint8_t alertDisabled : 1; + uint8_t reserved_1 : 2; + uint8_t privLimit : 4; + uint8_t reserved_2 : 4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t reserved_1 : 2; + uint8_t alertDisabled : 1; + uint8_t msgAuthDisabled : 1; + uint8_t usrAuthDisabled : 1; + uint8_t accessMode : 3; + uint8_t reserved_2 : 4; + uint8_t privLimit : 4; +#endif +} __attribute__((packed)); + +struct getChannelInfoReq +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t chNum : 4; + uint8_t reserved_1 : 4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t reserved_1 : 4; + uint8_t chNum : 4; +#endif +} __attribute__((packed)); + +struct getChannelInfoResp +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t chNum : 4; + uint8_t reserved_1 : 4; + uint8_t mediumType : 7; + uint8_t reserved_2 : 1; + uint8_t msgProtType : 5; + uint8_t reserved_3 : 3; + uint8_t actSessCount : 6; + uint8_t sessType : 2; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t reserved_1 : 4; + uint8_t chNum : 4; + uint8_t reserved_2 : 1; + uint8_t mediumType : 7; + uint8_t reserved_3 : 3; + uint8_t msgProtType : 5; + uint8_t sessType : 2; + uint8_t actSessCount : 6; +#endif + uint8_t vendorId[3]; + uint8_t auxChInfo[2]; +} __attribute__((packed)); + +ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, + ipmi_data_len_t data_len, + ipmi_context_t context) +{ + const setChannelAccessReq* req = static_cast<setChannelAccessReq*>(request); + size_t reqLength = *data_len; + + *data_len = 0; + + if (reqLength != sizeof(*req)) + { + log<level::DEBUG>("Set channel access - Invalid Length"); + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + + // TODO: Self channel number (0xE) has to be determined. + uint8_t chNum = req->chNum; + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Set channel access - Parameter out of range"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (EChannelSessSupported::none == getChannelSessionSupport(chNum)) + { + log<level::DEBUG>("Set channel access - No support on channel"); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + ChannelAccess chActData; + ChannelAccess chNVData; + uint8_t setActFlag = 0; + uint8_t setNVFlag = 0; + ipmi_ret_t compCode = IPMI_CC_OK; + + switch (req->accessSetMode) + { + case doNotSet: + // Do nothing + break; + case nvData: + chNVData.accessMode = req->accessMode; + chNVData.userAuthDisabled = req->usrAuthDisabled; + chNVData.perMsgAuthDisabled = req->msgAuthDisabled; + chNVData.alertingDisabled = req->alertDisabled; + setNVFlag |= (setAccessMode | setUserAuthEnabled | + setMsgAuthEnabled | setAlertingEnabled); + break; + case activeData: + chActData.accessMode = req->accessMode; + chActData.userAuthDisabled = req->usrAuthDisabled; + chActData.perMsgAuthDisabled = req->msgAuthDisabled; + chActData.alertingDisabled = req->alertDisabled; + setActFlag |= (setAccessMode | setUserAuthEnabled | + setMsgAuthEnabled | setAlertingEnabled); + break; + case reserved: + default: + log<level::DEBUG>("Set channel access - Invalid access set mode"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + switch (req->privSetMode) + { + case doNotSet: + // Do nothing + break; + case nvData: + chNVData.privLimit = req->privLimit; + setNVFlag |= setPrivLimit; + break; + case activeData: + chActData.privLimit = req->privLimit; + setActFlag |= setPrivLimit; + break; + case reserved: + default: + log<level::DEBUG>("Set channel access - Invalid access priv mode"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (setNVFlag != 0) + { + compCode = setChannelAccessPersistData(chNum, chNVData, setNVFlag); + if (compCode != IPMI_CC_OK) + { + log<level::DEBUG>("Set channel access - Failed to set access data"); + return compCode; + } + } + + if (setActFlag != 0) + { + compCode = setChannelAccessData(chNum, chActData, setActFlag); + if (compCode != IPMI_CC_OK) + { + log<level::DEBUG>("Set channel access - Failed to set access data"); + return compCode; + } + } + + return IPMI_CC_OK; +} + +ipmi_ret_t ipmiGetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, + ipmi_data_len_t data_len, + ipmi_context_t context) +{ + const getChannelAccessReq* req = static_cast<getChannelAccessReq*>(request); + size_t reqLength = *data_len; + + *data_len = 0; + + if (reqLength != sizeof(*req)) + { + log<level::DEBUG>("Get channel access - Invalid Length"); + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + + // TODO: Self channel number (0xE) has to be determined. + uint8_t chNum = req->chNum; + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Get channel access - Parameter out of range"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if ((req->accessSetMode == doNotSet) || (req->accessSetMode == reserved)) + { + log<level::DEBUG>("Get channel access - Invalid Access mode"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + if (EChannelSessSupported::none == getChannelSessionSupport(chNum)) + { + log<level::DEBUG>("Get channel access - No support on channel"); + return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL; + } + + getChannelAccessResp* resp = static_cast<getChannelAccessResp*>(response); + + std::fill(reinterpret_cast<uint8_t*>(resp), + reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); + + ChannelAccess chAccess; + ipmi_ret_t compCode = IPMI_CC_OK; + + if (req->accessSetMode == nvData) + { + compCode = getChannelAccessPersistData(chNum, chAccess); + } + else if (req->accessSetMode == activeData) + { + compCode = getChannelAccessData(chNum, chAccess); + } + + if (compCode != IPMI_CC_OK) + { + return compCode; + } + + resp->accessMode = chAccess.accessMode; + resp->usrAuthDisabled = chAccess.userAuthDisabled; + resp->msgAuthDisabled = chAccess.perMsgAuthDisabled; + resp->alertDisabled = chAccess.alertingDisabled; + resp->privLimit = chAccess.privLimit; + + *data_len = sizeof(*resp); + return IPMI_CC_OK; +} + +ipmi_ret_t ipmiGetChannelInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len, ipmi_context_t context) +{ + const getChannelInfoReq* req = static_cast<getChannelInfoReq*>(request); + size_t reqLength = *data_len; + + *data_len = 0; + + if (reqLength != sizeof(*req)) + { + log<level::DEBUG>("Get channel info - Invalid Length"); + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + + // TODO: Self channel number (0xE) has to be determined. + uint8_t chNum = req->chNum; + if (!isValidChannel(chNum)) + { + log<level::DEBUG>("Get channel info - Parameter out of range"); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + // Check the existance of device for session-less channels. + if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) && + (!(doesDeviceExist(chNum)))) + { + log<level::DEBUG>("Get channel info - Device not exist"); + return IPMI_CC_PARM_OUT_OF_RANGE; + } + + getChannelInfoResp* resp = static_cast<getChannelInfoResp*>(response); + + std::fill(reinterpret_cast<uint8_t*>(resp), + reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); + + ChannelInfo chInfo; + ipmi_ret_t compCode = getChannelInfo(chNum, chInfo); + if (compCode != IPMI_CC_OK) + { + return compCode; + } + + resp->chNum = chNum; + resp->mediumType = chInfo.mediumType; + resp->msgProtType = chInfo.protocolType; + resp->actSessCount = getChannelActiveSessions(chNum); + resp->sessType = chInfo.sessionSupported; + + // IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal) + resp->vendorId[0] = 0xF2; + resp->vendorId[1] = 0x1B; + resp->vendorId[2] = 0x00; + + // Auxiliary Channel info - byte 1:2 + // TODO: For System Interface(0xF) and OEM channel types, this needs + // to be changed acoordingly. + // All other channel types, its reverved + resp->auxChInfo[0] = 0x00; + resp->auxChInfo[1] = 0x00; + + *data_len = sizeof(*resp); + + return IPMI_CC_OK; +} + +void registerChannelFunctions() +{ + ipmiChannelInit(); + + ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHANNEL_ACCESS, NULL, + ipmiSetChannelAccess, PRIVILEGE_ADMIN); + + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, + ipmiGetChannelAccess, PRIVILEGE_USER); + + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_INFO, NULL, + ipmiGetChannelInfo, PRIVILEGE_USER); + return; +} + +} // namespace ipmi diff --git a/user_channel/channelcommands.hpp b/user_channel/channelcommands.hpp new file mode 100644 index 0000000..84d2dcb --- /dev/null +++ b/user_channel/channelcommands.hpp @@ -0,0 +1,32 @@ +/* +// Copyright (c) 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 <cstdint> + +namespace ipmi +{ + +// IPMI commands for channel command NETFN:APP. +enum ipmi_netfn_channel_cmds +{ + IPMI_CMD_SET_CHANNEL_ACCESS = 0x40, + IPMI_CMD_GET_CHANNEL_ACCESS = 0x41, + IPMI_CMD_GET_CHANNEL_INFO = 0x42, +}; + +void registerChannelFunctions(); +} // namespace ipmi |