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