1 #include "config.h" 2 3 #include "network_manager.hpp" 4 #include "rtnetlink_server.hpp" 5 #include "types.hpp" 6 7 #include <linux/netlink.h> 8 9 #include <filesystem> 10 #include <fstream> 11 #include <functional> 12 #include <memory> 13 #ifdef SYNC_MAC_FROM_INVENTORY 14 #include <nlohmann/json.hpp> 15 #endif 16 #include <phosphor-logging/elog-errors.hpp> 17 #include <phosphor-logging/log.hpp> 18 #include <sdbusplus/bus.hpp> 19 #include <sdbusplus/bus/match.hpp> 20 #include <sdbusplus/server/manager.hpp> 21 #include <sdeventplus/event.hpp> 22 #include <xyz/openbmc_project/Common/error.hpp> 23 24 using phosphor::logging::elog; 25 using phosphor::logging::entry; 26 using phosphor::logging::level; 27 using phosphor::logging::log; 28 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 29 using DbusObjectPath = std::string; 30 using DbusInterface = std::string; 31 using PropertyValue = std::string; 32 33 constexpr char NETWORK_CONF_DIR[] = "/etc/systemd/network"; 34 35 constexpr char DEFAULT_OBJPATH[] = "/xyz/openbmc_project/network"; 36 37 constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; 38 constexpr auto configFile = "/usr/share/network/config.json"; 39 40 constexpr auto invNetworkIntf = 41 "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 42 43 namespace phosphor 44 { 45 namespace network 46 { 47 48 std::unique_ptr<phosphor::network::Manager> manager = nullptr; 49 std::unique_ptr<Timer> refreshObjectTimer = nullptr; 50 std::unique_ptr<Timer> reloadTimer = nullptr; 51 52 #ifdef SYNC_MAC_FROM_INVENTORY 53 std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr; 54 std::vector<std::string> first_boot_status; 55 56 bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, 57 const nlohmann::json& configJson, 58 const std::string& intfname) 59 { 60 try 61 { 62 auto inventoryMAC = mac_address::getfromInventory(bus, intfname); 63 if (!mac_address::toString(inventoryMAC).empty()) 64 { 65 log<level::INFO>("Mac Address in Inventory on "), 66 entry("Interface : ", intfname.c_str()), 67 entry("MAC Address :", 68 (mac_address::toString(inventoryMAC)).c_str()); 69 manager->setFistBootMACOnInterface(std::make_pair( 70 intfname.c_str(), mac_address::toString(inventoryMAC))); 71 first_boot_status.push_back(intfname.c_str()); 72 bool status = true; 73 for (const auto& keys : configJson.items()) 74 { 75 if (!(std::find(first_boot_status.begin(), 76 first_boot_status.end(), 77 keys.key()) != first_boot_status.end())) 78 { 79 log<level::INFO>("Interface MAC is NOT set from VPD"), 80 entry("INTERFACE", keys.key().c_str()); 81 status = false; 82 } 83 } 84 if (status) 85 { 86 log<level::INFO>("Removing the match for ethernet interfaces"); 87 phosphor::network::EthInterfaceMatch = nullptr; 88 } 89 } 90 else 91 { 92 log<level::INFO>("Nothing is present in Inventory"); 93 return false; 94 } 95 } 96 catch (const std::exception& e) 97 { 98 log<level::ERR>("Exception occurred during getting of MAC " 99 "address from Inventory"); 100 return false; 101 } 102 return true; 103 } 104 105 // register the macthes to be monitored from inventory manager 106 void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson) 107 { 108 log<level::INFO>("Registering the Inventory Signals Matcher"); 109 110 static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch; 111 112 auto callback = [&](sdbusplus::message_t& m) { 113 std::map<DbusObjectPath, 114 std::map<DbusInterface, std::variant<PropertyValue>>> 115 interfacesProperties; 116 117 sdbusplus::message::object_path objPath; 118 std::pair<std::string, std::string> ethPair; 119 m.read(objPath, interfacesProperties); 120 121 for (const auto& pattern : configJson.items()) 122 { 123 if (objPath.str.find(pattern.value()) != std::string::npos) 124 { 125 for (auto& interface : interfacesProperties) 126 { 127 if (interface.first == invNetworkIntf) 128 { 129 for (const auto& property : interface.second) 130 { 131 if (property.first == "MACAddress") 132 { 133 ethPair = std::make_pair( 134 pattern.key(), 135 std::get<std::string>(property.second)); 136 break; 137 } 138 } 139 break; 140 } 141 } 142 if (!(ethPair.first.empty() || ethPair.second.empty())) 143 { 144 manager->setFistBootMACOnInterface(ethPair); 145 } 146 } 147 } 148 }; 149 150 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>( 151 bus, 152 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 153 "member='InterfacesAdded',path='/xyz/openbmc_project/" 154 "inventory'", 155 callback); 156 } 157 158 void watchEthernetInterface(sdbusplus::bus_t& bus, 159 const nlohmann::json& configJson) 160 { 161 auto mycallback = [&](sdbusplus::message_t& m) { 162 std::map<DbusObjectPath, 163 std::map<DbusInterface, std::variant<PropertyValue>>> 164 interfacesProperties; 165 166 sdbusplus::message::object_path objPath; 167 std::pair<std::string, std::string> ethPair; 168 m.read(objPath, interfacesProperties); 169 for (const auto& interfaces : interfacesProperties) 170 { 171 if (interfaces.first == 172 "xyz.openbmc_project.Network.EthernetInterface") 173 { 174 for (const auto& property : interfaces.second) 175 { 176 if (property.first == "InterfaceName") 177 { 178 std::string infname = 179 std::get<std::string>(property.second); 180 181 if (configJson.find(infname) == configJson.end()) 182 { 183 // ethernet interface not found in configJSON 184 // check if it is not sit0 interface, as it is 185 // expected. 186 if (infname != "sit0") 187 { 188 log<level::ERR>( 189 "Wrong Interface Name in Config Json"); 190 } 191 } 192 else 193 { 194 if (!phosphor::network::setInventoryMACOnSystem( 195 bus, configJson, infname)) 196 { 197 phosphor::network::registerSignals(bus, 198 configJson); 199 phosphor::network::EthInterfaceMatch = nullptr; 200 } 201 } 202 break; 203 } 204 } 205 break; 206 } 207 } 208 }; 209 // Incase if phosphor-inventory-manager started early and the VPD is already 210 // collected by the time network service has come up, better to check the 211 // VPD directly and set the MAC Address on the respective Interface. 212 213 bool registeredSignals = false; 214 for (const auto& interfaceString : configJson.items()) 215 { 216 if (!std::filesystem::exists(firstBootPath + interfaceString.key()) && 217 !registeredSignals) 218 { 219 220 log<level::INFO>( 221 "First boot file is not present, check VPD for MAC"); 222 phosphor::network::EthInterfaceMatch = std::make_unique< 223 sdbusplus::bus::match_t>( 224 bus, 225 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 226 "member='InterfacesAdded',path='/xyz/openbmc_project/network'", 227 mycallback); 228 registeredSignals = true; 229 } 230 } 231 } 232 233 #endif 234 235 /** @brief refresh the network objects. */ 236 void refreshObjects() 237 { 238 if (manager) 239 { 240 log<level::INFO>("Refreshing the objects."); 241 manager->createChildObjects(); 242 log<level::INFO>("Refreshing complete."); 243 } 244 } 245 246 void reloadNetworkd() 247 { 248 if (manager) 249 { 250 log<level::INFO>("Sending networkd reload"); 251 manager->doReloadConfigs(); 252 log<level::INFO>("Done networkd reload"); 253 } 254 } 255 256 void initializeTimers() 257 { 258 auto event = sdeventplus::Event::get_default(); 259 refreshObjectTimer = 260 std::make_unique<Timer>(event, std::bind(refreshObjects)); 261 reloadTimer = std::make_unique<Timer>(event, std::bind(reloadNetworkd)); 262 } 263 264 } // namespace network 265 } // namespace phosphor 266 267 int main(int /*argc*/, char** /*argv*/) 268 { 269 phosphor::network::initializeTimers(); 270 271 auto bus = sdbusplus::bus::new_default(); 272 273 // Need sd_event to watch for OCC device errors 274 sd_event* event = nullptr; 275 auto r = sd_event_default(&event); 276 if (r < 0) 277 { 278 log<level::ERR>("Error creating a default sd_event handler"); 279 return r; 280 } 281 282 phosphor::network::EventPtr eventPtr{event}; 283 event = nullptr; 284 285 // Attach the bus to sd_event to service user requests 286 bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL); 287 288 // Add sdbusplus Object Manager for the 'root' path of the network manager. 289 sdbusplus::server::manager_t objManager(bus, DEFAULT_OBJPATH); 290 bus.request_name(DEFAULT_BUSNAME); 291 292 phosphor::network::manager = std::make_unique<phosphor::network::Manager>( 293 bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR); 294 295 // create the default network files if the network file 296 // is not there for any interface. 297 if (phosphor::network::manager->createDefaultNetworkFiles()) 298 { 299 phosphor::network::manager->reloadConfigs(); 300 } 301 302 // RTNETLINK event handler 303 phosphor::network::rtnetlink::Server svr(eventPtr); 304 305 #ifdef SYNC_MAC_FROM_INVENTORY 306 std::ifstream in(configFile); 307 nlohmann::json configJson; 308 in >> configJson; 309 phosphor::network::watchEthernetInterface(bus, configJson); 310 #endif 311 312 // Trigger the initial object scan 313 // This is intentionally deferred, to ensure that systemd-networkd is 314 // fully configured. 315 phosphor::network::refreshObjectTimer->restartOnce( 316 phosphor::network::refreshTimeout); 317 318 sd_event_loop(eventPtr.get()); 319 } 320