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