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 (InternalFailure& e) 362 { 363 log<level::INFO>("Unable to delete the objects having", 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