#include "config.h" #include "network_manager.hpp" #include "rtnetlink_server.hpp" #include "types.hpp" #include "watch.hpp" #include #include #include #include #include #ifdef SYNC_MAC_FROM_INVENTORY #include #endif #include #include #include #include #include #include #include using phosphor::logging::elog; using phosphor::logging::entry; using phosphor::logging::level; using phosphor::logging::log; using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; using DbusObjectPath = std::string; using DbusInterface = std::string; using PropertyValue = std::string; constexpr char NETWORK_CONF_DIR[] = "/etc/systemd/network"; constexpr char DEFAULT_OBJPATH[] = "/xyz/openbmc_project/network"; constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; constexpr auto configFile = "/usr/share/network/config.json"; constexpr auto invNetworkIntf = "xyz.openbmc_project.Inventory.Item.NetworkInterface"; namespace phosphor { namespace network { std::unique_ptr manager = nullptr; std::unique_ptr refreshObjectTimer = nullptr; std::unique_ptr restartTimer = nullptr; #ifdef SYNC_MAC_FROM_INVENTORY std::unique_ptr EthInterfaceMatch = nullptr; std::vector first_boot_status; bool setInventoryMACOnSystem(sdbusplus::bus::bus& bus, const nlohmann::json& configJson, const std::string& intfname) { try { auto inventoryMAC = mac_address::getfromInventory(bus, intfname); if (!mac_address::toString(inventoryMAC).empty()) { log("Mac Address in Inventory on "), entry("Interface : ", intfname.c_str()), entry("MAC Address :", (mac_address::toString(inventoryMAC)).c_str()); manager->setFistBootMACOnInterface(std::make_pair( intfname.c_str(), mac_address::toString(inventoryMAC))); first_boot_status.push_back(intfname.c_str()); 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())) { log("Interface MAC is NOT set from VPD"), entry("INTERFACE", keys.key().c_str()); status = false; } } if (status) { log("Removing the match for ethernet interfaces"); phosphor::network::EthInterfaceMatch = nullptr; } } else { log("Nothing is present in Inventory"); return false; } } catch (const std::exception& e) { log("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::bus& bus, const nlohmann::json& configJson) { log("Registering the Inventory Signals Matcher"); static std::unique_ptr MacAddressMatch; auto callback = [&](sdbusplus::message::message& m) { std::map>> interfacesProperties; sdbusplus::message::object_path objPath; std::pair ethPair; 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") { ethPair = std::make_pair( pattern.key(), std::get(property.second)); break; } } break; } } if (!(ethPair.first.empty() || ethPair.second.empty())) { manager->setFistBootMACOnInterface(ethPair); } } } }; MacAddressMatch = std::make_unique( bus, "interface='org.freedesktop.DBus.ObjectManager',type='signal'," "member='InterfacesAdded',path='/xyz/openbmc_project/" "inventory'", callback); } void watchEthernetInterface(sdbusplus::bus::bus& bus, const nlohmann::json& configJson) { auto mycallback = [&](sdbusplus::message::message& m) { std::map>> interfacesProperties; sdbusplus::message::object_path objPath; std::pair ethPair; m.read(objPath, interfacesProperties); for (const auto& interfaces : interfacesProperties) { if (interfaces.first == "xyz.openbmc_project.Network.EthernetInterface") { for (const auto& property : interfaces.second) { if (property.first == "InterfaceName") { std::string infname = std::get(property.second); 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") { log( "Wrong Interface Name in Config Json"); } } else { if (!phosphor::network::setInventoryMACOnSystem( bus, configJson, infname)) { phosphor::network::registerSignals(bus, configJson); phosphor::network::EthInterfaceMatch = nullptr; } } 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 (!std::filesystem::exists(firstBootPath + interfaceString.key()) && !registeredSignals) { log( "First boot file is not present, check VPD for MAC"); phosphor::network::EthInterfaceMatch = std::make_unique< sdbusplus::bus::match::match>( bus, "interface='org.freedesktop.DBus.ObjectManager',type='signal'," "member='InterfacesAdded',path='/xyz/openbmc_project/network'", mycallback); registeredSignals = true; } } } #endif /** @brief refresh the network objects. */ void refreshObjects() { if (manager) { log("Refreshing the objects."); manager->createChildObjects(); log("Refreshing complete."); } } /** @brief restart the systemd networkd. */ void restartNetwork() { if (manager) { manager->restartSystemdUnit("systemd-networkd.service"); } } void initializeTimers() { auto event = sdeventplus::Event::get_default(); refreshObjectTimer = std::make_unique(event, std::bind(refreshObjects)); restartTimer = std::make_unique(event, std::bind(restartNetwork)); } } // namespace network } // namespace phosphor void createNetLinkSocket(phosphor::Descriptor& smartSock) { // RtnetLink socket auto fd = socket(PF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE); if (fd < 0) { log("Unable to create the net link socket", entry("ERRNO=%d", errno)); elog(); } smartSock.set(fd); } int main(int /*argc*/, char** /*argv*/) { phosphor::network::initializeTimers(); auto bus = sdbusplus::bus::new_default(); // Need sd_event to watch for OCC device errors sd_event* event = nullptr; auto r = sd_event_default(&event); if (r < 0) { log("Error creating a default sd_event handler"); return r; } phosphor::network::EventPtr eventPtr{event}; event = nullptr; // Attach the bus to sd_event to service user requests bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL); // Add sdbusplus Object Manager for the 'root' path of the network manager. sdbusplus::server::manager::manager objManager(bus, DEFAULT_OBJPATH); bus.request_name(DEFAULT_BUSNAME); phosphor::network::manager = std::make_unique( bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR); // create the default network files if the network file // is not there for any interface. // Parameter false means don't create the network // files forcefully. if (phosphor::network::manager->createDefaultNetworkFiles(false)) { // if files created restart the network. // don't need to call the create child objects as eventhandler // will create it. phosphor::network::restartNetwork(); } else { // this will add the additional fixes which is needed // in the existing network file. phosphor::network::manager->writeToConfigurationFile(); // whenever the configuration file gets written it restart // the network which creates the network objects } // RtnetLink socket phosphor::Descriptor smartSock; createNetLinkSocket(smartSock); // RTNETLINK event handler phosphor::network::rtnetlink::Server svr(eventPtr, smartSock); #ifdef SYNC_MAC_FROM_INVENTORY std::ifstream in(configFile); nlohmann::json configJson; in >> configJson; phosphor::network::watchEthernetInterface(bus, configJson); #endif sd_event_loop(eventPtr.get()); }