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> reloadTimer = nullptr; 52 53 #ifdef SYNC_MAC_FROM_INVENTORY 54 std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr; 55 std::vector<std::string> first_boot_status; 56 57 bool setInventoryMACOnSystem(sdbusplus::bus_t& 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_t& bus, const nlohmann::json& configJson) 108 { 109 log<level::INFO>("Registering the Inventory Signals Matcher"); 110 111 static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch; 112 113 auto callback = [&](sdbusplus::message_t& 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_t>( 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_t& bus, 160 const nlohmann::json& configJson) 161 { 162 auto mycallback = [&](sdbusplus::message_t& 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_t>( 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 void reloadNetworkd() 248 { 249 if (manager) 250 { 251 log<level::INFO>("Sending networkd reload"); 252 manager->doReloadConfigs(); 253 log<level::INFO>("Done networkd reload"); 254 } 255 } 256 257 void initializeTimers() 258 { 259 auto event = sdeventplus::Event::get_default(); 260 refreshObjectTimer = 261 std::make_unique<Timer>(event, std::bind(refreshObjects)); 262 reloadTimer = std::make_unique<Timer>(event, std::bind(reloadNetworkd)); 263 } 264 265 } // namespace network 266 } // namespace phosphor 267 268 void createNetLinkSocket(phosphor::Descriptor& smartSock) 269 { 270 // RtnetLink socket 271 auto fd = socket(PF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE); 272 if (fd < 0) 273 { 274 log<level::ERR>("Unable to create the net link socket", 275 entry("ERRNO=%d", errno)); 276 elog<InternalFailure>(); 277 } 278 smartSock.set(fd); 279 } 280 281 int main(int /*argc*/, char** /*argv*/) 282 { 283 phosphor::network::initializeTimers(); 284 285 auto bus = sdbusplus::bus::new_default(); 286 287 // Need sd_event to watch for OCC device errors 288 sd_event* event = nullptr; 289 auto r = sd_event_default(&event); 290 if (r < 0) 291 { 292 log<level::ERR>("Error creating a default sd_event handler"); 293 return r; 294 } 295 296 phosphor::network::EventPtr eventPtr{event}; 297 event = nullptr; 298 299 // Attach the bus to sd_event to service user requests 300 bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL); 301 302 // Add sdbusplus Object Manager for the 'root' path of the network manager. 303 sdbusplus::server::manager_t objManager(bus, DEFAULT_OBJPATH); 304 bus.request_name(DEFAULT_BUSNAME); 305 306 phosphor::network::manager = std::make_unique<phosphor::network::Manager>( 307 bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR); 308 309 // create the default network files if the network file 310 // is not there for any interface. 311 // Parameter false means don't create the network 312 // files forcefully. 313 if (phosphor::network::manager->createDefaultNetworkFiles(false)) 314 { 315 phosphor::network::manager->reloadConfigs(); 316 } 317 318 // RtnetLink socket 319 phosphor::Descriptor smartSock; 320 createNetLinkSocket(smartSock); 321 322 // RTNETLINK event handler 323 phosphor::network::rtnetlink::Server svr(eventPtr, smartSock); 324 325 #ifdef SYNC_MAC_FROM_INVENTORY 326 std::ifstream in(configFile); 327 nlohmann::json configJson; 328 in >> configJson; 329 phosphor::network::watchEthernetInterface(bus, configJson); 330 #endif 331 332 // Trigger the initial object scan 333 // This is intentionally deferred, to ensure that systemd-networkd is 334 // fully configured. 335 phosphor::network::refreshObjectTimer->restartOnce( 336 phosphor::network::refreshTimeout); 337 338 sd_event_loop(eventPtr.get()); 339 } 340