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