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