/* // 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 "ChassisIntrusionSensor.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr bool debug = false; static constexpr const char* sensorType = "ChassisIntrusionSensor"; static constexpr const char* nicType = "NIC"; static constexpr auto nicTypes{std::to_array({nicType})}; static const std::map compatibleHwmonNames = { {"Aspeed2600_Hwmon", "intrusion0_alarm"} // Add compatible strings here for new hwmon intrusion detection // drivers that have different hwmon names but would also like to // use the available Hwmon class. }; static void createSensorsFromConfig( boost::asio::io_context& io, sdbusplus::asio::object_server& objServer, const std::shared_ptr& dbusConnection, std::shared_ptr& pSensor) { // find matched configuration according to sensor type ManagedObjectType sensorConfigurations; bool useCache = false; if (!getSensorConfiguration(sensorType, dbusConnection, sensorConfigurations, useCache)) { std::cerr << "error communicating to entity manager\n"; return; } const SensorData* sensorData = nullptr; const std::pair* baseConfiguration = nullptr; for (const auto& [path, cfgData] : sensorConfigurations) { baseConfiguration = nullptr; sensorData = &cfgData; // match sensor type auto sensorBase = sensorData->find(configInterfaceName(sensorType)); if (sensorBase == sensorData->end()) { std::cerr << "error finding base configuration \n"; continue; } baseConfiguration = &(*sensorBase); // Rearm defaults to "Automatic" mode bool autoRearm = true; auto findRearm = baseConfiguration->second.find("Rearm"); if (findRearm != baseConfiguration->second.end()) { std::string rearmStr = std::get(findRearm->second); if (rearmStr != "Automatic" && rearmStr != "Manual") { std::cerr << "Wrong input for Rearm parameter\n"; continue; } autoRearm = (rearmStr == "Automatic"); } // judge class, "Gpio", "Hwmon" or "I2C" auto findClass = baseConfiguration->second.find("Class"); if (findClass != baseConfiguration->second.end()) { auto classString = std::get(findClass->second); if (classString == "Gpio") { auto findGpioPolarity = baseConfiguration->second.find("GpioPolarity"); if (findGpioPolarity == baseConfiguration->second.end()) { std::cerr << "error finding gpio polarity in configuration \n"; continue; } try { bool gpioInverted = (std::get(findGpioPolarity->second) == "Low"); pSensor = std::make_shared( autoRearm, io, objServer, gpioInverted); pSensor->start(); if (debug) { std::cout << "find chassis intrusion sensor polarity inverted " "flag is " << gpioInverted << "\n"; } return; } catch (const std::bad_variant_access& e) { std::cerr << "invalid value for gpio info in config. \n"; continue; } catch (const std::exception& e) { std::cerr << e.what() << std::endl; continue; } } // If class string contains Hwmon string else if (classString.find("Hwmon") != std::string::npos) { std::string hwmonName; std::map::const_iterator compatIterator = compatibleHwmonNames.find(classString); if (compatIterator == compatibleHwmonNames.end()) { std::cerr << "Hwmon Class string is not supported\n"; continue; } hwmonName = compatIterator->second; try { pSensor = std::make_shared( autoRearm, io, objServer, hwmonName); pSensor->start(); return; } catch (const std::exception& e) { std::cerr << e.what() << std::endl; continue; } } else { auto findBus = baseConfiguration->second.find("Bus"); auto findAddress = baseConfiguration->second.find("Address"); if (findBus == baseConfiguration->second.end() || findAddress == baseConfiguration->second.end()) { std::cerr << "error finding bus or address in configuration \n"; continue; } try { int busId = std::get(findBus->second); int slaveAddr = std::get(findAddress->second); pSensor = std::make_shared( autoRearm, io, objServer, busId, slaveAddr); pSensor->start(); if (debug) { std::cout << "find matched bus " << busId << ", matched slave addr " << slaveAddr << "\n"; } return; } catch (const std::bad_variant_access& e) { std::cerr << "invalid value for bus or address in config. \n"; continue; } catch (const std::exception& e) { std::cerr << e.what() << std::endl; continue; } } } } std::cerr << " Can't find matched I2C, GPIO or Hwmon configuration\n"; // Make sure nothing runs when there's failure in configuration for the // sensor after rescan if (pSensor) { std::cerr << " Reset the occupied sensor pointer\n"; pSensor = nullptr; } } static constexpr bool debugLanLeash = false; boost::container::flat_map lanStatusMap; boost::container::flat_map lanInfoMap; boost::container::flat_map pathSuffixMap; static void getNicNameInfo( const std::shared_ptr& dbusConnection) { auto getter = std::make_shared( dbusConnection, [](const ManagedObjectType& sensorConfigurations) { // Get NIC name and save to map lanInfoMap.clear(); for (const auto& [path, cfgData] : sensorConfigurations) { const std::pair* baseConfiguration = nullptr; // find base configuration auto sensorBase = cfgData.find(configInterfaceName(nicType)); if (sensorBase == cfgData.end()) { continue; } baseConfiguration = &(*sensorBase); auto findEthIndex = baseConfiguration->second.find("EthIndex"); auto findName = baseConfiguration->second.find("Name"); if (findEthIndex != baseConfiguration->second.end() && findName != baseConfiguration->second.end()) { const auto* pEthIndex = std::get_if(&findEthIndex->second); const auto* pName = std::get_if(&findName->second); if (pEthIndex != nullptr && pName != nullptr) { lanInfoMap[*pEthIndex] = *pName; if (debugLanLeash) { std::cout << "find name of eth" << *pEthIndex << " is " << *pName << "\n"; } } } } if (lanInfoMap.empty()) { std::cerr << "can't find matched NIC name. \n"; } }); getter->getConfiguration( std::vector{nicTypes.begin(), nicTypes.end()}); } static void processLanStatusChange(sdbusplus::message_t& message) { const std::string& pathName = message.get_path(); std::string interfaceName; SensorBaseConfigMap properties; message.read(interfaceName, properties); auto findStateProperty = properties.find("OperationalState"); if (findStateProperty == properties.end()) { return; } std::string* pState = std::get_if(&(findStateProperty->second)); if (pState == nullptr) { std::cerr << "invalid OperationalState \n"; return; } bool newLanConnected = (*pState == "routable" || *pState == "carrier" || *pState == "degraded"); // get ethNum from path. /org/freedesktop/network1/link/_32 for eth0 size_t pos = pathName.find("/_"); if (pos == std::string::npos || pathName.length() <= pos + 2) { std::cerr << "unexpected path name " << pathName << "\n"; return; } std::string suffixStr = pathName.substr(pos + 2); auto findEthNum = pathSuffixMap.find(suffixStr); if (findEthNum == pathSuffixMap.end()) { std::cerr << "unexpected eth for suffixStr " << suffixStr << "\n"; return; } int ethNum = findEthNum->second; // get lan status from map auto findLanStatus = lanStatusMap.find(ethNum); if (findLanStatus == lanStatusMap.end()) { std::cerr << "unexpected eth " << ethNum << " in lanStatusMap \n"; return; } bool oldLanConnected = findLanStatus->second; // get lan info from map std::string lanInfo; if (!lanInfoMap.empty()) { auto findLanInfo = lanInfoMap.find(ethNum); if (findLanInfo == lanInfoMap.end()) { std::cerr << "unexpected eth " << ethNum << " in lanInfoMap \n"; } else { lanInfo = "(" + findLanInfo->second + ")"; } } if (debugLanLeash) { std::cout << "ethNum = " << ethNum << ", state = " << *pState << ", oldLanConnected = " << (oldLanConnected ? "true" : "false") << ", newLanConnected = " << (newLanConnected ? "true" : "false") << "\n"; } if (oldLanConnected != newLanConnected) { std::string strEthNum = "eth" + std::to_string(ethNum) + lanInfo; const auto* strState = newLanConnected ? "connected" : "lost"; const auto* strMsgId = newLanConnected ? "OpenBMC.0.1.LanRegained" : "OpenBMC.0.1.LanLost"; lg2::info("{ETHDEV} LAN leash {STATE}", "ETHDEV", strEthNum, "STATE", strState, "REDFISH_MESSAGE_ID", strMsgId, "REDFISH_MESSAGE_ARGS", strEthNum); lanStatusMap[ethNum] = newLanConnected; } } /** @brief Initialize the lan status. * * @return true on success and false on failure */ static bool initializeLanStatus( const std::shared_ptr& conn) { // init lan port name from configuration getNicNameInfo(conn); // get eth info from sysfs std::vector files; if (!findFiles(fs::path("/sys/class/net/"), R"(eth\d+/ifindex)", files)) { std::cerr << "No eth in system\n"; return false; } // iterate through all found eth files, and save ifindex for (const fs::path& fileName : files) { if (debugLanLeash) { std::cout << "Reading " << fileName << "\n"; } std::ifstream sysFile(fileName); if (!sysFile.good()) { std::cerr << "Failure reading " << fileName << "\n"; continue; } std::string line; getline(sysFile, line); const uint8_t ifindex = std::stoi(line); // pathSuffix is ASCII of ifindex const std::string& pathSuffix = std::to_string(ifindex + 30); // extract ethNum const std::string& fileStr = fileName.string(); const int pos = fileStr.find("eth"); const std::string& ethNumStr = fileStr.substr(pos + 3); int ethNum = 0; std::from_chars_result r = std::from_chars( ethNumStr.data(), ethNumStr.data() + ethNumStr.size(), ethNum); if (r.ec != std::errc()) { std::cerr << "invalid ethNum string: " << ethNumStr << "\n"; continue; } // save pathSuffix pathSuffixMap[pathSuffix] = ethNum; if (debugLanLeash) { std::cout << "ethNum = " << std::to_string(ethNum) << ", ifindex = " << line << ", pathSuffix = " << pathSuffix << "\n"; } // init lan connected status from networkd conn->async_method_call( [ethNum](boost::system::error_code ec, const std::variant& property) { lanStatusMap[ethNum] = false; if (ec) { std::cerr << "Error reading init status of eth" << ethNum << "\n"; return; } const std::string* pState = std::get_if(&property); if (pState == nullptr) { std::cerr << "Unable to read lan status value\n"; return; } bool isLanConnected = (*pState == "routable" || *pState == "carrier" || *pState == "degraded"); if (debugLanLeash) { std::cout << "ethNum = " << std::to_string(ethNum) << ", init LAN status = " << (isLanConnected ? "true" : "false") << "\n"; } lanStatusMap[ethNum] = isLanConnected; }, "org.freedesktop.network1", "/org/freedesktop/network1/link/_" + pathSuffix, "org.freedesktop.DBus.Properties", "Get", "org.freedesktop.network1.Link", "OperationalState"); } return true; } int main() { std::shared_ptr intrusionSensor; // setup connection to dbus boost::asio::io_context io; auto systemBus = std::make_shared(io); // setup object server, define interface systemBus->request_name("xyz.openbmc_project.IntrusionSensor"); sdbusplus::asio::object_server objServer(systemBus, true); objServer.add_manager("/xyz/openbmc_project/Chassis"); createSensorsFromConfig(io, objServer, systemBus, intrusionSensor); // callback to handle configuration change boost::asio::steady_timer filterTimer(io); std::function eventHandler = [&](sdbusplus::message_t& message) { if (message.is_method_error()) { std::cerr << "callback method error\n"; return; } // this implicitly cancels the timer filterTimer.expires_after(std::chrono::seconds(1)); filterTimer.async_wait([&](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { // timer was cancelled return; } std::cout << "rescan due to configuration change \n"; createSensorsFromConfig(io, objServer, systemBus, intrusionSensor); }); }; std::vector> matches = setupPropertiesChangedMatches( *systemBus, std::to_array({sensorType}), eventHandler); if (initializeLanStatus(systemBus)) { // add match to monitor lan status change sdbusplus::bus::match_t lanStatusMatch( static_cast(*systemBus), "type='signal', member='PropertiesChanged'," "arg0namespace='org.freedesktop.network1.Link'", [](sdbusplus::message_t& msg) { processLanStatusChange(msg); }); // add match to monitor entity manager signal about nic name config // change sdbusplus::bus::match_t lanConfigMatch( static_cast(*systemBus), "type='signal', member='PropertiesChanged',path_namespace='" + std::string(inventoryPath) + "',arg0namespace='" + configInterfaceName(nicType) + "'", [&systemBus](sdbusplus::message_t& msg) { if (msg.is_method_error()) { std::cerr << "callback method error\n"; return; } getNicNameInfo(systemBus); }); } io.run(); return 0; }