1 #include "config.h" 2 3 #include "inventory_mac.hpp" 4 5 #include "network_manager.hpp" 6 #include "types.hpp" 7 8 #include <nlohmann/json.hpp> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <phosphor-logging/lg2.hpp> 11 #include <sdbusplus/bus.hpp> 12 #include <sdbusplus/bus/match.hpp> 13 #include <stdplus/str/maps.hpp> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 #include <filesystem> 17 #include <fstream> 18 #include <memory> 19 #include <string> 20 #include <vector> 21 22 namespace phosphor::network::inventory 23 { 24 25 using phosphor::logging::elog; 26 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 27 28 using DbusObjectPath = std::string; 29 using DbusInterface = std::string; 30 using PropertyValue = std::string; 31 using DbusService = std::string; 32 using ObjectTree = 33 stdplus::string_umap<stdplus::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 lg2::info("Setting MAC {NET_MAC} on interface {NET_INTF}", 63 "NET_MAC", mac, "NET_INTF", intf); 64 std::error_code ec; 65 if (std::filesystem::is_directory("/var/lib/network", ec)) 66 { 67 std::ofstream persistentFile(firstBootPath + intf); 68 } 69 break; 70 } 71 else 72 { 73 lg2::info("MAC is Not Set on ethernet Interface"); 74 } 75 } 76 } 77 } 78 79 stdplus::EtherAddr getfromInventory(sdbusplus::bus_t& bus, 80 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 = [&]() { 95 try 96 { 97 return bus.call(mapperCall); 98 } 99 catch (const sdbusplus::exception::SdBusError& e) 100 { 101 lg2::error("Error in mapper call"); 102 elog<InternalFailure>(); 103 } 104 }(); 105 106 ObjectTree objectTree; 107 mapperReply.read(objectTree); 108 109 if (objectTree.empty()) 110 { 111 lg2::error("No Object has implemented the interface {NET_INTF}", 112 "NET_INTF", invNetworkIntf); 113 elog<InternalFailure>(); 114 } 115 116 DbusObjectPath objPath; 117 DbusService service; 118 119 if (1 == objectTree.size()) 120 { 121 objPath = objectTree.begin()->first; 122 service = objectTree.begin()->second.begin()->first; 123 } 124 else 125 { 126 // If there are more than 2 objects, object path must contain the 127 // interface name 128 for (const auto& object : objectTree) 129 { 130 lg2::info("Get info on interface {NET_INTF}, object {OBJ}", 131 "NET_INTF", interfaceName, "OBJ", object.first); 132 if (object.first.ends_with("/" + interfaceName)) 133 { 134 objPath = object.first; 135 service = object.second.begin()->first; 136 break; 137 } 138 } 139 140 if (objPath.empty()) 141 { 142 lg2::error("Can't find the object for the interface {NET_INTF}", 143 "NET_INTF", interfaceName); 144 elog<InternalFailure>(); 145 } 146 } 147 148 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 149 propIntf, methodGet); 150 151 method.append(invNetworkIntf, "MACAddress"); 152 153 auto reply = [&]() { 154 try 155 { 156 return bus.call(method); 157 } 158 catch (const sdbusplus::exception::SdBusError& e) 159 { 160 lg2::error( 161 "Failed to get MACAddress for path {DBUS_PATH} interface {DBUS_INTF}", 162 "DBUS_PATH", objPath, "DBUS_INTF", invNetworkIntf); 163 elog<InternalFailure>(); 164 } 165 }(); 166 167 std::variant<std::string> value; 168 reply.read(value); 169 return stdplus::fromStr<stdplus::EtherAddr>(std::get<std::string>(value)); 170 } 171 172 bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, const std::string& intfname) 173 { 174 try 175 { 176 auto inventoryMAC = getfromInventory(bus, intfname); 177 if (inventoryMAC != stdplus::EtherAddr{}) 178 { 179 auto macStr = stdplus::toStr(inventoryMAC); 180 lg2::info( 181 "Mac Address {NET_MAC} in Inventory on Interface {NET_INTF}", 182 "NET_MAC", macStr, "NET_INTF", intfname); 183 setFirstBootMACOnInterface(intfname, macStr); 184 first_boot_status.push_back(intfname); 185 bool status = true; 186 for (const auto& keys : configJson.items()) 187 { 188 if (!(std::find(first_boot_status.begin(), 189 first_boot_status.end(), keys.key()) != 190 first_boot_status.end())) 191 { 192 lg2::info("Interface {NET_INTF} MAC is NOT set from VPD", 193 "NET_INTF", keys.key()); 194 status = false; 195 } 196 } 197 if (status) 198 { 199 lg2::info("Removing the match for ethernet interfaces"); 200 EthInterfaceMatch = nullptr; 201 } 202 } 203 else 204 { 205 lg2::info("Nothing is present in Inventory"); 206 return false; 207 } 208 } 209 catch (const std::exception& e) 210 { 211 lg2::error("Exception occurred during getting of MAC " 212 "address from Inventory"); 213 return false; 214 } 215 return true; 216 } 217 218 // register the matches to be monitored from inventory manager 219 void registerSignals(sdbusplus::bus_t& bus) 220 { 221 lg2::info("Registering the Inventory Signals Matcher"); 222 223 auto callback = [&](sdbusplus::message_t& m) { 224 std::map<DbusObjectPath, 225 std::map<DbusInterface, std::variant<PropertyValue>>> 226 interfacesProperties; 227 228 sdbusplus::message::object_path objPath; 229 m.read(objPath, interfacesProperties); 230 231 for (const auto& pattern : configJson.items()) 232 { 233 if (objPath.str.ends_with("/" + pattern.value().get<std::string>())) 234 { 235 for (auto& interface : interfacesProperties) 236 { 237 if (interface.first == invNetworkIntf) 238 { 239 for (const auto& property : interface.second) 240 { 241 if (property.first == "MACAddress") 242 { 243 // Only set mac address on interface once the 244 // firstboot file does not exist or it is being 245 // FORCE_SYNC_MAC_FROM_INVENTORY 246 if (FORCE_SYNC_MAC_FROM_INVENTORY || 247 !std::filesystem::exists( 248 firstBootPath + pattern.key())) 249 { 250 setFirstBootMACOnInterface( 251 pattern.key(), 252 std::get<std::string>(property.second)); 253 } 254 break; 255 } 256 } 257 break; 258 } 259 } 260 } 261 } 262 }; 263 264 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>( 265 bus, 266 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 267 "member='InterfacesAdded',path='/xyz/openbmc_project/" 268 "inventory'", 269 callback); 270 } 271 272 void watchEthernetInterface(sdbusplus::bus_t& bus) 273 { 274 auto handle_interface = [&](auto infname) { 275 if (configJson.find(infname) == configJson.end()) 276 { 277 // ethernet interface not found in configJSON 278 // check if it is not sit0 interface, as it is 279 // expected. 280 if (infname != "sit0") 281 { 282 lg2::error("Wrong Interface Name in Config Json"); 283 } 284 } 285 else 286 { 287 registerSignals(bus); 288 289 if (setInventoryMACOnSystem(bus, infname)) 290 { 291 MacAddressMatch = nullptr; 292 } 293 } 294 }; 295 296 auto mycallback = [&, handle_interface](sdbusplus::message_t& m) { 297 std::map<DbusObjectPath, 298 std::map<DbusInterface, std::variant<PropertyValue>>> 299 interfacesProperties; 300 301 sdbusplus::message::object_path objPath; 302 std::pair<std::string, std::string> ethPair; 303 m.read(objPath, interfacesProperties); 304 305 for (const auto& interfaces : interfacesProperties) 306 { 307 lg2::info("Check {DBUS_INTF} for sdbus response", "DBUS_INTF", 308 interfaces.first); 309 if (interfaces.first == 310 "xyz.openbmc_project.Network.EthernetInterface") 311 { 312 for (const auto& property : interfaces.second) 313 { 314 if (property.first == "InterfaceName") 315 { 316 handle_interface( 317 std::get<std::string>(property.second)); 318 319 break; 320 } 321 } 322 break; 323 } 324 } 325 }; 326 327 // The VPD may already have been assigned because phosphor-inventory-manager 328 // started ahead of the network service. Read the VPD directly and assign 329 // the MAC address despite this possibility. 330 331 for (const auto& interfaceString : configJson.items()) 332 { 333 if (FORCE_SYNC_MAC_FROM_INVENTORY || 334 !std::filesystem::exists(firstBootPath + interfaceString.key())) 335 { 336 lg2::info("Check VPD for MAC: {REASON}", "REASON", 337 (FORCE_SYNC_MAC_FROM_INVENTORY) 338 ? "Force sync enabled" 339 : "First boot file is not present"); 340 EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>( 341 bus, 342 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 343 "member='InterfacesAdded',path='/xyz/openbmc_project/network'", 344 mycallback); 345 346 for (const auto& intf : manager->interfaces) 347 { 348 if (intf.first == interfaceString.key()) 349 { 350 handle_interface(intf.first); 351 } 352 } 353 } 354 } 355 } 356 357 std::unique_ptr<Runtime> watch(stdplus::PinnedRef<sdbusplus::bus_t> bus, 358 stdplus::PinnedRef<Manager> m) 359 { 360 manager = &m.get(); 361 std::ifstream in(configFile); 362 in >> configJson; 363 watchEthernetInterface(bus); 364 return nullptr; 365 } 366 367 } // namespace phosphor::network::inventory 368