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