diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-network/network')
9 files changed, 831 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor.bb b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor.bb new file mode 100644 index 000000000..cdb8e2097 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor.bb @@ -0,0 +1,25 @@ +SUMMARY = "Check for host in reset to disable the NCSI iface" +DESCRIPTION = "If the host is in reset, the NCSI NIC will not be \ + available, so this will manually disable the NIC" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" + +PV = "1.0" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI = "\ + file://check-for-host-in-reset \ + file://${BPN}.service \ + " + +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE:${PN} += "${BPN}.service" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/check-for-host-in-reset ${D}/${bindir}/check-for-host-in-reset + +} diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/check-for-host-in-reset b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/check-for-host-in-reset new file mode 100755 index 000000000..aa17aebf2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/check-for-host-in-reset @@ -0,0 +1,65 @@ +#!/bin/sh + +# PFR Boot Time Detection +# +# The Platform Firmware Recovery system is designed to confirm the server is +# running valid images. The server boot process is controlled with a +# programmable device. The programmable device prevents the system, and the +# BMC from booting until after it has confirmed the firmware images match a +# known checksum. Two reset controls are asserted while the checksum +# calculation is being performed. One prevents the BMC from booting, the other +# (RSMRST_N) prevents the main processors from leaving reset. +# +# If the BMC FW checksum is correct the BMC is allowed to boot. +# If the BIOS checksum fails the BIOS is not allowed to boot. +# In this condition the BMC will boot believing the NCSI NIC is functional. +# This will not be the case when RMSRST_N is asserted. The BIOS will not +# configure the shared NIC. The BMC will not be able to send or receive +# network traffic via the shared NIC. This becomes a problem depending on how +# the NCSI channel is configured. +# +# When the NCSI channel is configured using DHCP the BMC is unable to +# communicate to a DHCP server. Unable to acquire a valid IP state, the NCSI +# NIC is left DOWN. +# The problem that occurs is when the NIC is configured with a static +# address. The BMC is unable to determine the configuration state of the NCSI +# NIC, and behaves as if everything is working. The problem is the network +# routing table will, in most cases, be left in a state that prevents traffic +# from being sent/received from the dedicated NIC. This prevents network +# access to the BMC, which in turn leaves the system unrecoverable. +# +# The purpose of this script is to check for the assertion of the RSMRST_N +# control at BMC boot time. It will perform this test once. In the event the +# RSMRST_N is found to be asserted, the BMC will take the NCSI NIC down. No +# logic for detecting the deassertion will be performed. Once the new image +# for the BIOS has been transferred, and the checksum confirmed, the BMC will +# be reset by the programmable device. The programmable device will confirm +# the checksums, and release both the BMC and the BIOS to boot normally. +# +# Flow: +# The service will be a one-shot that waits for the network.target, as is done +# by BMCWeb. +# During a normal boot the RSMRST_N will not be asserted, and this script will +# not perform an action. +# When RSMRST_N is asserted the NCSI channel will be given a link down +# command. This regardless of static or DHCP configuration mode. + +GPIOFIND=/usr/bin/gpiofind +GPIOGET=/usr/bin/gpioget +RSMRST="RSMRST_N" + +# Read the assertion state from the RSMRST_N input +function get_rsmrst_state { + local __resultVal=$1 + local gpio_state=$($GPIOGET $($GPIOFIND "$RSMRST")) + eval $__resultVal="'$gpio_state'" + return 0 +} + +get_rsmrst_state rsmrst_val + +if [ "$rsmrst_val" -eq 0 ] +then + echo "RSMRST_N is asserted, take eth1 down" + ip link set down dev eth1 +fi diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/ncsi-monitor.service b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/ncsi-monitor.service new file mode 100644 index 000000000..19554c94d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/ncsi-monitor/ncsi-monitor.service @@ -0,0 +1,11 @@ +[Unit] +Description=Check for host in reset +After=multi-user.target + +[Service] +Type=oneshot +Restart=no +ExecStart=/usr/bin/check-for-host-in-reset + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0003-Adding-channel-specific-privilege-to-network.patch b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0003-Adding-channel-specific-privilege-to-network.patch new file mode 100644 index 000000000..f21283a75 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0003-Adding-channel-specific-privilege-to-network.patch @@ -0,0 +1,412 @@ +From 145778897e36f407773844b3b96847ff10306ee8 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Thu, 2 Apr 2020 17:06:07 +0530 +Subject: [PATCH] Adding channel specific privilege to network + + - Adding the channel access information to the network + interface object. This privilege will be used in + channel specific authorization. + - Get supported priv from user manager service dynamically. + - Signal handling for capturing the supported priv list + changes from user managerment. + +Tested-by: +Verified channel access through ipmitool get/set channel +access command + +Change-Id: I3b592a19363eef684e31d5f7c34dad8f2f9211df +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +Signed-off-by: Ramya Narayana <ramyax.narayana@intel.com> +--- + src/ethernet_interface.cpp | 124 +++++++++++++++++++++++++++++++++++++ + src/ethernet_interface.hpp | 37 ++++++++++- + src/network_manager.cpp | 102 ++++++++++++++++++++++++++++++ + src/network_manager.hpp | 9 +++ + 4 files changed, 271 insertions(+), 1 deletion(-) + +diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp +index 2e15803..1145773 100644 +--- a/src/ethernet_interface.cpp ++++ b/src/ethernet_interface.cpp +@@ -48,6 +48,10 @@ constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; + constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/"; + constexpr auto METHOD_GET = "Get"; + ++static constexpr const char* networkChannelCfgFile = ++ "/var/channel_intf_data.json"; ++static constexpr const char* defaultChannelPriv = "priv-admin"; ++ + struct EthernetIntfSocket + { + EthernetIntfSocket(int domain, int type, int protocol) +@@ -132,6 +136,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + EthernetInterfaceIntf::autoNeg(std::get<2>(ifInfo)); + EthernetInterfaceIntf::speed(std::get<0>(ifInfo)); + #endif ++ getChannelPrivilege(intfName); + + // Emit deferred signal. + if (emitSignal) +@@ -1322,5 +1327,124 @@ std::string EthernetInterface::defaultGateway6(std::string gateway) + + return gw; + } ++ ++nlohmann::json EthernetInterface::readJsonFile(const std::string& configFile) ++{ ++ std::ifstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file not found"); ++ return nullptr; ++ } ++ ++ nlohmann::json data = nullptr; ++ try ++ { ++ data = nlohmann::json::parse(jsonFile, nullptr, false); ++ } ++ catch (nlohmann::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 EthernetInterface::writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData) ++{ ++ std::ofstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file open failed", ++ entry("FILE=%s", networkChannelCfgFile)); ++ return -1; ++ } ++ ++ // Write JSON to file ++ jsonFile << jsonData; ++ ++ jsonFile.flush(); ++ return 0; ++} ++ ++std::string ++ EthernetInterface::getChannelPrivilege(const std::string& interfaceName) ++{ ++ std::string priv(defaultChannelPriv); ++ std::string retPriv; ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ if (jsonData != nullptr) ++ { ++ try ++ { ++ priv = jsonData[interfaceName].get<std::string>(); ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ return retPriv; ++ } ++ catch (const nlohmann::json::exception& e) ++ { ++ jsonData[interfaceName] = priv; ++ } ++ } ++ else ++ { ++ jsonData[interfaceName] = priv; ++ } ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ ++ return retPriv; ++} ++ ++std::string EthernetInterface::maxPrivilege(std::string priv) ++{ ++ std::string intfName = interfaceName(); ++ ++ if (manager.supportedPrivList.empty()) ++ { ++ // Populate the supported privilege list ++ manager.initSupportedPrivilges(); ++ } ++ ++ if (!priv.empty() && (std::find(manager.supportedPrivList.begin(), ++ manager.supportedPrivList.end(), ++ priv) == manager.supportedPrivList.end())) ++ { ++ log<level::ERR>("Invalid privilege"); ++ elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), ++ Argument::ARGUMENT_VALUE(priv.c_str())); ++ } ++ ++ if (ChannelAccessIntf::maxPrivilege() == priv) ++ { ++ // No change in privilege so just return. ++ return priv; ++ } ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ jsonData[intfName] = priv; ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ // Property change signal will be sent ++ return ChannelAccessIntf::maxPrivilege(std::move(priv)); ++} ++ + } // namespace network + } // namespace phosphor +diff --git a/src/ethernet_interface.hpp b/src/ethernet_interface.hpp +index 0fe3778..fa5c889 100644 +--- a/src/ethernet_interface.hpp ++++ b/src/ethernet_interface.hpp +@@ -2,11 +2,14 @@ + + #include "types.hpp" + #include "util.hpp" ++#include "xyz/openbmc_project/Channel/ChannelAccess/server.hpp" + #include "xyz/openbmc_project/Network/IP/Create/server.hpp" + #include "xyz/openbmc_project/Network/Neighbor/CreateStatic/server.hpp" + + #include <filesystem> ++#include <nlohmann/json.hpp> + #include <sdbusplus/bus.hpp> ++#include <sdbusplus/bus/match.hpp> + #include <sdbusplus/server/object.hpp> + #include <string> + #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp> +@@ -23,7 +26,8 @@ using Ifaces = sdbusplus::server::object::object< + sdbusplus::xyz::openbmc_project::Network::server::MACAddress, + sdbusplus::xyz::openbmc_project::Network::IP::server::Create, + sdbusplus::xyz::openbmc_project::Network::Neighbor::server::CreateStatic, +- sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>; ++ sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll, ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess>; + + using IP = sdbusplus::xyz::openbmc_project::Network::server::IP; + +@@ -31,11 +35,14 @@ using EthernetInterfaceIntf = + sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; + using MacAddressIntf = + sdbusplus::xyz::openbmc_project::Network::server::MACAddress; ++using ChannelAccessIntf = ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess; + + using ServerList = std::vector<std::string>; + using ObjectPath = sdbusplus::message::object_path; + + namespace fs = std::filesystem; ++using DbusVariant = std::variant<std::string, std::vector<std::string>>; + + class Manager; // forward declaration of network manager. + +@@ -240,6 +247,14 @@ class EthernetInterface : public Ifaces + std::string defaultGateway6(std::string gateway) override; + + using EthernetInterfaceIntf::dhcpEnabled; ++ /** @brief sets the channel maxium privilege. ++ * @param[in] value - Channel privilege which needs to be set on the ++ * system. ++ * @returns privilege of the interface or throws an error. ++ */ ++ std::string maxPrivilege(std::string value) override; ++ ++ using ChannelAccessIntf::maxPrivilege; + using EthernetInterfaceIntf::interfaceName; + using EthernetInterfaceIntf::linkUp; + using EthernetInterfaceIntf::mtu; +@@ -372,6 +387,26 @@ class EthernetInterface : public Ifaces + * @returns true/false value if the NIC is enabled + */ + bool queryNicEnabled() const; ++ ++ /** @brief gets the channel privilege. ++ * @param[in] interfaceName - Network interface name. ++ * @returns privilege of the interface ++ */ ++ std::string getChannelPrivilege(const std::string& interfaceName); ++ ++ /** @brief reads the channel access info from file. ++ * @param[in] configFile - channel access filename ++ * @returns json file data ++ */ ++ nlohmann::json readJsonFile(const std::string& configFile); ++ ++ /** @brief writes the channel access info to file. ++ * @param[in] configFile - channel access filename ++ * @param[in] jsonData - json data to write ++ * @returns success or failure ++ */ ++ int writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData); + }; + + } // namespace network +diff --git a/src/network_manager.cpp b/src/network_manager.cpp +index fe59f0b..01a99a3 100644 +--- a/src/network_manager.cpp ++++ b/src/network_manager.cpp +@@ -39,6 +39,13 @@ extern std::unique_ptr<Timer> refreshObjectTimer; + using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + ++static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user"; ++static constexpr const char* userMgrInterface = ++ "xyz.openbmc_project.User.Manager"; ++static constexpr const char* propNameAllPrivileges = "AllPrivileges"; ++ ++std::unique_ptr<sdbusplus::bus::match_t> usrMgmtSignal(nullptr); ++ + Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + const std::string& path) : + details::VLANCreateIface(bus, objPath, true), +@@ -46,6 +53,101 @@ Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + { + fs::path confDir(path); + setConfDir(confDir); ++ initSupportedPrivilges(); ++} ++ ++std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf, ++ const std::string& path) ++{ ++ auto mapperCall = ++ bus.new_method_call("xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject"); ++ ++ mapperCall.append(path); ++ mapperCall.append(std::vector<std::string>({intf})); ++ ++ auto mapperResponseMsg = bus.call(mapperCall); ++ ++ std::map<std::string, std::vector<std::string>> mapperResponse; ++ mapperResponseMsg.read(mapperResponse); ++ ++ if (mapperResponse.begin() == mapperResponse.end()) ++ { ++ throw std::runtime_error("ERROR in reading the mapper response"); ++ } ++ ++ return mapperResponse.begin()->first; ++} ++ ++std::string Manager::getUserServiceName() ++{ ++ static std::string userMgmtService; ++ if (userMgmtService.empty()) ++ { ++ try ++ { ++ userMgmtService = ++ getUserService(bus, userMgrInterface, userMgrObjBasePath); ++ } ++ catch (const std::exception& e) ++ { ++ log<level::ERR>("Exception caught in getUserServiceName."); ++ userMgmtService.clear(); ++ } ++ } ++ return userMgmtService; ++} ++ ++void Manager::initSupportedPrivilges() ++{ ++ std::string userServiceName = getUserServiceName(); ++ if (!userServiceName.empty()) ++ { ++ auto method = bus.new_method_call( ++ getUserServiceName().c_str(), userMgrObjBasePath, ++ "org.freedesktop.DBus.Properties", "Get"); ++ method.append(userMgrInterface, propNameAllPrivileges); ++ ++ auto reply = bus.call(method); ++ if (reply.is_method_error()) ++ { ++ log<level::DEBUG>("get-property AllPrivileges failed", ++ entry("OBJPATH:%s", userMgrObjBasePath), ++ entry("INTERFACE:%s", userMgrInterface)); ++ return; ++ } ++ ++ std::variant<std::vector<std::string>> result; ++ reply.read(result); ++ ++ supportedPrivList = std::get<std::vector<std::string>>(result); ++ } ++ ++ // Resgister the signal ++ if (usrMgmtSignal == nullptr) ++ { ++ log<level::DEBUG>("Registering User.Manager propertychange signal."); ++ usrMgmtSignal = std::make_unique<sdbusplus::bus::match_t>( ++ bus, ++ sdbusplus::bus::match::rules::propertiesChanged(userMgrObjBasePath, ++ userMgrInterface), ++ [&](sdbusplus::message::message& msg) { ++ log<level::DEBUG>("UserMgr properties changed signal"); ++ std::map<std::string, DbusVariant> props; ++ std::string iface; ++ msg.read(iface, props); ++ for (const auto& t : props) ++ { ++ if (t.first == propNameAllPrivileges) ++ { ++ supportedPrivList = ++ std::get<std::vector<std::string>>(t.second); ++ } ++ } ++ }); ++ } ++ return; + } + + bool Manager::createDefaultNetworkFiles(bool force) +diff --git a/src/network_manager.hpp b/src/network_manager.hpp +index fb3cc32..0c3d49b 100644 +--- a/src/network_manager.hpp ++++ b/src/network_manager.hpp +@@ -156,6 +156,12 @@ class Manager : public details::VLANCreateIface + return routeTable; + } + ++ /** supported privilege list **/ ++ std::vector<std::string> supportedPrivList; ++ ++ /** @brief initializes the supportedPrivilege List */ ++ void initSupportedPrivilges(); ++ + protected: + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; +@@ -181,6 +187,9 @@ class Manager : public details::VLANCreateIface + + /** @brief The routing table */ + route::Table routeTable; ++ ++ /** Get the user management service name dynamically **/ ++ std::string getUserServiceName(); + }; + + } // namespace network +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0004-Fix-for-updating-MAC-address-from-RedFish.patch b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0004-Fix-for-updating-MAC-address-from-RedFish.patch new file mode 100644 index 000000000..90bbc1d5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0004-Fix-for-updating-MAC-address-from-RedFish.patch @@ -0,0 +1,109 @@ +From cbd034daf844529eb7f098c990dc8f44c12f6b97 Mon Sep 17 00:00:00 2001 +From: sunitakx <sunitax.kumari@linux.intel.com> +Date: Tue, 13 Jul 2021 12:54:01 +0000 +Subject: [PATCH] Fix for updating MAC address from RedFish + +Issue: When IP address source for an interface is DHCP and its MAC +address is patched using RedFish, response code is not reaching the +RedFish request initiator (client). + +RootCause: After bmcweb patches the MAC address, immediately IP address +of that interface also changes to new value (because of DHCP). +Due to this, success response from bmcweb is not reaching the client as +expected. + +Fix: Do MAC-ADDR patching after validating the request and responding +"200 OK" to RedFish client. i.e Start a timer which will modify the +MAC-ADDR at the end of its expiry. + +Tested: +Update the MAC address from RedFish. +PATCH: https://<bmc_ip>/redfish/v1/Managers/bmc/EthernetInterfaces/eth0 +Body: + {"MACAddress": "xx:xx:xx:xx:xx:xx"} + +Response code: {"200 OK"} received. + +Signed-off-by: sunitakx <sunitax.kumari@linux.intel.com> +Signed-off-by: Ramya Narayana <ramyax.narayana@intel.com> +--- + src/ethernet_interface.cpp | 17 ++++++++++++----- + src/ethernet_interface.hpp | 5 +++++ + 2 files changed, 17 insertions(+), 5 deletions(-) + +diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp +index 1145773..129905e 100644 +--- a/src/ethernet_interface.cpp ++++ b/src/ethernet_interface.cpp +@@ -143,6 +143,8 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + { + this->emit_object_added(); + } ++ macUpdateTimer = std::make_unique<phosphor::Timer>( ++ [this](void) { macAddressTimeoutHandler(); }); + } + + static IP::Protocol convertFamily(int family) +@@ -1209,8 +1211,17 @@ void EthernetInterface::writeDHCPSection(std::fstream& stream) + } + } + ++void EthernetInterface::macAddressTimeoutHandler() ++{ ++ macUpdateTimer->stop(); ++ // The MAC and LLADDRs will only update if the NIC is already down ++ EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); ++ setNICAdminState(eifSocket.sock, interfaceName().c_str(), false); ++ manager.reloadConfigs(); ++} + std::string EthernetInterface::macAddress(std::string value) + { ++ std::chrono::seconds usec(defaultTimeout); + ether_addr newMAC; + try + { +@@ -1244,12 +1255,8 @@ std::string EthernetInterface::macAddress(std::string value) + intf->MacAddressIntf::macAddress(validMAC); + } + MacAddressIntf::macAddress(validMAC); +- ++ macUpdateTimer->start(usec); + writeConfigurationFile(); +- // The MAC and LLADDRs will only update if the NIC is already down +- EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); +- setNICAdminState(eifSocket.sock, interface.c_str(), false); +- manager.reloadConfigs(); + } + + #ifdef HAVE_UBOOT_ENV +diff --git a/src/ethernet_interface.hpp b/src/ethernet_interface.hpp +index fa5c889..70f4756 100644 +--- a/src/ethernet_interface.hpp ++++ b/src/ethernet_interface.hpp +@@ -11,11 +11,14 @@ + #include <sdbusplus/bus.hpp> + #include <sdbusplus/bus/match.hpp> + #include <sdbusplus/server/object.hpp> ++#include <sdbusplus/timer.hpp> + #include <string> + #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp> + #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> + #include <xyz/openbmc_project/Network/MACAddress/server.hpp> + ++static constexpr const uint32_t defaultTimeout = 1; ++ + namespace phosphor + { + namespace network +@@ -84,6 +87,8 @@ class EthernetInterface : public Ifaces + EthernetInterface& operator=(EthernetInterface&&) = delete; + virtual ~EthernetInterface() = default; + ++ std::unique_ptr<phosphor::Timer> macUpdateTimer; ++ void macAddressTimeoutHandler(); + /** @brief Constructor to put object onto bus at a dbus path. + * @param[in] bus - Bus to attach to. + * @param[in] objPath - Path to attach at. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network_%.bbappend b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network_%.bbappend new file mode 100644 index 000000000..4828c9ea0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network_%.bbappend @@ -0,0 +1,13 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += "nlohmann-json boost" + +SRC_URI = "git://github.com/openbmc/phosphor-networkd" +SRCREV = "ee2cba8a7d22ef4a251181087e9ef9bfc5c4b165" + +SRC_URI += " file://0003-Adding-channel-specific-privilege-to-network.patch \ + file://0004-Fix-for-updating-MAC-address-from-RedFish.patch \ + " + +EXTRA_OECONF:append = " --enable-nic-ethtool=yes" +EXTRA_OECONF:append = " --enable-ipv6-accept-ra=yes" diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr.bb b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr.bb new file mode 100644 index 000000000..ee55c5407 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr.bb @@ -0,0 +1,24 @@ +SUMMARY = "Enforce static MAC addresses" +DESCRIPTION = "Set a priority on MAC addresses to run with: \ + factory-specified > u-boot-specified > random" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +PV = "1.0" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI = "\ + file://mac-check \ + file://${PN}.service \ + " + +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE:${PN} += "${PN}.service" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/mac-check ${D}${bindir} +} diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/mac-check b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/mac-check new file mode 100644 index 000000000..39d7dd8a7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/mac-check @@ -0,0 +1,161 @@ +#!/bin/sh +# Copyright 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. + +SOFS_MNT=/var/sofs +SOFS_MACDIR=${SOFS_MNT}/factory-settings/network/mac + +read_hw_mac() { + local iface="$1" + cat /sys/class/net/"$iface"/address 2>/dev/null | tr [:upper:] [:lower:] 2>/dev/null +} + +set_hw_mac() { + local iface="$1" + local mac="$2" + ip link show dev "$iface" | grep -q "${iface}:.*\<UP\>" 2>/dev/null + local up=$? + [[ $up -eq 0 ]] && ip link set dev "$iface" down + ip link set dev "$iface" address "$mac" + [[ $up -eq 0 ]] && ip link set dev "$iface" up +} + +read_sofs_mac() { + local iface="$1" + cat "${SOFS_MACDIR}/${iface}" 2>/dev/null | tr [:upper:] [:lower:] 2>/dev/null +} + +read_fw_env_mac() { + local envname="$1" + fw_printenv "$envname" 2>/dev/null | sed "s/^$envname=//" 2>/dev/null | tr [:upper:] [:lower:] 2>/dev/null +} + +set_fw_env_mac() { + local envname="$1" + local mac="$2" + fw_setenv "$envname" "$mac" +} + +create_macdir() { +if [ -a ${SOFS_MACDIR} ]; then + if [ ! -d ${SOFS_MACDIR} ]; then + rm -rf ${SOFS_MACDIR} + mkdir -p ${SOFS_MACDIR} + fi +else + mkdir -p ${SOFS_MACDIR} +fi +return 0 +} + +# An earlier version of the mac_check utility disabled the netipmid for +# eth1. This was done to eliminate an error message being logged in the +# journal for systems that only had a single NIC. The error message is +# undesirable as it is present in Redfish session log output. + +# Systems that have both NICs have also had the eth1 netipmid disabled. +# The reason for this is failing to specify the correct kernel device +# tree during the U-Boot kernel boot process. Without the correct +# device tree, eth1 is not enumerated by the kernel. The mac-check +# script turned off the netipmid service for eth1. + +# The configure_netipmid_svc_eth1 function manages enabling and +# disabling netipmid for eth1. It is explicit, and does not rely upon +# previous state to enable or disable the service. + +# Note: Enabling the service is independent of the IPMI channel +# enable/disable command. This means "ipmitool lan set <chid> access +# off" functions correctly with the netipmid service enabled. +configure_netipmid_svc_eth1() { + if [ -h /sys/class/net/eth1 ]; then + if [ $(systemctl is-enabled phosphor-ipmi-net@eth1.socket) == "disabled" ]; + then + /bin/systemctl enable "phosphor-ipmi-net@eth1.socket" + /bin/systemctl start "phosphor-ipmi-net@eth1.socket" + fi + if [ $(systemctl is-enabled phosphor-ipmi-net@eth1.service) =="disabled" ]; + then + /bin/systemctl enable "phosphor-ipmi-net@eth1.service" + /bin/systemctl start "phosphor-ipmi-net@eth1.service" + fi + else + if [ $(systemctl is-enabled phosphor-ipmi-net@eth1.socket) == "enabled" ]; + then + /bin/systemctl disable "phosphor-ipmi-net@eth1.socket" + /bin/systemctl stop "phosphor-ipmi-net@eth1.socket" + fi + if [ $(systemctl is-enabled phosphor-ipmi-net@eth1.service) == "enabled" ]; + then + /bin/systemctl disable "phosphor-ipmi-net@eth1.service" + /bin/systemctl stop "phosphor-ipmi-net@eth1.service" + fi + fi +} + +mac_check() { + local iface="$1" + local envname="$2" + + # Read the MAC address in use by the NIC + local hw_mac=$(read_hw_mac "$iface") + + # Read the MAC address stored in the non-volatile file provisioned in + # manufacturing. + local sofs_mac=$(read_sofs_mac "$iface") + + if [ -n "$sofs_mac" ] && [ "$hw_mac" != "$sofs_mac" ]; then + # A factory assigned address was found, and it is newly assigned. + # Update the active interface and save the new value to the u-boot + # environment. + set_hw_mac "$iface" "$sofs_mac" + set_fw_env_mac "$envname" "$sofs_mac" + return $? + elif [ -n "$hw_mac" ]; then + # Read the MAC address stored by U-Boot + local fw_env_mac=$(read_fw_env_mac "$envname") + if [ -z "$fw_env_mac" ] || [ "$fw_env_mac" != "$hw_mac" ]; then + set_fw_env_mac "$envname" "$hw_mac" + return $? + fi + else + # Could not identify a MAC address + return 255 + fi + return 0 +} + +create_macdir + +configure_netipmid_svc_eth1 + +error=0 +first_error_seen=0 + +while read IFACE UBDEV; do + # Try to configure the MAC address if the kernel finds the NIC. Blindly + # trying all of the interfaces listed in the DOCSTRING (END_CONF) below + # may result in first_error_seen being set to a non-zero value. If that + # happens the journal log will report the error, which is undesirable. + if [ -h /sys/class/net/$IFACE ]; then + mac_check "$IFACE" "$UBDEV" + error=$? + if [ $error -ne 0 ] && [ $first_error_seen -eq 0 ]; then + first_error_seen=$error + fi + fi +done <<-END_CONF + eth0 eth1addr + eth1 ethaddr +END_CONF +exit $first_error_seen diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/static-mac-addr.service b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/static-mac-addr.service new file mode 100644 index 000000000..86371db11 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/static-mac-addr/static-mac-addr.service @@ -0,0 +1,11 @@ +[Unit] +Description=Enforce Static MAC addr mapping + +[Service] +Type=oneshot +Restart=no +ExecStart=/usr/bin/mac-check + +[Install] +WantedBy=network.target + |