1 #include "config.h" 2 3 #include "inventory_mac.hpp" 4 5 #include "network_manager.hpp" 6 #include "types.hpp" 7 8 #include <filesystem> 9 #include <fstream> 10 #include <memory> 11 #include <nlohmann/json.hpp> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/log.hpp> 14 #include <sdbusplus/bus.hpp> 15 #include <sdbusplus/bus/match.hpp> 16 #include <string> 17 #include <vector> 18 #include <xyz/openbmc_project/Common/error.hpp> 19 20 namespace phosphor::network::inventory 21 { 22 23 using phosphor::logging::elog; 24 using phosphor::logging::entry; 25 using phosphor::logging::level; 26 using phosphor::logging::log; 27 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 28 29 using DbusObjectPath = std::string; 30 using DbusInterface = std::string; 31 using PropertyValue = std::string; 32 using DbusService = std::string; 33 using ObjectTree = string_umap<string_umap<std::vector<std::string>>>; 34 35 constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; 36 constexpr auto configFile = "/usr/share/network/config.json"; 37 38 constexpr auto invNetworkIntf = 39 "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 40 constexpr auto invRoot = "/xyz/openbmc_project/inventory"; 41 constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; 42 constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; 43 constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 44 constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 45 constexpr auto methodGet = "Get"; 46 47 Manager* manager = nullptr; 48 std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr; 49 std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch = nullptr; 50 std::vector<std::string> first_boot_status; 51 nlohmann::json configJson; 52 53 void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac) 54 { 55 for (const auto& interface : manager->interfaces) 56 { 57 if (interface.first == intf) 58 { 59 auto returnMAC = interface.second->macAddress(mac); 60 if (returnMAC == mac) 61 { 62 log<level::INFO>(fmt::format("Setting MAC on {}", intf).c_str(), 63 entry("INTF=%s", intf.c_str()), 64 entry("MAC=%s", mac.c_str())); 65 std::error_code ec; 66 if (std::filesystem::is_directory("/var/lib/network", ec)) 67 { 68 std::ofstream persistentFile(firstBootPath + intf); 69 } 70 break; 71 } 72 else 73 { 74 log<level::INFO>("MAC is Not Set on ethernet Interface"); 75 } 76 } 77 } 78 } 79 80 ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName) 81 { 82 std::string interfaceName = configJson[intfName]; 83 84 std::vector<DbusInterface> interfaces; 85 interfaces.emplace_back(invNetworkIntf); 86 87 auto depth = 0; 88 89 auto mapperCall = 90 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); 91 92 mapperCall.append(invRoot, depth, interfaces); 93 94 auto mapperReply = bus.call(mapperCall); 95 if (mapperReply.is_method_error()) 96 { 97 log<level::ERR>("Error in mapper call"); 98 elog<InternalFailure>(); 99 } 100 101 ObjectTree objectTree; 102 mapperReply.read(objectTree); 103 104 if (objectTree.empty()) 105 { 106 log<level::ERR>("No Object has implemented the interface", 107 entry("INTERFACE=%s", invNetworkIntf)); 108 elog<InternalFailure>(); 109 } 110 111 DbusObjectPath objPath; 112 DbusService service; 113 114 if (1 == objectTree.size()) 115 { 116 objPath = objectTree.begin()->first; 117 service = objectTree.begin()->second.begin()->first; 118 } 119 else 120 { 121 // If there are more than 2 objects, object path must contain the 122 // interface name 123 for (auto const& object : objectTree) 124 { 125 log<level::INFO>("interface", 126 entry("INT=%s", interfaceName.c_str())); 127 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str())); 128 129 if (std::string::npos != object.first.find(interfaceName.c_str())) 130 { 131 objPath = object.first; 132 service = object.second.begin()->first; 133 break; 134 } 135 } 136 137 if (objPath.empty()) 138 { 139 log<level::ERR>("Can't find the object for the interface", 140 entry("intfName=%s", interfaceName.c_str())); 141 elog<InternalFailure>(); 142 } 143 } 144 145 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 146 propIntf, methodGet); 147 148 method.append(invNetworkIntf, "MACAddress"); 149 150 auto reply = bus.call(method); 151 if (reply.is_method_error()) 152 { 153 log<level::ERR>("Failed to get MACAddress", 154 entry("PATH=%s", objPath.c_str()), 155 entry("INTERFACE=%s", invNetworkIntf)); 156 elog<InternalFailure>(); 157 } 158 159 std::variant<std::string> value; 160 reply.read(value); 161 return ToAddr<ether_addr>{}(std::get<std::string>(value)); 162 } 163 164 bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, const std::string& intfname) 165 { 166 try 167 { 168 auto inventoryMAC = getfromInventory(bus, intfname); 169 if (inventoryMAC != ether_addr{}) 170 { 171 auto macStr = std::to_string(inventoryMAC); 172 log<level::INFO>("Mac Address in Inventory on ", 173 entry("Interface : ", intfname.c_str()), 174 entry("MAC Address :", macStr.c_str())); 175 setFirstBootMACOnInterface(intfname, macStr); 176 first_boot_status.push_back(intfname); 177 bool status = true; 178 for (const auto& keys : configJson.items()) 179 { 180 if (!(std::find(first_boot_status.begin(), 181 first_boot_status.end(), 182 keys.key()) != first_boot_status.end())) 183 { 184 log<level::INFO>("Interface MAC is NOT set from VPD"), 185 entry("INTERFACE", keys.key().c_str()); 186 status = false; 187 } 188 } 189 if (status) 190 { 191 log<level::INFO>("Removing the match for ethernet interfaces"); 192 EthInterfaceMatch = nullptr; 193 } 194 } 195 else 196 { 197 log<level::INFO>("Nothing is present in Inventory"); 198 return false; 199 } 200 } 201 catch (const std::exception& e) 202 { 203 log<level::ERR>("Exception occurred during getting of MAC " 204 "address from Inventory"); 205 return false; 206 } 207 return true; 208 } 209 210 // register the macthes to be monitored from inventory manager 211 void registerSignals(sdbusplus::bus_t& bus) 212 { 213 log<level::INFO>("Registering the Inventory Signals Matcher"); 214 215 auto callback = [&](sdbusplus::message_t& m) { 216 std::map<DbusObjectPath, 217 std::map<DbusInterface, std::variant<PropertyValue>>> 218 interfacesProperties; 219 220 sdbusplus::message::object_path objPath; 221 m.read(objPath, interfacesProperties); 222 223 for (const auto& pattern : configJson.items()) 224 { 225 if (objPath.str.find(pattern.value()) != std::string::npos) 226 { 227 for (auto& interface : interfacesProperties) 228 { 229 if (interface.first == invNetworkIntf) 230 { 231 for (const auto& property : interface.second) 232 { 233 if (property.first == "MACAddress") 234 { 235 setFirstBootMACOnInterface( 236 pattern.key(), 237 std::get<std::string>(property.second)); 238 break; 239 } 240 } 241 break; 242 } 243 } 244 } 245 } 246 }; 247 248 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>( 249 bus, 250 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 251 "member='InterfacesAdded',path='/xyz/openbmc_project/" 252 "inventory'", 253 callback); 254 } 255 256 void watchEthernetInterface(sdbusplus::bus_t& bus) 257 { 258 auto handle_interface = [&](auto infname) { 259 if (configJson.find(infname) == configJson.end()) 260 { 261 // ethernet interface not found in configJSON 262 // check if it is not sit0 interface, as it is 263 // expected. 264 if (infname != "sit0") 265 { 266 log<level::ERR>("Wrong Interface Name in Config Json"); 267 } 268 } 269 else 270 { 271 registerSignals(bus); 272 EthInterfaceMatch = nullptr; 273 274 if (setInventoryMACOnSystem(bus, infname)) 275 { 276 MacAddressMatch = nullptr; 277 } 278 } 279 }; 280 281 auto mycallback = [&, handle_interface](sdbusplus::message_t& m) { 282 std::map<DbusObjectPath, 283 std::map<DbusInterface, std::variant<PropertyValue>>> 284 interfacesProperties; 285 286 sdbusplus::message::object_path objPath; 287 std::pair<std::string, std::string> ethPair; 288 m.read(objPath, interfacesProperties); 289 290 for (const auto& interfaces : interfacesProperties) 291 { 292 log<level::INFO>(interfaces.first.c_str()); 293 if (interfaces.first == 294 "xyz.openbmc_project.Network.EthernetInterface") 295 { 296 for (const auto& property : interfaces.second) 297 { 298 if (property.first == "InterfaceName") 299 { 300 handle_interface( 301 std::get<std::string>(property.second)); 302 303 break; 304 } 305 } 306 break; 307 } 308 } 309 }; 310 // Incase if phosphor-inventory-manager started early and the VPD is already 311 // collected by the time network service has come up, better to check the 312 // VPD directly and set the MAC Address on the respective Interface. 313 314 bool registeredSignals = false; 315 for (const auto& interfaceString : configJson.items()) 316 { 317 if ((FORCE_SYNC_MAC_FROM_INVENTORY || 318 !std::filesystem::exists(firstBootPath + interfaceString.key())) && 319 !registeredSignals) 320 { 321 auto msg = fmt::format("{}, check VPD for MAC", 322 (FORCE_SYNC_MAC_FROM_INVENTORY) 323 ? "Force sync enabled" 324 : "First boot file is not present"); 325 log<level::INFO>(msg.c_str()); 326 EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>( 327 bus, 328 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 329 "member='InterfacesAdded',path='/xyz/openbmc_project/network'", 330 mycallback); 331 registeredSignals = true; 332 333 for (const auto& intf : manager->interfaces) 334 { 335 if (intf.first == interfaceString.key()) 336 { 337 handle_interface(intf.first); 338 } 339 } 340 } 341 } 342 } 343 344 std::unique_ptr<Runtime> watch(stdplus::PinnedRef<sdbusplus::bus_t> bus, 345 stdplus::PinnedRef<Manager> m) 346 { 347 manager = &m.get(); 348 std::ifstream in(configFile); 349 in >> configJson; 350 watchEthernetInterface(bus); 351 return nullptr; 352 } 353 354 } // namespace phosphor::network::inventory 355