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