1 #include <arpa/inet.h> 2 #include <dirent.h> 3 #include <net/if.h> 4 5 #include <algorithm> 6 #include <chrono> 7 #include <ipmid/utils.hpp> 8 #include <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <sdbusplus/message/types.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 namespace ipmi 14 { 15 16 using namespace phosphor::logging; 17 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 18 19 namespace network 20 { 21 22 /** @brief checks if the given ip is Link Local Ip or not. 23 * @param[in] ipaddress - IPAddress. 24 */ 25 bool isLinkLocalIP(const std::string& ipaddress); 26 27 } // namespace network 28 29 // TODO There may be cases where an interface is implemented by multiple 30 // objects,to handle such cases we are interested on that object 31 // which are on interested busname. 32 // Currently mapper doesn't give the readable busname(gives busid) so we can't 33 // use busname to find the object,will do later once the support is there. 34 35 DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus, 36 const std::string& interface, 37 const std::string& serviceRoot, 38 const std::string& match) 39 { 40 std::vector<DbusInterface> interfaces; 41 interfaces.emplace_back(interface); 42 43 auto depth = 0; 44 45 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 46 MAPPER_INTF, "GetSubTree"); 47 48 mapperCall.append(serviceRoot, depth, interfaces); 49 50 auto mapperReply = bus.call(mapperCall); 51 if (mapperReply.is_method_error()) 52 { 53 log<level::ERR>("Error in mapper call"); 54 elog<InternalFailure>(); 55 } 56 57 ObjectTree objectTree; 58 mapperReply.read(objectTree); 59 60 if (objectTree.empty()) 61 { 62 log<level::ERR>("No Object has implemented the interface", 63 entry("INTERFACE=%s", interface.c_str())); 64 elog<InternalFailure>(); 65 } 66 67 DbusObjectInfo objectInfo; 68 69 // if match is empty then return the first object 70 if (match == "") 71 { 72 objectInfo = std::make_pair( 73 objectTree.begin()->first, 74 std::move(objectTree.begin()->second.begin()->first)); 75 return objectInfo; 76 } 77 78 // else search the match string in the object path 79 auto found = std::find_if( 80 objectTree.begin(), objectTree.end(), [&match](const auto& object) { 81 return (object.first.find(match) != std::string::npos); 82 }); 83 84 if (found == objectTree.end()) 85 { 86 log<level::ERR>("Failed to find object which matches", 87 entry("MATCH=%s", match.c_str())); 88 elog<InternalFailure>(); 89 // elog<> throws an exception. 90 } 91 92 return make_pair(found->first, std::move(found->second.begin()->first)); 93 } 94 95 DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus, 96 const std::string& interface, 97 const std::string& serviceRoot, 98 const std::string& match) 99 { 100 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); 101 102 if (objectTree.empty()) 103 { 104 log<level::ERR>("No Object has implemented the IP interface", 105 entry("INTERFACE=%s", interface.c_str())); 106 elog<InternalFailure>(); 107 } 108 109 DbusObjectInfo objectInfo; 110 111 for (auto& object : objectTree) 112 { 113 auto variant = ipmi::getDbusProperty( 114 bus, object.second.begin()->first, object.first, 115 ipmi::network::IP_INTERFACE, "Address"); 116 117 objectInfo = std::make_pair(object.first, object.second.begin()->first); 118 119 // if LinkLocalIP found look for Non-LinkLocalIP 120 if (ipmi::network::isLinkLocalIP(std::get<std::string>(variant))) 121 { 122 continue; 123 } 124 else 125 { 126 break; 127 } 128 } 129 return objectInfo; 130 } 131 132 Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service, 133 const std::string& objPath, const std::string& interface, 134 const std::string& property, 135 std::chrono::microseconds timeout) 136 { 137 138 Value value; 139 140 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 141 PROP_INTF, METHOD_GET); 142 143 method.append(interface, property); 144 145 auto reply = bus.call(method, timeout.count()); 146 147 if (reply.is_method_error()) 148 { 149 log<level::ERR>("Failed to get property", 150 entry("PROPERTY=%s", property.c_str()), 151 entry("PATH=%s", objPath.c_str()), 152 entry("INTERFACE=%s", interface.c_str())); 153 elog<InternalFailure>(); 154 } 155 156 reply.read(value); 157 158 return value; 159 } 160 161 PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus, 162 const std::string& service, 163 const std::string& objPath, 164 const std::string& interface, 165 std::chrono::microseconds timeout) 166 { 167 PropertyMap properties; 168 169 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 170 PROP_INTF, METHOD_GET_ALL); 171 172 method.append(interface); 173 174 auto reply = bus.call(method, timeout.count()); 175 176 if (reply.is_method_error()) 177 { 178 log<level::ERR>("Failed to get all properties", 179 entry("PATH=%s", objPath.c_str()), 180 entry("INTERFACE=%s", interface.c_str())); 181 elog<InternalFailure>(); 182 } 183 184 reply.read(properties); 185 return properties; 186 } 187 188 ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus, 189 const std::string& service, 190 const std::string& objPath) 191 { 192 ipmi::ObjectValueTree interfaces; 193 194 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 195 "org.freedesktop.DBus.ObjectManager", 196 "GetManagedObjects"); 197 198 auto reply = bus.call(method); 199 200 if (reply.is_method_error()) 201 { 202 log<level::ERR>("Failed to get managed objects", 203 entry("PATH=%s", objPath.c_str())); 204 elog<InternalFailure>(); 205 } 206 207 reply.read(interfaces); 208 return interfaces; 209 } 210 211 void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service, 212 const std::string& objPath, const std::string& interface, 213 const std::string& property, const Value& value, 214 std::chrono::microseconds timeout) 215 { 216 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 217 PROP_INTF, METHOD_SET); 218 219 method.append(interface, property, value); 220 221 if (!bus.call(method, timeout.count())) 222 { 223 log<level::ERR>("Failed to set property", 224 entry("PROPERTY=%s", property.c_str()), 225 entry("PATH=%s", objPath.c_str()), 226 entry("INTERFACE=%s", interface.c_str())); 227 elog<InternalFailure>(); 228 } 229 } 230 231 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) : 232 intf(intf), path(path), cachedService(std::nullopt), 233 cachedBusName(std::nullopt) 234 { 235 } 236 237 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) : 238 intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt), 239 cachedBusName(std::nullopt) 240 { 241 } 242 243 const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus) 244 { 245 if (!isValid(bus)) 246 { 247 cachedBusName = bus.get_unique_name(); 248 cachedService = ::ipmi::getService(bus, intf, path); 249 } 250 return cachedService.value(); 251 } 252 253 void ServiceCache::invalidate() 254 { 255 cachedBusName = std::nullopt; 256 cachedService = std::nullopt; 257 } 258 259 sdbusplus::message::message 260 ServiceCache::newMethodCall(sdbusplus::bus::bus& bus, const char* intf, 261 const char* method) 262 { 263 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf, 264 method); 265 } 266 267 bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const 268 { 269 return cachedService && cachedBusName == bus.get_unique_name(); 270 } 271 272 std::string getService(sdbusplus::bus::bus& bus, const std::string& intf, 273 const std::string& path) 274 { 275 auto mapperCall = 276 bus.new_method_call("xyz.openbmc_project.ObjectMapper", 277 "/xyz/openbmc_project/object_mapper", 278 "xyz.openbmc_project.ObjectMapper", "GetObject"); 279 280 mapperCall.append(path); 281 mapperCall.append(std::vector<std::string>({intf})); 282 283 auto mapperResponseMsg = bus.call(mapperCall); 284 285 if (mapperResponseMsg.is_method_error()) 286 { 287 throw std::runtime_error("ERROR in mapper call"); 288 } 289 290 std::map<std::string, std::vector<std::string>> mapperResponse; 291 mapperResponseMsg.read(mapperResponse); 292 293 if (mapperResponse.begin() == mapperResponse.end()) 294 { 295 throw std::runtime_error("ERROR in reading the mapper response"); 296 } 297 298 return mapperResponse.begin()->first; 299 } 300 301 ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus& bus, 302 const std::string& serviceRoot, 303 const std::string& interface, 304 const std::string& match) 305 { 306 std::vector<std::string> interfaces; 307 interfaces.emplace_back(interface); 308 309 auto depth = 0; 310 311 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 312 MAPPER_INTF, "GetSubTree"); 313 314 mapperCall.append(serviceRoot, depth, interfaces); 315 316 auto mapperReply = bus.call(mapperCall); 317 if (mapperReply.is_method_error()) 318 { 319 log<level::ERR>("Error in mapper call", 320 entry("SERVICEROOT=%s", serviceRoot.c_str()), 321 entry("INTERFACE=%s", interface.c_str())); 322 323 elog<InternalFailure>(); 324 } 325 326 ObjectTree objectTree; 327 mapperReply.read(objectTree); 328 329 for (auto it = objectTree.begin(); it != objectTree.end();) 330 { 331 if (it->first.find(match) == std::string::npos) 332 { 333 it = objectTree.erase(it); 334 } 335 else 336 { 337 ++it; 338 } 339 } 340 341 return objectTree; 342 } 343 344 void deleteAllDbusObjects(sdbusplus::bus::bus& bus, 345 const std::string& serviceRoot, 346 const std::string& interface, 347 const std::string& match) 348 { 349 try 350 { 351 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); 352 353 for (auto& object : objectTree) 354 { 355 method_no_args::callDbusMethod(bus, object.second.begin()->first, 356 object.first, DELETE_INTERFACE, 357 "Delete"); 358 } 359 } 360 catch (sdbusplus::exception::exception& e) 361 { 362 log<level::INFO>("sdbusplus exception - Unable to delete the objects", 363 entry("ERROR=%s", e.what()), 364 entry("INTERFACE=%s", interface.c_str()), 365 entry("SERVICE=%s", serviceRoot.c_str())); 366 } 367 } 368 369 ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path, 370 InterfaceList&& interfaces) 371 { 372 auto convertToString = [](InterfaceList& interfaces) -> std::string { 373 std::string intfStr; 374 for (const auto& intf : interfaces) 375 { 376 intfStr += "," + intf; 377 } 378 return intfStr; 379 }; 380 381 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 382 MAPPER_INTF, "GetAncestors"); 383 mapperCall.append(path, interfaces); 384 385 auto mapperReply = bus.call(mapperCall); 386 if (mapperReply.is_method_error()) 387 { 388 log<level::ERR>( 389 "Error in mapper call", entry("PATH=%s", path.c_str()), 390 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 391 392 elog<InternalFailure>(); 393 } 394 395 ObjectTree objectTree; 396 mapperReply.read(objectTree); 397 398 if (objectTree.empty()) 399 { 400 log<level::ERR>( 401 "No Object has implemented the interface", 402 entry("PATH=%s", path.c_str()), 403 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 404 elog<InternalFailure>(); 405 } 406 407 return objectTree; 408 } 409 410 namespace method_no_args 411 { 412 413 void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service, 414 const std::string& objPath, const std::string& interface, 415 const std::string& method) 416 417 { 418 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 419 interface.c_str(), method.c_str()); 420 421 auto reply = bus.call(busMethod); 422 423 if (reply.is_method_error()) 424 { 425 log<level::ERR>("Failed to execute method", 426 entry("METHOD=%s", method.c_str()), 427 entry("PATH=%s", objPath.c_str()), 428 entry("INTERFACE=%s", interface.c_str())); 429 elog<InternalFailure>(); 430 } 431 } 432 433 } // namespace method_no_args 434 435 namespace network 436 { 437 438 bool isLinkLocalIP(const std::string& address) 439 { 440 return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0; 441 } 442 443 void createIP(sdbusplus::bus::bus& bus, const std::string& service, 444 const std::string& objPath, const std::string& protocolType, 445 const std::string& ipaddress, uint8_t prefix) 446 { 447 std::string gateway = ""; 448 449 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 450 IP_CREATE_INTERFACE, "IP"); 451 452 busMethod.append(protocolType, ipaddress, prefix, gateway); 453 454 auto reply = bus.call(busMethod); 455 456 if (reply.is_method_error()) 457 { 458 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"), 459 entry("PATH=%s", objPath.c_str())); 460 elog<InternalFailure>(); 461 } 462 } 463 464 void createVLAN(sdbusplus::bus::bus& bus, const std::string& service, 465 const std::string& objPath, const std::string& interfaceName, 466 uint32_t vlanID) 467 { 468 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 469 VLAN_CREATE_INTERFACE, "VLAN"); 470 471 busMethod.append(interfaceName, vlanID); 472 473 auto reply = bus.call(busMethod); 474 475 if (reply.is_method_error()) 476 { 477 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"), 478 entry("PATH=%s", objPath.c_str())); 479 elog<InternalFailure>(); 480 } 481 } 482 483 uint8_t toPrefix(int addressFamily, const std::string& subnetMask) 484 { 485 if (addressFamily == AF_INET6) 486 { 487 return 0; 488 } 489 490 uint32_t buff{}; 491 492 auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff); 493 if (rc <= 0) 494 { 495 log<level::ERR>("inet_pton failed:", 496 entry("SUBNETMASK=%s", subnetMask.c_str())); 497 return 0; 498 } 499 500 buff = be32toh(buff); 501 // total no of bits - total no of leading zero == total no of ones 502 if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) == 503 __builtin_popcount(buff)) 504 { 505 return __builtin_popcount(buff); 506 } 507 else 508 { 509 log<level::ERR>("Invalid Mask", 510 entry("SUBNETMASK=%s", subnetMask.c_str())); 511 return 0; 512 } 513 } 514 515 uint32_t getVLAN(const std::string& path) 516 { 517 // Path would be look like 518 // /xyz/openbmc_project/network/eth0_443/ipv4 519 520 uint32_t vlanID = 0; 521 try 522 { 523 auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1); 524 525 auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1); 526 527 auto index = intfName.find("_"); 528 if (index != std::string::npos) 529 { 530 auto str = intfName.substr(index + 1); 531 vlanID = std::stoul(str); 532 } 533 } 534 catch (std::exception& e) 535 { 536 log<level::ERR>("Exception occurred during getVLAN", 537 entry("PATH=%s", path.c_str()), 538 entry("EXCEPTION=%s", e.what())); 539 } 540 return vlanID; 541 } 542 543 } // namespace network 544 } // namespace ipmi 545