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