diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network')
3 files changed, 1105 insertions, 0 deletions
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..05e26bedb --- /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 5dfcd6c81a9f26c6c9c867a7a49f5259c3512be2 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 1/2] 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> +--- + ethernet_interface.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ + ethernet_interface.hpp | 39 +++++++++++++++- + network_manager.cpp | 104 +++++++++++++++++++++++++++++++++++++++++ + network_manager.hpp | 9 ++++ + 4 files changed, 274 insertions(+), 1 deletion(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index ed1e1ba..fca86bd 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -37,6 +37,10 @@ using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + using Argument = xyz::openbmc_project::Common::InvalidArgument; + ++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) +@@ -86,6 +90,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + EthernetInterfaceIntf::speed(std::get<0>(ifInfo)); + EthernetInterfaceIntf::linkUp(std::get<3>(ifInfo)); + #endif ++ getChannelPrivilege(intfName); + + // Emit deferred signal. + if (emitSignal) +@@ -869,5 +874,123 @@ void EthernetInterface::deleteAll() + manager.writeToConfigurationFile(); + } + ++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/ethernet_interface.hpp b/ethernet_interface.hpp +index 68668d6..058d328 100644 +--- a/ethernet_interface.hpp ++++ b/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 <experimental/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,10 +35,15 @@ 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; + ++using DbusVariant = ++ sdbusplus::message::variant<std::string, std::vector<std::string>>; ++ + namespace fs = std::experimental::filesystem; + + class Manager; // forward declaration of network manager. +@@ -199,6 +208,14 @@ class EthernetInterface : public Ifaces + */ + void deleteAll(); + ++ /** @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::dHCPEnabled; + using EthernetInterfaceIntf::interfaceName; + using EthernetInterfaceIntf::linkUp; +@@ -296,6 +313,26 @@ class EthernetInterface : public Ifaces + std::string objPath; + + friend class TestEthernetInterface; ++ ++ /** @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/network_manager.cpp b/network_manager.cpp +index 043d7a2..75f4e5f 100644 +--- a/network_manager.cpp ++++ b/network_manager.cpp +@@ -34,6 +34,13 @@ extern std::unique_ptr<Timer> restartTimer; + 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), +@@ -41,6 +48,103 @@ 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; ++ } ++ ++ sdbusplus::message::variant<std::vector<std::string>> result; ++ reply.read(result); ++ ++ supportedPrivList = ++ sdbusplus::message::variant_ns::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 = sdbusplus::message::variant_ns::get< ++ std::vector<std::string>>(t.second); ++ } ++ } ++ }); ++ } ++ return; + } + + bool Manager::createDefaultNetworkFiles(bool force) +diff --git a/network_manager.hpp b/network_manager.hpp +index edb341f..5fb9fe8 100644 +--- a/network_manager.hpp ++++ b/network_manager.hpp +@@ -137,6 +137,12 @@ class Manager : public details::VLANCreateIface + return (interfaces.find(intf) != interfaces.end()); + } + ++ /** 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; +@@ -159,6 +165,9 @@ class Manager : public details::VLANCreateIface + + /** @brief Network Configuration directory. */ + fs::path confDir; ++ ++ /** Get the user management service name dynamically **/ ++ std::string getUserServiceName(); + }; + + } // namespace network +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0009-Enhance-DHCP-beyond-just-OFF-and-IPv4-IPv6-enabled.patch b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0009-Enhance-DHCP-beyond-just-OFF-and-IPv4-IPv6-enabled.patch new file mode 100644 index 000000000..596dfce48 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0009-Enhance-DHCP-beyond-just-OFF-and-IPv4-IPv6-enabled.patch @@ -0,0 +1,504 @@ +From 163c1756ee676859622614996be81393eb348220 Mon Sep 17 00:00:00 2001 +From: Johnathan Mantey <johnathanx.mantey@intel.com> +Date: Thu, 30 Jan 2020 15:07:39 -0800 +Subject: [PATCH] Enhance DHCP beyond just OFF and IPv4/IPv6 enabled. + +DHCP is not a binary option. The network interface can have DHCP +disabled, IPv4 only, IPv6 only, and IPv4/IPv6. + +Tested: +Using dbus-send or busctl: +Disabled DHCP, and confirmed only link local addresses were present. + +Assigned only static addresses. Both with/and without the gateway set +to 0.0.0.0 + +Deleted static IPv4 addresses. +Reassigned static addresses. + +Enabled DHCP for ipv4 only, and witnessed a DHCP server assign a valid +address. + +Assigned static IPv4 address. +Assigned static IPv6 address. +Confirmed both IPv4 and IPv6 static addresses are active. + +Enabled DHCP for ipv6 only, and confirmed the static v4 address +remains. The ipv6 address is removed, waiting for a DHCP6 server. + +Enabled DHCP for both ipv4 and ipv6. IPv4 address was assigned. IPv6 +address is assumed to succeed, as systemd config file enables IPv6 +DHCP. + +Change-Id: I2e0ff80ac3a5e88bcff28adac419bf21e37be162 +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +--- + Makefile.am | 1 + + configure.ac | 1 + + ethernet_interface.cpp | 146 ++++++++++++++++++++++--------- + ethernet_interface.hpp | 31 ++++++- + test/test_ethernet_interface.cpp | 3 +- + test/test_vlan_interface.cpp | 3 +- + types.hpp | 3 + + util.cpp | 16 +++- + util.hpp | 7 +- + vlan_interface.cpp | 2 +- + vlan_interface.hpp | 4 +- + 11 files changed, 164 insertions(+), 53 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 3bb5e7b..0bbbc8f 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -97,6 +97,7 @@ phosphor_network_manager_CXXFLAGS = \ + $(SDEVENTPLUS_CFLAGS) \ + $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \ + $(PHOSPHOR_LOGGING_CFLAGS) \ ++ -DBOOST_ASIO_DISABLE_THREADS \ + -flto + + if FEATURE_NIC_ETHTOOL +diff --git a/configure.ac b/configure.ac +index 12d6caa..fed3e09 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -36,6 +36,7 @@ AC_PATH_PROG([SDBUSPLUSPLUS], [sdbus++]) + PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging]) + PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces]) + PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0]) ++AC_CHECK_HEADER(boost/algorithm/string/split.hpp, [], [AC_MSG_ERROR([Could not find boost/algorithm/string/split.hpp])]) + + # Checks for header files. + AC_CHECK_HEADER(systemd/sd-bus.h, ,\ +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index fca86bd..3fb0f5e 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -3,7 +3,6 @@ + #include "ethernet_interface.hpp" + + #include "config_parser.hpp" +-#include "ipaddress.hpp" + #include "neighbor.hpp" + #include "network_manager.hpp" + #include "vlan_interface.hpp" +@@ -62,10 +61,12 @@ struct EthernetIntfSocket + + int sock{-1}; + }; ++std::map<std::string, std::string> mapDHCPToSystemd = { ++ {"both", "true"}, {"v4", "ipv4"}, {"v6", "ipv6"}, {"none", "false"}}; + + EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + const std::string& objPath, +- bool dhcpEnabled, Manager& parent, ++ DHCPConf dhcpEnabled, Manager& parent, + bool emitSignal) : + Ifaces(bus, objPath.c_str(), true), + bus(bus), manager(parent), objPath(objPath) +@@ -112,6 +113,65 @@ static IP::Protocol convertFamily(int family) + throw std::invalid_argument("Bad address family"); + } + ++void EthernetInterface::disableDHCP(IP::Protocol protocol) ++{ ++ DHCPConf dhcpState = EthernetInterfaceIntf::dHCPEnabled(); ++ if (dhcpState == EthernetInterface::DHCPConf::both) ++ { ++ if (protocol == IP::Protocol::IPv4) ++ { ++ dHCPEnabled(EthernetInterface::DHCPConf::v6); ++ } ++ else if (protocol == IP::Protocol::IPv6) ++ { ++ dHCPEnabled(EthernetInterface::DHCPConf::v4); ++ } ++ } ++ else if ((dhcpState == EthernetInterface::DHCPConf::v4) && ++ (protocol == IP::Protocol::IPv4)) ++ { ++ dHCPEnabled(EthernetInterface::DHCPConf::none); ++ } ++ else if ((dhcpState == EthernetInterface::DHCPConf::v6) && ++ (protocol == IP::Protocol::IPv6)) ++ { ++ dHCPEnabled(EthernetInterface::DHCPConf::none); ++ } ++} ++ ++bool EthernetInterface::dhcpIsEnabled(IP::Protocol family, bool ignoreProtocol) ++{ ++ return ((EthernetInterfaceIntf::dHCPEnabled() == ++ EthernetInterface::DHCPConf::both) || ++ ((EthernetInterfaceIntf::dHCPEnabled() == ++ EthernetInterface::DHCPConf::v6) && ++ ((family == IP::Protocol::IPv6) || ignoreProtocol)) || ++ ((EthernetInterfaceIntf::dHCPEnabled() == ++ EthernetInterface::DHCPConf::v4) && ++ ((family == IP::Protocol::IPv4) || ignoreProtocol))); ++} ++ ++bool EthernetInterface::dhcpToBeEnabled(IP::Protocol family, ++ std::string& nextDHCPState) ++{ ++ return ((nextDHCPState == "true") || ++ ((nextDHCPState == "ipv6") && (family == IP::Protocol::IPv6)) || ++ ((nextDHCPState == "ipv4") && (family == IP::Protocol::IPv4))); ++} ++ ++bool EthernetInterface::addressIsStatic(IP::AddressOrigin origin) ++{ ++ return ( ++#ifdef LINK_LOCAL_AUTOCONFIGURATION ++ (origin == IP::AddressOrigin::Static) ++#else ++ (origin == IP::AddressOrigin::Static || ++ origin == IP::AddressOrigin::LinkLocal) ++#endif ++ ++ ); ++} ++ + void EthernetInterface::createIPAddressObjects() + { + addrs.clear(); +@@ -122,7 +182,7 @@ void EthernetInterface::createIPAddressObjects() + { + IP::Protocol addressType = convertFamily(addr.addrType); + IP::AddressOrigin origin = IP::AddressOrigin::Static; +- if (dHCPEnabled()) ++ if (dhcpIsEnabled(addressType)) + { + origin = IP::AddressOrigin::DHCP; + } +@@ -183,11 +243,11 @@ ObjectPath EthernetInterface::iP(IP::Protocol protType, std::string ipaddress, + uint8_t prefixLength, std::string gateway) + { + +- if (dHCPEnabled()) ++ if (dhcpIsEnabled(protType)) + { + log<level::INFO>("DHCP enabled on the interface"), + entry("INTERFACE=%s", interfaceName().c_str()); +- dHCPEnabled(false); ++ disableDHCP(protType); + } + + IP::AddressOrigin origin = IP::AddressOrigin::Static; +@@ -460,7 +520,7 @@ bool EthernetInterface::iPv6AcceptRA(bool value) + return value; + } + +-bool EthernetInterface::dHCPEnabled(bool value) ++EthernetInterface::DHCPConf EthernetInterface::dHCPEnabled(DHCPConf value) + { + if (value == EthernetInterfaceIntf::dHCPEnabled()) + { +@@ -552,7 +612,7 @@ void EthernetInterface::loadVLAN(VlanId id) + std::string path = objPath; + path += "_" + std::to_string(id); + +- auto dhcpEnabled = ++ DHCPConf dhcpEnabled = + getDHCPValue(manager.getConfDir().string(), vlanInterfaceName); + + auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( +@@ -574,7 +634,8 @@ ObjectPath EthernetInterface::createVLAN(VlanId id) + path += "_" + std::to_string(id); + + auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( +- bus, path.c_str(), false, id, *this, manager); ++ bus, path.c_str(), EthernetInterface::DHCPConf::none, id, *this, ++ manager); + + // write the device file for the vlan interface. + vlanIntf->writeDeviceFile(); +@@ -647,8 +708,6 @@ void EthernetInterface::writeConfigurationFile() + // write all the static ip address in the systemd-network conf file + + using namespace std::string_literals; +- using AddressOrigin = +- sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin; + namespace fs = std::experimental::filesystem; + + // if there is vlan interafce then write the configuration file +@@ -717,42 +776,45 @@ void EthernetInterface::writeConfigurationFile() + } + + // Add the DHCP entry +- auto value = dHCPEnabled() ? "true"s : "false"s; +- stream << "DHCP="s + value + "\n"; +- +- // When the interface configured as dhcp, we don't need below given entries +- // in config file. +- if (dHCPEnabled() == false) ++ std::string value = convertForMessage(EthernetInterfaceIntf::dHCPEnabled()); ++ std::string::size_type loc = value.rfind("."); ++ std::string requestedDHCPState = value.substr(loc + 1); ++ std::string mappedDHCPState = mapDHCPToSystemd[requestedDHCPState]; ++ stream << "DHCP="s + mappedDHCPState + "\n"; ++ ++ bool dhcpv6Requested = dhcpToBeEnabled(IP::Protocol::IPv6, mappedDHCPState); ++ bool dhcpv4Requested = dhcpToBeEnabled(IP::Protocol::IPv4, mappedDHCPState); ++ // Static IP addresses ++ for (const auto& addr : addrs) + { +- // Static +- for (const auto& addr : addrs) ++ bool isValidIPv4 = isValidIP(AF_INET, addr.second->address()); ++ bool isValidIPv6 = isValidIP(AF_INET6, addr.second->address()); ++ if (((!dhcpv4Requested && isValidIPv4) || ++ (!dhcpv6Requested && isValidIPv6)) && ++ addressIsStatic(addr.second->origin())) + { +- if (addr.second->origin() == AddressOrigin::Static +-#ifndef LINK_LOCAL_AUTOCONFIGURATION +- || addr.second->origin() == AddressOrigin::LinkLocal +-#endif +- ) +- { +- std::string address = +- addr.second->address() + "/" + +- std::to_string(addr.second->prefixLength()); +- +- stream << "Address=" << address << "\n"; +- } ++ // Process all static addresses ++ std::string address = addr.second->address() + "/" + ++ std::to_string(addr.second->prefixLength()); ++ ++ // build the address entries. Do not use [Network] shortcuts to ++ // insert address entries. ++ stream << "[Address]\n"; ++ stream << "Address=" << address << "\n"; + } ++ } + +- if (manager.getSystemConf()) ++ if (manager.getSystemConf()) ++ { ++ const auto& gateway = manager.getSystemConf()->defaultGateway(); ++ if (!gateway.empty()) + { +- const auto& gateway = manager.getSystemConf()->defaultGateway(); +- if (!gateway.empty()) +- { +- stream << "Gateway=" << gateway << "\n"; +- } +- const auto& gateway6 = manager.getSystemConf()->defaultGateway6(); +- if (!gateway6.empty()) +- { +- stream << "Gateway=" << gateway6 << "\n"; +- } ++ stream << "Gateway=" << gateway << "\n"; ++ } ++ const auto& gateway6 = manager.getSystemConf()->defaultGateway6(); ++ if (!gateway6.empty()) ++ { ++ stream << "Gateway=" << gateway6 << "\n"; + } + } + +@@ -863,7 +925,7 @@ std::string EthernetInterface::mACAddress(std::string value) + + void EthernetInterface::deleteAll() + { +- if (EthernetInterfaceIntf::dHCPEnabled()) ++ if (dhcpIsEnabled(IP::Protocol::IPv4, true)) + { + log<level::INFO>("DHCP enabled on the interface"), + entry("INTERFACE=%s", interfaceName().c_str()); +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index 058d328..4e36ae8 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -92,7 +92,7 @@ class EthernetInterface : public Ifaces + * send. + */ + EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, +- bool dhcpEnabled, Manager& parent, ++ DHCPConf dhcpEnabled, Manager& parent, + bool emitSignal = true); + + /** @brief Function to create ipaddress dbus object. +@@ -158,7 +158,34 @@ class EthernetInterface : public Ifaces + } + + /** Set value of DHCPEnabled */ +- bool dHCPEnabled(bool value) override; ++ DHCPConf dHCPEnabled(DHCPConf value) override; ++ ++ /** @brief Determines if DHCP is active for the IP::Protocol supplied. ++ * @param[in] protocol - Either IPv4 or IPv6 ++ * @param[in] ignoreProtocol - Allows IPv4 and IPv6 to be checked using a ++ * single call. ++ * @returns true/false value if DHCP is active for the input protocol ++ */ ++ bool dhcpIsEnabled(IP::Protocol protocol, bool ignoreProtocol = false); ++ ++ /** @brief Determines if DHCP will be active following next reconfig ++ * @param[in] protocol - Either IPv4 or IPv6 ++ * @param[in] nextDHCPState - The new DHCP mode to take affect ++ * @returns true/false value if DHCP is active for the input protocol ++ */ ++ bool dhcpToBeEnabled(IP::Protocol family, std::string& nextDHCPState); ++ ++ /** @brief Determines if the address is manually assigned ++ * @param[in] origin - The origin entry of the IP::Address ++ * @returns true/false value if the address is static ++ */ ++ bool addressIsStatic(IP::AddressOrigin origin); ++ ++ /** @brief Selectively disables DHCP ++ * @param[in] protocol - The IPv4 or IPv6 protocol to return to static ++ * addressing mode ++ */ ++ void disableDHCP(IP::Protocol protocol); + + /** Retrieve Link State */ + bool linkUp() const override; +diff --git a/test/test_ethernet_interface.cpp b/test/test_ethernet_interface.cpp +index 30dee8a..87fd68d 100644 +--- a/test/test_ethernet_interface.cpp ++++ b/test/test_ethernet_interface.cpp +@@ -58,7 +58,8 @@ class TestEthernetInterface : public testing::Test + { + mock_clear(); + mock_addIF("test0", 1, mac); +- return {bus, "/xyz/openbmc_test/network/test0", false, manager}; ++ return {bus, "/xyz/openbmc_test/network/test0", ++ EthernetInterface::DHCPConf::none, manager}; + } + + int countIPObjects() +diff --git a/test/test_vlan_interface.cpp b/test/test_vlan_interface.cpp +index 1dffc7e..e49b43f 100644 +--- a/test/test_vlan_interface.cpp ++++ b/test/test_vlan_interface.cpp +@@ -50,7 +50,8 @@ class TestVlanInterface : public testing::Test + { + mock_clear(); + mock_addIF("test0", 1); +- return {bus, "/xyz/openbmc_test/network/test0", false, manager}; ++ return {bus, "/xyz/openbmc_test/network/test0", ++ EthernetInterface::DHCPConf::none, manager}; + } + + void setConfDir() +diff --git a/types.hpp b/types.hpp +index 123067a..c4409fe 100644 +--- a/types.hpp ++++ b/types.hpp +@@ -1,5 +1,7 @@ + #pragma once + ++#include "ipaddress.hpp" ++ + #include <ifaddrs.h> + #include <netinet/in.h> + #include <systemd/sd-event.h> +@@ -50,6 +52,7 @@ struct AddrInfo + { + uint8_t addrType; + std::string ipaddress; ++ IP::AddressOrigin origin; + uint16_t prefix; + }; + +diff --git a/util.cpp b/util.cpp +index 13a607f..554d7f6 100644 +--- a/util.cpp ++++ b/util.cpp +@@ -410,9 +410,11 @@ std::optional<std::string> interfaceToUbootEthAddr(const char* intf) + return "eth" + std::to_string(idx) + "addr"; + } + +-bool getDHCPValue(const std::string& confDir, const std::string& intf) ++EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir, ++ const std::string& intf) + { +- bool dhcp = false; ++ EthernetInterfaceIntf::DHCPConf dhcp = ++ EthernetInterfaceIntf::DHCPConf::none; + // Get the interface mode value from systemd conf + // using namespace std::string_literals; + fs::path confPath = confDir; +@@ -434,7 +436,15 @@ bool getDHCPValue(const std::string& confDir, const std::string& intf) + // There will be only single value for DHCP key. + if (values[0] == "true") + { +- dhcp = true; ++ dhcp = EthernetInterfaceIntf::DHCPConf::both; ++ } ++ else if (values[0] == "ipv4") ++ { ++ dhcp = EthernetInterfaceIntf::DHCPConf::v4; ++ } ++ else if (values[0] == "ipv6") ++ { ++ dhcp = EthernetInterfaceIntf::DHCPConf::v6; + } + return dhcp; + } +diff --git a/util.hpp b/util.hpp +index 251aa0d..ee11f4e 100644 +--- a/util.hpp ++++ b/util.hpp +@@ -13,12 +13,16 @@ + #include <sdbusplus/bus.hpp> + #include <string> + #include <string_view> ++#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> + + namespace phosphor + { + namespace network + { + ++using EthernetInterfaceIntf = ++ sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; ++ + constexpr auto IPV4_MIN_PREFIX_LENGTH = 1; + constexpr auto IPV4_MAX_PREFIX_LENGTH = 32; + constexpr auto IPV6_MAX_PREFIX_LENGTH = 64; +@@ -156,7 +160,8 @@ std::optional<std::string> interfaceToUbootEthAddr(const char* intf); + * @param[in] confDir - Network configuration directory. + * @param[in] intf - Interface name. + */ +-bool getDHCPValue(const std::string& confDir, const std::string& intf); ++EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir, ++ const std::string& intf); + + namespace internal + { +diff --git a/vlan_interface.cpp b/vlan_interface.cpp +index 73de4e8..26282cb 100644 +--- a/vlan_interface.cpp ++++ b/vlan_interface.cpp +@@ -22,7 +22,7 @@ using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + + VlanInterface::VlanInterface(sdbusplus::bus::bus& bus, +- const std::string& objPath, bool dhcpEnabled, ++ const std::string& objPath, DHCPConf dhcpEnabled, + uint32_t vlanID, EthernetInterface& intf, + Manager& parent) : + VlanIface(bus, objPath.c_str()), +diff --git a/vlan_interface.hpp b/vlan_interface.hpp +index a994d05..37ae7ee 100644 +--- a/vlan_interface.hpp ++++ b/vlan_interface.hpp +@@ -45,8 +45,8 @@ class VlanInterface : public VlanIface, + * @param[in] manager - network manager object. + */ + VlanInterface(sdbusplus::bus::bus& bus, const std::string& objPath, +- bool dhcpEnabled, uint32_t vlanID, EthernetInterface& intf, +- Manager& manager); ++ DHCPConf dhcpEnabled, uint32_t vlanID, ++ EthernetInterface& intf, Manager& manager); + + /** @brief Delete this d-bus object. + */ +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0011-Added-enable-disable-control-of-the-Network-Interfac.patch b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0011-Added-enable-disable-control-of-the-Network-Interfac.patch new file mode 100644 index 000000000..58dcf3f21 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/phosphor-network/0011-Added-enable-disable-control-of-the-Network-Interfac.patch @@ -0,0 +1,189 @@ +From 4bfb4ad5ff795d78e06fbeaf1664df6819880f50 Mon Sep 17 00:00:00 2001 +From: Johnathan Mantey <johnathanx.mantey@intel.com> +Date: Tue, 29 Oct 2019 16:20:28 -0700 +Subject: [PATCH] Added enable/disable control of the Network Interface Card + +Implemented enable/disable function to perform +"ip link set eth(x) up" +"ip link set eth(x) down" +functionality from DBus. + +Tested: + +Confirmed Redfish PATCH commands on the InterfaceEnabled property +changes the NIC state. Confirmed the NIC is DOWN/UP using "ip link". +Confirmed "ip link" state changes can be obsserved from dbus-send +commands, and from Redfish GET actions. + +Confirmed the link is inactive after a reboot. + +Confirmed link stays down despite assigning an IP manually. + +Confirmed link stays down despite enabling DHCP. + +Change-Id: I4152b53055e6546f7a6ca81b5a5eef6f689bcc66 +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +--- + ethernet_interface.cpp | 73 ++++++++++++++++++++++++++++++++++++++++-- + ethernet_interface.hpp | 11 ++++++- + 2 files changed, 81 insertions(+), 3 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index 8b8f698..a2754a4 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -85,6 +85,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + EthernetInterfaceIntf::autoNeg(std::get<2>(ifInfo)); + EthernetInterfaceIntf::speed(std::get<0>(ifInfo)); + EthernetInterfaceIntf::linkUp(std::get<3>(ifInfo)); ++ EthernetInterfaceIntf::nICEnabled(std::get<4>(ifInfo)); + #endif + getChannelPrivilege(intfName); + +@@ -323,11 +324,12 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + Autoneg autoneg{0}; + DuplexMode duplex{0}; + LinkUp linkState{false}; ++ NICEnabled nicEnabled{false}; + EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (eifSocket.sock < 0) + { +- return std::make_tuple(speed, duplex, autoneg, linkState); ++ return std::make_tuple(speed, duplex, autoneg, linkState, nicEnabled); + } + + std::strncpy(ifr.ifr_name, interfaceName().c_str(), IFNAMSIZ - 1); +@@ -341,9 +343,10 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + autoneg = edata.autoneg; + } + ++ nicEnabled = nICEnabled(); + linkState = linkUp(); + +- return std::make_tuple(speed, duplex, autoneg, linkState); ++ return std::make_tuple(speed, duplex, autoneg, linkState, nicEnabled); + } + #endif + +@@ -548,6 +551,67 @@ bool EthernetInterface::linkUp() const + log<level::ERR>("ioctl failed for SIOCGIFFLAGS:", + entry("ERROR=%s", strerror(errno))); + } ++ return value; ++} ++ ++bool EthernetInterface::nICEnabled() const ++{ ++ EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); ++ bool value = EthernetInterfaceIntf::nICEnabled(); ++ ++ if (eifSocket.sock < 0) ++ { ++ return value; ++ } ++ ++ ifreq ifr{0}; ++ std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); ++ if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) == 0) ++ { ++ value = static_cast<bool>(ifr.ifr_flags & IFF_UP); ++ } ++ else ++ { ++ log<level::ERR>("ioctl failed for SIOCGIFFLAGS:", ++ entry("ERROR=%s", strerror(errno))); ++ } ++ return value; ++} ++ ++bool EthernetInterface::nICEnabled(bool value) ++{ ++ if (value == EthernetInterfaceIntf::nICEnabled()) ++ { ++ return value; ++ } ++ ++ EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP); ++ if (eifSocket.sock < 0) ++ { ++ return EthernetInterfaceIntf::nICEnabled(); ++ } ++ ++ ifreq ifr{0}; ++ std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); ++ if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) != 0) ++ { ++ log<level::ERR>("ioctl failed for SIOCGIFFLAGS:", ++ entry("ERROR=%s", strerror(errno))); ++ return EthernetInterfaceIntf::nICEnabled(); ++ } ++ ++ ifr.ifr_flags &= ~IFF_UP; ++ ifr.ifr_flags |= value ? IFF_UP : 0; ++ ++ if (ioctl(eifSocket.sock, SIOCSIFFLAGS, &ifr) != 0) ++ { ++ log<level::ERR>("ioctl failed for SIOCSIFFLAGS:", ++ entry("ERROR=%s", strerror(errno))); ++ return EthernetInterfaceIntf::nICEnabled(); ++ } ++ EthernetInterfaceIntf::nICEnabled(value); ++ writeConfigurationFile(); ++ manager.restartSystemdUnit(networkdService); + + return value; + } +@@ -742,6 +806,11 @@ void EthernetInterface::writeConfigurationFile() + stream << "MACAddress=" << mac << "\n"; + } + ++ if (!nICEnabled()) ++ { ++ stream << "Unmanaged=yes\n"; ++ } ++ + // write the network section + stream << "[Network]\n"; + #ifdef LINK_LOCAL_AUTOCONFIGURATION +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index 4e36ae8..104750e 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -60,9 +60,11 @@ using LinkSpeed = uint16_t; + using DuplexMode = uint8_t; + using Autoneg = uint8_t; + using LinkUp = bool; ++using NICEnabled = bool; + using VlanId = uint32_t; + using InterfaceName = std::string; +-using InterfaceInfo = std::tuple<LinkSpeed, DuplexMode, Autoneg, LinkUp>; ++using InterfaceInfo = ++ std::tuple<LinkSpeed, DuplexMode, Autoneg, LinkUp, NICEnabled>; + using AddressMap = std::map<std::string, std::shared_ptr<IPAddress>>; + using NeighborMap = std::map<std::string, std::shared_ptr<Neighbor>>; + using VlanInterfaceMap = +@@ -190,6 +192,12 @@ class EthernetInterface : public Ifaces + /** Retrieve Link State */ + bool linkUp() const override; + ++ /** Retrieve NIC State */ ++ bool nICEnabled() const override; ++ ++ /** Set value of NICEnabled */ ++ bool nICEnabled(bool value) override; ++ + /** @brief sets the MAC address. + * @param[in] value - MAC address which needs to be set on the system. + * @returns macAddress of the interface or throws an error. +@@ -246,6 +254,7 @@ class EthernetInterface : public Ifaces + using EthernetInterfaceIntf::dHCPEnabled; + using EthernetInterfaceIntf::interfaceName; + using EthernetInterfaceIntf::linkUp; ++ using EthernetInterfaceIntf::nICEnabled; + using MacAddressIntf::mACAddress; + + /** @brief Absolute path of the resolv conf file */ +-- +2.24.1 + |