#include "config.h" #include "inventory_mac.hpp" #include "network_manager.hpp" #include "types.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace phosphor::network::inventory { using phosphor::logging::elog; using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; using DbusObjectPath = std::string; using DbusInterface = std::string; using PropertyValue = std::string; using DbusService = std::string; using ObjectTree = string_umap>>; constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; constexpr auto configFile = "/usr/share/network/config.json"; constexpr auto invNetworkIntf = "xyz.openbmc_project.Inventory.Item.NetworkInterface"; constexpr auto invRoot = "/xyz/openbmc_project/inventory"; constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; constexpr auto propIntf = "org.freedesktop.DBus.Properties"; constexpr auto methodGet = "Get"; Manager* manager = nullptr; std::unique_ptr EthInterfaceMatch = nullptr; std::unique_ptr MacAddressMatch = nullptr; std::vector first_boot_status; nlohmann::json configJson; void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac) { for (const auto& interface : manager->interfaces) { if (interface.first == intf) { auto returnMAC = interface.second->macAddress(mac); if (returnMAC == mac) { lg2::info("Setting MAC {NET_MAC} on interface {NET_INTF}", "NET_MAC", mac, "NET_INTF", intf); std::error_code ec; if (std::filesystem::is_directory("/var/lib/network", ec)) { std::ofstream persistentFile(firstBootPath + intf); } break; } else { lg2::info("MAC is Not Set on ethernet Interface"); } } } } ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName) { std::string interfaceName = configJson[intfName]; std::vector interfaces; interfaces.emplace_back(invNetworkIntf); auto depth = 0; auto mapperCall = bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); mapperCall.append(invRoot, depth, interfaces); auto mapperReply = bus.call(mapperCall); if (mapperReply.is_method_error()) { lg2::error("Error in mapper call"); elog(); } ObjectTree objectTree; mapperReply.read(objectTree); if (objectTree.empty()) { lg2::error("No Object has implemented the interface {NET_INTF}", "NET_INTF", invNetworkIntf); elog(); } DbusObjectPath objPath; DbusService service; if (1 == objectTree.size()) { objPath = objectTree.begin()->first; service = objectTree.begin()->second.begin()->first; } else { // If there are more than 2 objects, object path must contain the // interface name for (const auto& object : objectTree) { lg2::info("Get info on interface {NET_INTF}, object {OBJ}", "NET_INTF", interfaceName, "OBJ", object.first); if (std::string::npos != object.first.find(interfaceName.c_str())) { objPath = object.first; service = object.second.begin()->first; break; } } if (objPath.empty()) { lg2::error("Can't find the object for the interface {NET_INTF}", "NET_INTF", interfaceName); elog(); } } auto method = bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, methodGet); method.append(invNetworkIntf, "MACAddress"); auto reply = bus.call(method); if (reply.is_method_error()) { lg2::error( "Failed to get MACAddress for path {DBUS_PATH} interface {DBUS_INTF}", "DBUS_PATH", objPath, "DBUS_INTF", invNetworkIntf); elog(); } std::variant value; reply.read(value); return ToAddr{}(std::get(value)); } bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, const std::string& intfname) { try { auto inventoryMAC = getfromInventory(bus, intfname); if (inventoryMAC != ether_addr{}) { auto macStr = std::to_string(inventoryMAC); lg2::info( "Mac Address {NET_MAC} in Inventory on Interface {NET_INTF}", "NET_MAC", macStr, "NET_INTF", intfname); setFirstBootMACOnInterface(intfname, macStr); first_boot_status.push_back(intfname); bool status = true; for (const auto& keys : configJson.items()) { if (!(std::find(first_boot_status.begin(), first_boot_status.end(), keys.key()) != first_boot_status.end())) { lg2::info("Interface {NET_INTF} MAC is NOT set from VPD", "NET_INTF", keys.key()); status = false; } } if (status) { lg2::info("Removing the match for ethernet interfaces"); EthInterfaceMatch = nullptr; } } else { lg2::info("Nothing is present in Inventory"); return false; } } catch (const std::exception& e) { lg2::error("Exception occurred during getting of MAC " "address from Inventory"); return false; } return true; } // register the macthes to be monitored from inventory manager void registerSignals(sdbusplus::bus_t& bus) { lg2::info("Registering the Inventory Signals Matcher"); auto callback = [&](sdbusplus::message_t& m) { std::map>> interfacesProperties; sdbusplus::message::object_path objPath; m.read(objPath, interfacesProperties); for (const auto& pattern : configJson.items()) { if (objPath.str.find(pattern.value()) != std::string::npos) { for (auto& interface : interfacesProperties) { if (interface.first == invNetworkIntf) { for (const auto& property : interface.second) { if (property.first == "MACAddress") { setFirstBootMACOnInterface( pattern.key(), std::get(property.second)); break; } } break; } } } } }; MacAddressMatch = std::make_unique( bus, "interface='org.freedesktop.DBus.ObjectManager',type='signal'," "member='InterfacesAdded',path='/xyz/openbmc_project/" "inventory'", callback); } void watchEthernetInterface(sdbusplus::bus_t& bus) { auto handle_interface = [&](auto infname) { if (configJson.find(infname) == configJson.end()) { // ethernet interface not found in configJSON // check if it is not sit0 interface, as it is // expected. if (infname != "sit0") { lg2::error("Wrong Interface Name in Config Json"); } } else { registerSignals(bus); EthInterfaceMatch = nullptr; if (setInventoryMACOnSystem(bus, infname)) { MacAddressMatch = nullptr; } } }; auto mycallback = [&, handle_interface](sdbusplus::message_t& m) { std::map>> interfacesProperties; sdbusplus::message::object_path objPath; std::pair ethPair; m.read(objPath, interfacesProperties); for (const auto& interfaces : interfacesProperties) { lg2::info("Check {DBUS_INTF} for sdbus response", "DBUS_INTF", interfaces.first); if (interfaces.first == "xyz.openbmc_project.Network.EthernetInterface") { for (const auto& property : interfaces.second) { if (property.first == "InterfaceName") { handle_interface( std::get(property.second)); break; } } break; } } }; // Incase if phosphor-inventory-manager started early and the VPD is already // collected by the time network service has come up, better to check the // VPD directly and set the MAC Address on the respective Interface. bool registeredSignals = false; for (const auto& interfaceString : configJson.items()) { if ((FORCE_SYNC_MAC_FROM_INVENTORY || !std::filesystem::exists(firstBootPath + interfaceString.key())) && !registeredSignals) { lg2::info("Check VPD for MAC: {REASON}", "REASON", (FORCE_SYNC_MAC_FROM_INVENTORY) ? "Force sync enabled" : "First boot file is not present"); EthInterfaceMatch = std::make_unique( bus, "interface='org.freedesktop.DBus.ObjectManager',type='signal'," "member='InterfacesAdded',path='/xyz/openbmc_project/network'", mycallback); registeredSignals = true; for (const auto& intf : manager->interfaces) { if (intf.first == interfaceString.key()) { handle_interface(intf.first); } } } } } std::unique_ptr watch(stdplus::PinnedRef bus, stdplus::PinnedRef m) { manager = &m.get(); std::ifstream in(configFile); in >> configJson; watchEthernetInterface(bus); return nullptr; } } // namespace phosphor::network::inventory