1 #include "transporthandler.hpp" 2 3 #include <stdplus/net/addr/subnet.hpp> 4 #include <stdplus/raw.hpp> 5 6 #include <array> 7 #include <fstream> 8 9 using phosphor::logging::commit; 10 using phosphor::logging::elog; 11 using phosphor::logging::entry; 12 using phosphor::logging::level; 13 using phosphor::logging::log; 14 using sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 15 using sdbusplus::error::xyz::openbmc_project::common::InvalidArgument; 16 using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface; 17 using sdbusplus::server::xyz::openbmc_project::network::IP; 18 using sdbusplus::server::xyz::openbmc_project::network::Neighbor; 19 20 namespace cipher 21 { 22 23 std::vector<uint8_t> getCipherList() 24 { 25 std::vector<uint8_t> cipherList; 26 27 std::ifstream jsonFile(cipher::configFile); 28 if (!jsonFile.is_open()) 29 { 30 log<level::ERR>("Channel Cipher suites file not found"); 31 elog<InternalFailure>(); 32 } 33 34 auto data = Json::parse(jsonFile, nullptr, false); 35 if (data.is_discarded()) 36 { 37 log<level::ERR>("Parsing channel cipher suites JSON failed"); 38 elog<InternalFailure>(); 39 } 40 41 // Byte 1 is reserved 42 cipherList.push_back(0x00); 43 44 for (const auto& record : data) 45 { 46 cipherList.push_back(record.value(cipher, 0)); 47 } 48 49 return cipherList; 50 } 51 } // namespace cipher 52 53 namespace ipmi 54 { 55 namespace transport 56 { 57 58 /** @brief Valid address origins for IPv4 */ 59 const std::unordered_set<IP::AddressOrigin> originsV4 = { 60 IP::AddressOrigin::Static, 61 IP::AddressOrigin::DHCP, 62 }; 63 64 static constexpr uint8_t oemCmdStart = 192; 65 66 // Checks if the ifname is part of the networkd path 67 // This assumes the path came from the network subtree PATH_ROOT 68 bool ifnameInPath(std::string_view ifname, std::string_view path) 69 { 70 constexpr auto rs = PATH_ROOT.size() + 1; // ROOT + separator 71 const auto is = rs + ifname.size(); // ROOT + sep + ifname 72 return path.size() > rs && path.substr(rs).starts_with(ifname) && 73 (path.size() == is || path[is] == '/'); 74 } 75 76 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus_t& bus, 77 uint8_t channel) 78 { 79 auto ifname = getChannelName(channel); 80 if (ifname.empty()) 81 { 82 return std::nullopt; 83 } 84 85 // Enumerate all VLAN + ETHERNET interfaces 86 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 87 "GetSubTree"); 88 req.append(std::string_view(PATH_ROOT), 0, 89 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET}); 90 auto reply = bus.call(req); 91 ObjectTree objs; 92 reply.read(objs); 93 94 ChannelParams params; 95 for (const auto& [path, impls] : objs) 96 { 97 if (!ifnameInPath(ifname, path)) 98 { 99 continue; 100 } 101 for (const auto& [service, intfs] : impls) 102 { 103 bool vlan = false; 104 bool ethernet = false; 105 for (const auto& intf : intfs) 106 { 107 if (intf == INTF_VLAN) 108 { 109 vlan = true; 110 } 111 else if (intf == INTF_ETHERNET) 112 { 113 ethernet = true; 114 } 115 } 116 if (params.service.empty() && (vlan || ethernet)) 117 { 118 params.service = service; 119 } 120 if (params.ifPath.empty() && !vlan && ethernet) 121 { 122 params.ifPath = path; 123 } 124 if (params.logicalPath.empty() && vlan) 125 { 126 params.logicalPath = path; 127 } 128 } 129 } 130 131 // We must have a path for the underlying interface 132 if (params.ifPath.empty()) 133 { 134 return std::nullopt; 135 } 136 // We don't have a VLAN so the logical path is the same 137 if (params.logicalPath.empty()) 138 { 139 params.logicalPath = params.ifPath; 140 } 141 142 params.id = channel; 143 params.ifname = std::move(ifname); 144 return params; 145 } 146 147 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel) 148 { 149 auto params = maybeGetChannelParams(bus, channel); 150 if (!params) 151 { 152 log<level::ERR>("Failed to get channel params", 153 entry("CHANNEL=%" PRIu8, channel)); 154 elog<InternalFailure>(); 155 } 156 return std::move(*params); 157 } 158 159 /** @brief Wraps the phosphor logging method to insert some additional metadata 160 * 161 * @param[in] params - The parameters for the channel 162 * ... 163 */ 164 template <auto level, typename... Args> 165 auto logWithChannel(const ChannelParams& params, Args&&... args) 166 { 167 return log<level>(std::forward<Args>(args)..., 168 entry("CHANNEL=%d", params.id), 169 entry("IFNAME=%s", params.ifname.c_str())); 170 } 171 template <auto level, typename... Args> 172 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args) 173 { 174 if (params) 175 { 176 return logWithChannel<level>(*params, std::forward<Args>(args)...); 177 } 178 return log<level>(std::forward<Args>(args)...); 179 } 180 181 /** @brief Get / Set the Property value from phosphor-networkd EthernetInterface 182 */ 183 template <typename T> 184 static T getEthProp(sdbusplus::bus_t& bus, const ChannelParams& params, 185 const std::string& prop) 186 { 187 return std::get<T>(getDbusProperty(bus, params.service, params.logicalPath, 188 INTF_ETHERNET, prop)); 189 } 190 template <typename T> 191 static void setEthProp(sdbusplus::bus_t& bus, const ChannelParams& params, 192 const std::string& prop, const T& t) 193 { 194 return setDbusProperty(bus, params.service, params.logicalPath, 195 INTF_ETHERNET, prop, t); 196 } 197 198 /** @brief Determines the MAC of the ethernet interface 199 * 200 * @param[in] bus - The bus object used for lookups 201 * @param[in] params - The parameters for the channel 202 * @return The configured mac address 203 */ 204 stdplus::EtherAddr getMACProperty(sdbusplus::bus_t& bus, 205 const ChannelParams& params) 206 { 207 auto prop = getDbusProperty(bus, params.service, params.ifPath, INTF_MAC, 208 "MACAddress"); 209 return stdplus::fromStr<stdplus::EtherAddr>(std::get<std::string>(prop)); 210 } 211 212 /** @brief Sets the system value for MAC address on the given interface 213 * 214 * @param[in] bus - The bus object used for lookups 215 * @param[in] params - The parameters for the channel 216 * @param[in] mac - MAC address to apply 217 */ 218 void setMACProperty(sdbusplus::bus_t& bus, const ChannelParams& params, 219 stdplus::EtherAddr mac) 220 { 221 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", 222 stdplus::toStr(mac)); 223 } 224 225 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, 226 const std::string& path) 227 { 228 if (path.empty()) 229 { 230 return; 231 } 232 try 233 { 234 auto req = bus.new_method_call(service.c_str(), path.c_str(), 235 ipmi::DELETE_INTERFACE, "Delete"); 236 bus.call_noreply(req); 237 } 238 catch (const sdbusplus::exception_t& e) 239 { 240 if (strcmp(e.name(), 241 "xyz.openbmc_project.Common.Error.InternalFailure") != 0 && 242 strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0) 243 { 244 // We want to rethrow real errors 245 throw; 246 } 247 } 248 } 249 250 /** @brief Sets the address info configured for the interface 251 * If a previous address path exists then it will be removed 252 * before the new address is added. 253 * 254 * @param[in] bus - The bus object used for lookups 255 * @param[in] params - The parameters for the channel 256 * @param[in] address - The address of the new IP 257 * @param[in] prefix - The prefix of the new IP 258 */ 259 template <int family> 260 void createIfAddr(sdbusplus::bus_t& bus, const ChannelParams& params, 261 typename AddrFamily<family>::addr address, uint8_t prefix) 262 { 263 auto newreq = bus.new_method_call(params.service.c_str(), 264 params.logicalPath.c_str(), 265 INTF_IP_CREATE, "IP"); 266 std::string protocol = 267 sdbusplus::common::xyz::openbmc_project::network::convertForMessage( 268 AddrFamily<family>::protocol); 269 stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> tsh; 270 newreq.append(protocol, tsh(address), prefix, ""); 271 bus.call_noreply(newreq); 272 } 273 274 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs() 275 * 276 * @param[in] bus - The bus object used for lookups 277 * @param[in] params - The parameters for the channel 278 * @return The address and prefix if found 279 */ 280 auto getIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params) 281 { 282 return getIfAddr<AF_INET>(bus, params, 0, originsV4); 283 } 284 285 /** @brief Reconfigures the IPv4 address info configured for the interface 286 * 287 * @param[in] bus - The bus object used for lookups 288 * @param[in] params - The parameters for the channel 289 * @param[in] address - The new address if specified 290 * @param[in] prefix - The new address prefix if specified 291 */ 292 void reconfigureIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params, 293 std::optional<stdplus::In4Addr> address, 294 std::optional<uint8_t> prefix) 295 { 296 auto ifaddr = getIfAddr4(bus, params); 297 if (!ifaddr && !address) 298 { 299 log<level::ERR>("Missing address for IPv4 assignment"); 300 elog<InternalFailure>(); 301 } 302 uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix; 303 if (ifaddr) 304 { 305 fallbackPrefix = ifaddr->prefix; 306 deleteObjectIfExists(bus, params.service, ifaddr->path); 307 } 308 auto addr = address.value_or(ifaddr->address); 309 if (addr != stdplus::In4Addr{}) 310 { 311 createIfAddr<AF_INET>(bus, params, addr, 312 prefix.value_or(fallbackPrefix)); 313 } 314 } 315 316 template <int family> 317 std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus_t& bus, 318 const ChannelParams& params, 319 ObjectLookupCache& neighbors) 320 { 321 auto gateway = getGatewayProperty<family>(bus, params); 322 if (!gateway) 323 { 324 return std::nullopt; 325 } 326 327 return findStaticNeighbor<family>(bus, params, *gateway, neighbors); 328 } 329 330 template <int family> 331 std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus_t& bus, 332 const ChannelParams& params) 333 { 334 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 335 return findGatewayNeighbor<family>(bus, params, neighbors); 336 } 337 338 template <int family> 339 void reconfigureGatewayMAC(sdbusplus::bus_t& bus, const ChannelParams& params, 340 stdplus::EtherAddr mac) 341 { 342 auto gateway = getGatewayProperty<family>(bus, params); 343 if (!gateway) 344 { 345 log<level::ERR>("Tried to set Gateway MAC without Gateway"); 346 elog<InternalFailure>(); 347 } 348 349 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 350 auto neighbor = findStaticNeighbor<family>(bus, params, *gateway, 351 neighbors); 352 if (neighbor) 353 { 354 deleteObjectIfExists(bus, params.service, neighbor->path); 355 } 356 357 createNeighbor<family>(bus, params, *gateway, mac); 358 } 359 360 /** @brief Deconfigures the IPv6 address info configured for the interface 361 * 362 * @param[in] bus - The bus object used for lookups 363 * @param[in] params - The parameters for the channel 364 * @param[in] idx - The address index to operate on 365 */ 366 void deconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 367 uint8_t idx) 368 { 369 auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static); 370 if (ifaddr) 371 { 372 deleteObjectIfExists(bus, params.service, ifaddr->path); 373 } 374 } 375 376 /** @brief Reconfigures the IPv6 address info configured for the interface 377 * 378 * @param[in] bus - The bus object used for lookups 379 * @param[in] params - The parameters for the channel 380 * @param[in] idx - The address index to operate on 381 * @param[in] address - The new address 382 * @param[in] prefix - The new address prefix 383 */ 384 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 385 uint8_t idx, stdplus::In6Addr address, uint8_t prefix) 386 { 387 deconfigureIfAddr6(bus, params, idx); 388 createIfAddr<AF_INET6>(bus, params, address, prefix); 389 } 390 391 /** @brief Converts the AddressOrigin into an IPv6Source 392 * 393 * @param[in] origin - The DBus Address Origin to convert 394 * @return The IPv6Source version of the origin 395 */ 396 IPv6Source originToSourceType(IP::AddressOrigin origin) 397 { 398 switch (origin) 399 { 400 case IP::AddressOrigin::Static: 401 return IPv6Source::Static; 402 case IP::AddressOrigin::DHCP: 403 return IPv6Source::DHCP; 404 case IP::AddressOrigin::SLAAC: 405 return IPv6Source::SLAAC; 406 default: 407 { 408 auto originStr = sdbusplus::common::xyz::openbmc_project::network:: 409 convertForMessage(origin); 410 log<level::ERR>( 411 "Invalid IP::AddressOrigin conversion to IPv6Source", 412 entry("ORIGIN=%s", originStr.c_str())); 413 elog<InternalFailure>(); 414 } 415 } 416 } 417 418 /** @brief Packs the IPMI message response with IPv6 address data 419 * 420 * @param[out] ret - The IPMI response payload to be packed 421 * @param[in] channel - The channel id corresponding to an ethernet interface 422 * @param[in] set - The set selector for determining address index 423 * @param[in] origins - Set of valid origins for address filtering 424 */ 425 void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set, 426 const std::unordered_set<IP::AddressOrigin>& origins) 427 { 428 auto source = IPv6Source::Static; 429 bool enabled = false; 430 stdplus::In6Addr addr{}; 431 uint8_t prefix{}; 432 auto status = IPv6AddressStatus::Disabled; 433 434 auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins); 435 if (ifaddr) 436 { 437 source = originToSourceType(ifaddr->origin); 438 enabled = (origins == originsV6Static); 439 addr = ifaddr->address; 440 prefix = ifaddr->prefix; 441 status = IPv6AddressStatus::Active; 442 } 443 444 ret.pack(set); 445 ret.pack(types::enum_cast<uint4_t>(source), uint3_t{}, enabled); 446 ret.pack(stdplus::raw::asView<char>(addr)); 447 ret.pack(prefix); 448 ret.pack(types::enum_cast<uint8_t>(status)); 449 } 450 451 /** @brief Gets the vlan ID configured on the interface 452 * 453 * @param[in] bus - The bus object used for lookups 454 * @param[in] params - The parameters for the channel 455 * @return VLAN id or the standard 0 for no VLAN 456 */ 457 uint16_t getVLANProperty(sdbusplus::bus_t& bus, const ChannelParams& params) 458 { 459 // VLAN devices will always have a separate logical object 460 if (params.ifPath == params.logicalPath) 461 { 462 return 0; 463 } 464 465 auto vlan = std::get<uint32_t>(getDbusProperty( 466 bus, params.service, params.logicalPath, INTF_VLAN, "Id")); 467 if ((vlan & VLAN_VALUE_MASK) != vlan) 468 { 469 logWithChannel<level::ERR>(params, "networkd returned an invalid vlan", 470 entry("VLAN=%" PRIu32, vlan)); 471 elog<InternalFailure>(); 472 } 473 return vlan; 474 } 475 476 /** @brief Deletes all of the possible configuration parameters for a channel 477 * 478 * @param[in] bus - The bus object used for lookups 479 * @param[in] params - The parameters for the channel 480 */ 481 void deconfigureChannel(sdbusplus::bus_t& bus, ChannelParams& params) 482 { 483 // Delete all objects associated with the interface 484 auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 485 "GetSubTree"); 486 objreq.append(std::string_view(PATH_ROOT), 0, 487 std::vector<std::string>{DELETE_INTERFACE}); 488 auto objreply = bus.call(objreq); 489 ObjectTree objs; 490 objreply.read(objs); 491 for (const auto& [path, impls] : objs) 492 { 493 if (!ifnameInPath(params.ifname, path)) 494 { 495 continue; 496 } 497 for (const auto& [service, intfs] : impls) 498 { 499 deleteObjectIfExists(bus, service, path); 500 } 501 // Update params to reflect the deletion of vlan 502 if (path == params.logicalPath) 503 { 504 params.logicalPath = params.ifPath; 505 } 506 } 507 508 // Clear out any settings on the lower physical interface 509 setEthProp(bus, params, "DHCP4", false); 510 setEthProp(bus, params, "DHCP6", false); 511 setEthProp(bus, params, "IPv6AcceptRA", false); 512 } 513 514 /** @brief Creates a new VLAN on the specified interface 515 * 516 * @param[in] bus - The bus object used for lookups 517 * @param[in] params - The parameters for the channel 518 * @param[in] vlan - The id of the new vlan 519 */ 520 void createVLAN(sdbusplus::bus_t& bus, ChannelParams& params, uint16_t vlan) 521 { 522 if (vlan == 0) 523 { 524 return; 525 } 526 527 auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT.c_str(), 528 INTF_VLAN_CREATE, "VLAN"); 529 req.append(params.ifname, static_cast<uint32_t>(vlan)); 530 auto reply = bus.call(req); 531 sdbusplus::message::object_path newPath; 532 reply.read(newPath); 533 params.logicalPath = std::move(newPath); 534 } 535 536 /** @brief Performs the necessary reconfiguration to change the VLAN 537 * 538 * @param[in] bus - The bus object used for lookups 539 * @param[in] params - The parameters for the channel 540 * @param[in] vlan - The new vlan id to use 541 */ 542 void reconfigureVLAN(sdbusplus::bus_t& bus, ChannelParams& params, 543 uint16_t vlan) 544 { 545 // Unfortunatetly we don't have built-in functions to migrate our interface 546 // customizations to new VLAN interfaces, or have some kind of decoupling. 547 // We therefore must retain all of our old information, setup the new VLAN 548 // configuration, then restore the old info. 549 550 // Save info from the old logical interface 551 bool dhcp4 = getEthProp<bool>(bus, params, "DHCP4"); 552 bool dhcp6 = getEthProp<bool>(bus, params, "DHCP6"); 553 bool ra = getEthProp<bool>(bus, params, "IPv6AcceptRA"); 554 ObjectLookupCache ips(bus, params, INTF_IP); 555 auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips); 556 std::vector<IfAddr<AF_INET6>> ifaddrs6; 557 for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i) 558 { 559 auto ifaddr6 = findIfAddr<AF_INET6>(bus, params, i, originsV6Static, 560 ips); 561 if (!ifaddr6) 562 { 563 break; 564 } 565 ifaddrs6.push_back(std::move(*ifaddr6)); 566 } 567 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 568 auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors); 569 auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors); 570 571 deconfigureChannel(bus, params); 572 createVLAN(bus, params, vlan); 573 574 // Re-establish the saved settings 575 setEthProp(bus, params, "DHCP4", dhcp4); 576 setEthProp(bus, params, "DHCP6", dhcp6); 577 setEthProp(bus, params, "IPv6AcceptRA", ra); 578 if (ifaddr4) 579 { 580 createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix); 581 } 582 for (const auto& ifaddr6 : ifaddrs6) 583 { 584 createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix); 585 } 586 if (neighbor4) 587 { 588 createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac); 589 } 590 if (neighbor6) 591 { 592 createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac); 593 } 594 } 595 596 // We need to store this value so it can be returned to the client 597 // It is volatile so safe to store in daemon memory. 598 static std::unordered_map<uint8_t, SetStatus> setStatus; 599 600 // Until we have good support for fixed versions of IPMI tool 601 // we need to return the VLAN id for disabled VLANs. The value is only 602 // used for verification that a disable operation succeeded and will only 603 // be sent if our system indicates that vlans are disabled. 604 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan; 605 606 /** @brief Gets the set status for the channel if it exists 607 * Otherise populates and returns the default value. 608 * 609 * @param[in] channel - The channel id corresponding to an ethernet interface 610 * @return A reference to the SetStatus for the channel 611 */ 612 SetStatus& getSetStatus(uint8_t channel) 613 { 614 auto it = setStatus.find(channel); 615 if (it != setStatus.end()) 616 { 617 return it->second; 618 } 619 return setStatus[channel] = SetStatus::Complete; 620 } 621 622 /** @brief Unpacks the trivially copyable type from the message */ 623 template <typename T> 624 static T unpackT(message::Payload& req) 625 { 626 std::array<uint8_t, sizeof(T)> bytes; 627 if (req.unpack(bytes) != 0) 628 { 629 throw ccReqDataLenInvalid; 630 } 631 return stdplus::raw::copyFrom<T>(bytes); 632 } 633 634 /** @brief Ensure the message is fully unpacked */ 635 static void unpackFinal(message::Payload& req) 636 { 637 if (!req.fullyUnpacked()) 638 { 639 throw ccReqDataTruncated; 640 } 641 } 642 643 /** 644 * Define placeholder command handlers for the OEM Extension bytes for the Set 645 * LAN Configuration Parameters and Get LAN Configuration Parameters 646 * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem 647 * functions below to be overridden. 648 * To create handlers for your own proprietary command set: 649 * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto 650 * recipe 651 * Create C++ file(s) that define IPMI handler functions matching the 652 * function names below (i.e. setLanOem). The default name for the 653 * transport IPMI commands is transporthandler_oem.cpp. 654 * Add: 655 * EXTRA_OEMESON:append = "-Dtransport-oem=enabled" 656 * Create a do_configure:prepend()/do_install:append() method in your 657 * bbappend file to copy the file to the build directory. 658 * Add: 659 * PROJECT_SRC_DIR := "${THISDIR}/${PN}" 660 * # Copy the "strong" functions into the working directory, overriding the 661 * # placeholder functions. 662 * do_configure:prepend(){ 663 * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S} 664 * } 665 * 666 * # Clean up after complilation has completed 667 * do_install:append(){ 668 * rm -f ${S}/transporthandler_oem.cpp 669 * } 670 * 671 */ 672 673 /** 674 * Define the placeholder OEM commands as having weak linkage. Create 675 * setLanOem, and getLanOem functions in the transporthandler_oem.cpp 676 * file. The functions defined there must not have the "weak" attribute 677 * applied to them. 678 */ 679 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req) 680 __attribute__((weak)); 681 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter, 682 uint8_t set, uint8_t block) 683 __attribute__((weak)); 684 685 RspType<> setLanOem(uint8_t, uint8_t, message::Payload& req) 686 { 687 req.trailingOk = true; 688 return response(ccParamNotSupported); 689 } 690 691 RspType<message::Payload> getLanOem(uint8_t, uint8_t, uint8_t, uint8_t) 692 { 693 return response(ccParamNotSupported); 694 } 695 696 /** 697 * @brief is a valid LAN channel. 698 * 699 * This function checks whether the input channel is a valid LAN channel or not. 700 * 701 * @param[in] channel: the channel number. 702 * @return nullopt if the channel is invalid, false if the channel is not a LAN 703 * channel, true if the channel is a LAN channel. 704 **/ 705 std::optional<bool> isLanChannel(uint8_t channel) 706 { 707 ChannelInfo chInfo; 708 auto cc = getChannelInfo(channel, chInfo); 709 if (cc != ccSuccess) 710 { 711 return std::nullopt; 712 } 713 714 return chInfo.mediumType == 715 static_cast<uint8_t>(EChannelMediumType::lan8032); 716 } 717 718 RspType<> setLanInt(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1, 719 uint8_t parameter, message::Payload& req) 720 { 721 const uint8_t channel = convertCurrentChannelNum( 722 static_cast<uint8_t>(channelBits), ctx->channel); 723 if (reserved1 || !isValidChannel(channel)) 724 { 725 log<level::ERR>("Set Lan - Invalid field in request"); 726 req.trailingOk = true; 727 return responseInvalidFieldRequest(); 728 } 729 730 if (!isLanChannel(channel).value_or(false)) 731 { 732 log<level::ERR>("Set Lan - Not a LAN channel"); 733 return responseInvalidFieldRequest(); 734 } 735 736 switch (static_cast<LanParam>(parameter)) 737 { 738 case LanParam::SetStatus: 739 { 740 uint2_t flag; 741 uint6_t rsvd; 742 if (req.unpack(flag, rsvd) != 0) 743 { 744 return responseReqDataLenInvalid(); 745 } 746 unpackFinal(req); 747 if (rsvd) 748 { 749 return responseInvalidFieldRequest(); 750 } 751 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); 752 switch (status) 753 { 754 case SetStatus::Complete: 755 { 756 getSetStatus(channel) = status; 757 return responseSuccess(); 758 } 759 case SetStatus::InProgress: 760 { 761 auto& storedStatus = getSetStatus(channel); 762 if (storedStatus == SetStatus::InProgress) 763 { 764 return response(ccParamSetLocked); 765 } 766 storedStatus = status; 767 return responseSuccess(); 768 } 769 case SetStatus::Commit: 770 if (getSetStatus(channel) != SetStatus::InProgress) 771 { 772 return responseInvalidFieldRequest(); 773 } 774 return responseSuccess(); 775 } 776 return response(ccParamNotSupported); 777 } 778 case LanParam::AuthSupport: 779 { 780 req.trailingOk = true; 781 return response(ccParamReadOnly); 782 } 783 case LanParam::AuthEnables: 784 { 785 req.trailingOk = true; 786 return response(ccParamReadOnly); 787 } 788 case LanParam::IP: 789 { 790 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 791 { 792 return responseCommandNotAvailable(); 793 } 794 auto ip = unpackT<stdplus::In4Addr>(req); 795 unpackFinal(req); 796 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); 797 return responseSuccess(); 798 } 799 case LanParam::IPSrc: 800 { 801 uint4_t flag; 802 uint4_t rsvd; 803 if (req.unpack(flag, rsvd) != 0) 804 { 805 return responseReqDataLenInvalid(); 806 } 807 unpackFinal(req); 808 if (rsvd) 809 { 810 return responseInvalidFieldRequest(); 811 } 812 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) 813 { 814 case IPSrc::DHCP: 815 // The IPSrc IPMI command is only for IPv4 816 // management. Modifying IPv6 state is done using 817 // a completely different Set LAN Configuration 818 // subcommand. 819 channelCall<setEthProp<bool>>(channel, "DHCP4", true); 820 return responseSuccess(); 821 case IPSrc::Unspecified: 822 case IPSrc::Static: 823 channelCall<setEthProp<bool>>(channel, "DHCP4", false); 824 return responseSuccess(); 825 case IPSrc::BIOS: 826 case IPSrc::BMC: 827 return responseInvalidFieldRequest(); 828 } 829 return response(ccParamNotSupported); 830 } 831 case LanParam::MAC: 832 { 833 auto mac = unpackT<stdplus::EtherAddr>(req); 834 unpackFinal(req); 835 channelCall<setMACProperty>(channel, mac); 836 return responseSuccess(); 837 } 838 case LanParam::SubnetMask: 839 { 840 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 841 { 842 return responseCommandNotAvailable(); 843 } 844 auto pfx = stdplus::maskToPfx(unpackT<stdplus::In4Addr>(req)); 845 unpackFinal(req); 846 channelCall<reconfigureIfAddr4>(channel, std::nullopt, pfx); 847 return responseSuccess(); 848 } 849 case LanParam::Gateway1: 850 { 851 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 852 { 853 return responseCommandNotAvailable(); 854 } 855 auto gateway = unpackT<stdplus::In4Addr>(req); 856 unpackFinal(req); 857 channelCall<setGatewayProperty<AF_INET>>(channel, gateway); 858 return responseSuccess(); 859 } 860 case LanParam::Gateway1MAC: 861 { 862 auto gatewayMAC = unpackT<stdplus::EtherAddr>(req); 863 unpackFinal(req); 864 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC); 865 return responseSuccess(); 866 } 867 case LanParam::VLANId: 868 { 869 uint12_t vlanData; 870 uint3_t rsvd; 871 bool vlanEnable; 872 873 if (req.unpack(vlanData, rsvd, vlanEnable) != 0) 874 { 875 return responseReqDataLenInvalid(); 876 } 877 unpackFinal(req); 878 879 if (rsvd) 880 { 881 return responseInvalidFieldRequest(); 882 } 883 884 uint16_t vlan = static_cast<uint16_t>(vlanData); 885 886 if (!vlanEnable) 887 { 888 lastDisabledVlan[channel] = vlan; 889 vlan = 0; 890 } 891 892 channelCall<reconfigureVLAN>(channel, vlan); 893 return responseSuccess(); 894 } 895 case LanParam::CiphersuiteSupport: 896 case LanParam::CiphersuiteEntries: 897 case LanParam::IPFamilySupport: 898 { 899 req.trailingOk = true; 900 return response(ccParamReadOnly); 901 } 902 case LanParam::IPFamilyEnables: 903 { 904 uint8_t enables; 905 if (req.unpack(enables) != 0) 906 { 907 return responseReqDataLenInvalid(); 908 } 909 unpackFinal(req); 910 switch (static_cast<IPFamilyEnables>(enables)) 911 { 912 case IPFamilyEnables::DualStack: 913 return responseSuccess(); 914 case IPFamilyEnables::IPv4Only: 915 case IPFamilyEnables::IPv6Only: 916 return response(ccParamNotSupported); 917 } 918 return response(ccParamNotSupported); 919 } 920 case LanParam::IPv6Status: 921 { 922 req.trailingOk = true; 923 return response(ccParamReadOnly); 924 } 925 case LanParam::IPv6StaticAddresses: 926 { 927 uint8_t set; 928 uint7_t rsvd; 929 bool enabled; 930 uint8_t prefix; 931 uint8_t status; 932 if (req.unpack(set, rsvd, enabled) != 0) 933 { 934 return responseReqDataLenInvalid(); 935 } 936 auto ip = unpackT<stdplus::In6Addr>(req); 937 if (req.unpack(prefix, status) != 0) 938 { 939 return responseReqDataLenInvalid(); 940 } 941 unpackFinal(req); 942 if (rsvd) 943 { 944 return responseInvalidFieldRequest(); 945 } 946 if (enabled) 947 { 948 if (prefix < MIN_IPV6_PREFIX_LENGTH || 949 prefix > MAX_IPV6_PREFIX_LENGTH) 950 { 951 return responseParmOutOfRange(); 952 } 953 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix); 954 } 955 else 956 { 957 channelCall<deconfigureIfAddr6>(channel, set); 958 } 959 return responseSuccess(); 960 } 961 case LanParam::IPv6DynamicAddresses: 962 { 963 req.trailingOk = true; 964 return response(ccParamReadOnly); 965 } 966 case LanParam::IPv6RouterControl: 967 { 968 std::bitset<8> control; 969 constexpr uint8_t reservedRACCBits = 0xfc; 970 if (req.unpack(control) != 0) 971 { 972 return responseReqDataLenInvalid(); 973 } 974 unpackFinal(req); 975 if (std::bitset<8> expected(control & 976 std::bitset<8>(reservedRACCBits)); 977 expected.any()) 978 { 979 return response(ccParamNotSupported); 980 } 981 982 bool enableRA = control[IPv6RouterControlFlag::Dynamic]; 983 channelCall<setEthProp<bool>>(channel, "IPv6AcceptRA", enableRA); 984 channelCall<setEthProp<bool>>(channel, "DHCP6", enableRA); 985 return responseSuccess(); 986 } 987 case LanParam::IPv6StaticRouter1IP: 988 { 989 auto gateway = unpackT<stdplus::In6Addr>(req); 990 unpackFinal(req); 991 channelCall<setGatewayProperty<AF_INET6>>(channel, gateway); 992 return responseSuccess(); 993 } 994 case LanParam::IPv6StaticRouter1MAC: 995 { 996 auto mac = unpackT<stdplus::EtherAddr>(req); 997 unpackFinal(req); 998 channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac); 999 return responseSuccess(); 1000 } 1001 case LanParam::IPv6StaticRouter1PrefixLength: 1002 { 1003 uint8_t prefix; 1004 if (req.unpack(prefix) != 0) 1005 { 1006 return responseReqDataLenInvalid(); 1007 } 1008 unpackFinal(req); 1009 if (prefix != 0) 1010 { 1011 return responseInvalidFieldRequest(); 1012 } 1013 return responseSuccess(); 1014 } 1015 case LanParam::IPv6StaticRouter1PrefixValue: 1016 { 1017 unpackT<stdplus::In6Addr>(req); 1018 unpackFinal(req); 1019 // Accept any prefix value since our prefix length has to be 0 1020 return responseSuccess(); 1021 } 1022 case LanParam::cipherSuitePrivilegeLevels: 1023 { 1024 uint8_t rsvd; 1025 std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs; 1026 1027 if (req.unpack(rsvd, cipherSuitePrivs)) 1028 { 1029 return responseReqDataLenInvalid(); 1030 } 1031 unpackFinal(req); 1032 1033 if (rsvd) 1034 { 1035 return responseInvalidFieldRequest(); 1036 } 1037 1038 uint8_t resp = getCipherConfigObject(csPrivFileName, 1039 csPrivDefaultFileName) 1040 .setCSPrivilegeLevels(channel, cipherSuitePrivs); 1041 if (!resp) 1042 { 1043 return responseSuccess(); 1044 } 1045 else 1046 { 1047 req.trailingOk = true; 1048 return response(resp); 1049 } 1050 } 1051 } 1052 1053 if (parameter >= oemCmdStart) 1054 { 1055 return setLanOem(channel, parameter, req); 1056 } 1057 1058 req.trailingOk = true; 1059 return response(ccParamNotSupported); 1060 } 1061 1062 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1, 1063 uint8_t parameter, message::Payload& req) 1064 { 1065 try 1066 { 1067 return setLanInt(ctx, channelBits, reserved1, parameter, req); 1068 } 1069 catch (ipmi::Cc cc) 1070 { 1071 return response(cc); 1072 } 1073 catch (const sdbusplus::exception_t& e) 1074 { 1075 if (std::string_view{InvalidArgument::errName} == e.name()) 1076 { 1077 return responseInvalidFieldRequest(); 1078 } 1079 throw; 1080 } 1081 } 1082 1083 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits, 1084 uint3_t reserved, bool revOnly, 1085 uint8_t parameter, uint8_t set, uint8_t block) 1086 { 1087 message::Payload ret; 1088 constexpr uint8_t current_revision = 0x11; 1089 ret.pack(current_revision); 1090 1091 if (revOnly) 1092 { 1093 return responseSuccess(std::move(ret)); 1094 } 1095 1096 const uint8_t channel = convertCurrentChannelNum( 1097 static_cast<uint8_t>(channelBits), ctx->channel); 1098 if (reserved || !isValidChannel(channel)) 1099 { 1100 log<level::ERR>("Get Lan - Invalid field in request"); 1101 return responseInvalidFieldRequest(); 1102 } 1103 1104 if (!isLanChannel(channel).value_or(false)) 1105 { 1106 log<level::ERR>("Set Lan - Not a LAN channel"); 1107 return responseInvalidFieldRequest(); 1108 } 1109 1110 static std::vector<uint8_t> cipherList; 1111 static bool listInit = false; 1112 if (!listInit) 1113 { 1114 try 1115 { 1116 cipherList = cipher::getCipherList(); 1117 listInit = true; 1118 } 1119 catch (const std::exception& e) 1120 {} 1121 } 1122 1123 switch (static_cast<LanParam>(parameter)) 1124 { 1125 case LanParam::SetStatus: 1126 { 1127 SetStatus status; 1128 try 1129 { 1130 status = setStatus.at(channel); 1131 } 1132 catch (const std::out_of_range&) 1133 { 1134 status = SetStatus::Complete; 1135 } 1136 ret.pack(types::enum_cast<uint2_t>(status), uint6_t{}); 1137 return responseSuccess(std::move(ret)); 1138 } 1139 case LanParam::AuthSupport: 1140 { 1141 std::bitset<6> support; 1142 ret.pack(support, uint2_t{}); 1143 return responseSuccess(std::move(ret)); 1144 } 1145 case LanParam::AuthEnables: 1146 { 1147 std::bitset<6> enables; 1148 ret.pack(enables, uint2_t{}); // Callback 1149 ret.pack(enables, uint2_t{}); // User 1150 ret.pack(enables, uint2_t{}); // Operator 1151 ret.pack(enables, uint2_t{}); // Admin 1152 ret.pack(enables, uint2_t{}); // OEM 1153 return responseSuccess(std::move(ret)); 1154 } 1155 case LanParam::IP: 1156 { 1157 auto ifaddr = channelCall<getIfAddr4>(channel); 1158 stdplus::In4Addr addr{}; 1159 if (ifaddr) 1160 { 1161 addr = ifaddr->address; 1162 } 1163 ret.pack(stdplus::raw::asView<char>(addr)); 1164 return responseSuccess(std::move(ret)); 1165 } 1166 case LanParam::IPSrc: 1167 { 1168 auto src = channelCall<getEthProp<bool>>(channel, "DHCP4") 1169 ? IPSrc::DHCP 1170 : IPSrc::Static; 1171 ret.pack(types::enum_cast<uint4_t>(src), uint4_t{}); 1172 return responseSuccess(std::move(ret)); 1173 } 1174 case LanParam::MAC: 1175 { 1176 auto mac = channelCall<getMACProperty>(channel); 1177 ret.pack(stdplus::raw::asView<char>(mac)); 1178 return responseSuccess(std::move(ret)); 1179 } 1180 case LanParam::SubnetMask: 1181 { 1182 auto ifaddr = channelCall<getIfAddr4>(channel); 1183 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; 1184 if (ifaddr) 1185 { 1186 prefix = ifaddr->prefix; 1187 } 1188 auto netmask = stdplus::pfxToMask<stdplus::In4Addr>(prefix); 1189 ret.pack(stdplus::raw::asView<char>(netmask)); 1190 return responseSuccess(std::move(ret)); 1191 } 1192 case LanParam::Gateway1: 1193 { 1194 auto gateway = channelCall<getGatewayProperty<AF_INET>>(channel); 1195 ret.pack(stdplus::raw::asView<char>( 1196 gateway.value_or(stdplus::In4Addr{}))); 1197 return responseSuccess(std::move(ret)); 1198 } 1199 case LanParam::Gateway1MAC: 1200 { 1201 stdplus::EtherAddr mac{}; 1202 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel); 1203 if (neighbor) 1204 { 1205 mac = neighbor->mac; 1206 } 1207 ret.pack(stdplus::raw::asView<char>(mac)); 1208 return responseSuccess(std::move(ret)); 1209 } 1210 case LanParam::VLANId: 1211 { 1212 uint16_t vlan = channelCall<getVLANProperty>(channel); 1213 if (vlan != 0) 1214 { 1215 vlan |= VLAN_ENABLE_FLAG; 1216 } 1217 else 1218 { 1219 vlan = lastDisabledVlan[channel]; 1220 } 1221 ret.pack(vlan); 1222 return responseSuccess(std::move(ret)); 1223 } 1224 case LanParam::CiphersuiteSupport: 1225 { 1226 if (getChannelSessionSupport(channel) == 1227 EChannelSessSupported::none) 1228 { 1229 return responseInvalidFieldRequest(); 1230 } 1231 if (!listInit) 1232 { 1233 return responseUnspecifiedError(); 1234 } 1235 ret.pack(static_cast<uint8_t>(cipherList.size() - 1)); 1236 return responseSuccess(std::move(ret)); 1237 } 1238 case LanParam::CiphersuiteEntries: 1239 { 1240 if (getChannelSessionSupport(channel) == 1241 EChannelSessSupported::none) 1242 { 1243 return responseInvalidFieldRequest(); 1244 } 1245 if (!listInit) 1246 { 1247 return responseUnspecifiedError(); 1248 } 1249 ret.pack(cipherList); 1250 return responseSuccess(std::move(ret)); 1251 } 1252 case LanParam::IPFamilySupport: 1253 { 1254 std::bitset<8> support; 1255 support[IPFamilySupportFlag::IPv6Only] = 0; 1256 support[IPFamilySupportFlag::DualStack] = 1; 1257 support[IPFamilySupportFlag::IPv6Alerts] = 1; 1258 ret.pack(support); 1259 return responseSuccess(std::move(ret)); 1260 } 1261 case LanParam::IPFamilyEnables: 1262 { 1263 ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack)); 1264 return responseSuccess(std::move(ret)); 1265 } 1266 case LanParam::IPv6Status: 1267 { 1268 ret.pack(MAX_IPV6_STATIC_ADDRESSES); 1269 ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES); 1270 std::bitset<8> support; 1271 support[IPv6StatusFlag::DHCP] = 1; 1272 support[IPv6StatusFlag::SLAAC] = 1; 1273 ret.pack(support); 1274 return responseSuccess(std::move(ret)); 1275 } 1276 case LanParam::IPv6StaticAddresses: 1277 { 1278 if (set >= MAX_IPV6_STATIC_ADDRESSES) 1279 { 1280 return responseParmOutOfRange(); 1281 } 1282 getLanIPv6Address(ret, channel, set, originsV6Static); 1283 return responseSuccess(std::move(ret)); 1284 } 1285 case LanParam::IPv6DynamicAddresses: 1286 { 1287 if (set >= MAX_IPV6_DYNAMIC_ADDRESSES) 1288 { 1289 return responseParmOutOfRange(); 1290 } 1291 getLanIPv6Address(ret, channel, set, originsV6Dynamic); 1292 return responseSuccess(std::move(ret)); 1293 } 1294 case LanParam::IPv6RouterControl: 1295 { 1296 std::bitset<8> control; 1297 control[IPv6RouterControlFlag::Dynamic] = 1298 channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA"); 1299 control[IPv6RouterControlFlag::Static] = 1; 1300 ret.pack(control); 1301 return responseSuccess(std::move(ret)); 1302 } 1303 case LanParam::IPv6StaticRouter1IP: 1304 { 1305 stdplus::In6Addr gateway{}; 1306 if (!channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA")) 1307 { 1308 gateway = 1309 channelCall<getGatewayProperty<AF_INET6>>(channel).value_or( 1310 stdplus::In6Addr{}); 1311 } 1312 ret.pack(stdplus::raw::asView<char>(gateway)); 1313 return responseSuccess(std::move(ret)); 1314 } 1315 case LanParam::IPv6StaticRouter1MAC: 1316 { 1317 stdplus::EtherAddr mac{}; 1318 auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel); 1319 if (neighbor) 1320 { 1321 mac = neighbor->mac; 1322 } 1323 ret.pack(stdplus::raw::asView<char>(mac)); 1324 return responseSuccess(std::move(ret)); 1325 } 1326 case LanParam::IPv6StaticRouter1PrefixLength: 1327 { 1328 ret.pack(UINT8_C(0)); 1329 return responseSuccess(std::move(ret)); 1330 } 1331 case LanParam::IPv6StaticRouter1PrefixValue: 1332 { 1333 ret.pack(stdplus::raw::asView<char>(stdplus::In6Addr{})); 1334 return responseSuccess(std::move(ret)); 1335 } 1336 case LanParam::cipherSuitePrivilegeLevels: 1337 { 1338 std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels; 1339 1340 uint8_t resp = 1341 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName) 1342 .getCSPrivilegeLevels(channel, csPrivilegeLevels); 1343 if (!resp) 1344 { 1345 constexpr uint8_t reserved1 = 0x00; 1346 ret.pack(reserved1, csPrivilegeLevels); 1347 return responseSuccess(std::move(ret)); 1348 } 1349 else 1350 { 1351 return response(resp); 1352 } 1353 } 1354 } 1355 1356 if (parameter >= oemCmdStart) 1357 { 1358 return getLanOem(channel, parameter, set, block); 1359 } 1360 1361 return response(ccParamNotSupported); 1362 } 1363 1364 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; 1365 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; 1366 constexpr const uint16_t solDefaultPort = 623; 1367 1368 RspType<> setSolConfParams(Context::ptr ctx, uint4_t channelBits, 1369 uint4_t /*reserved*/, uint8_t parameter, 1370 message::Payload& req) 1371 { 1372 const uint8_t channel = convertCurrentChannelNum( 1373 static_cast<uint8_t>(channelBits), ctx->channel); 1374 1375 if (!isValidChannel(channel)) 1376 { 1377 log<level::ERR>("Set Sol Config - Invalid channel in request"); 1378 return responseInvalidFieldRequest(); 1379 } 1380 1381 std::string solService{}; 1382 std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel); 1383 1384 if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService)) 1385 { 1386 log<level::ERR>("Set Sol Config - Invalid solInterface", 1387 entry("SERVICE=%s", solService.c_str()), 1388 entry("OBJPATH=%s", solPathWitheEthName.c_str()), 1389 entry("INTERFACE=%s", solInterface)); 1390 return responseInvalidFieldRequest(); 1391 } 1392 1393 switch (static_cast<SolConfParam>(parameter)) 1394 { 1395 case SolConfParam::Progress: 1396 { 1397 uint8_t progress; 1398 if (req.unpack(progress) != 0 || !req.fullyUnpacked()) 1399 { 1400 return responseReqDataLenInvalid(); 1401 } 1402 1403 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1404 solInterface, "Progress", progress)) 1405 { 1406 return responseUnspecifiedError(); 1407 } 1408 break; 1409 } 1410 case SolConfParam::Enable: 1411 { 1412 bool enable; 1413 uint7_t reserved2; 1414 1415 if (req.unpack(enable, reserved2) != 0 || !req.fullyUnpacked()) 1416 { 1417 return responseReqDataLenInvalid(); 1418 } 1419 1420 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1421 solInterface, "Enable", enable)) 1422 { 1423 return responseUnspecifiedError(); 1424 } 1425 break; 1426 } 1427 case SolConfParam::Authentication: 1428 { 1429 uint4_t privilegeBits{}; 1430 uint2_t reserved2{}; 1431 bool forceAuth = false; 1432 bool forceEncrypt = false; 1433 1434 if (req.unpack(privilegeBits, reserved2, forceAuth, forceEncrypt) != 1435 0 || 1436 !req.fullyUnpacked()) 1437 { 1438 return responseReqDataLenInvalid(); 1439 } 1440 1441 uint8_t privilege = static_cast<uint8_t>(privilegeBits); 1442 if (privilege < static_cast<uint8_t>(Privilege::User) || 1443 privilege > static_cast<uint8_t>(Privilege::Oem)) 1444 { 1445 return ipmi::responseInvalidFieldRequest(); 1446 } 1447 1448 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1449 solInterface, "Privilege", privilege)) 1450 { 1451 return responseUnspecifiedError(); 1452 } 1453 1454 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1455 solInterface, "ForceEncryption", 1456 forceEncrypt)) 1457 { 1458 return responseUnspecifiedError(); 1459 } 1460 1461 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1462 solInterface, "ForceAuthentication", 1463 forceAuth)) 1464 { 1465 return responseUnspecifiedError(); 1466 } 1467 break; 1468 } 1469 case SolConfParam::Accumulate: 1470 { 1471 uint8_t interval; 1472 uint8_t threshold; 1473 if (req.unpack(interval, threshold) != 0 || !req.fullyUnpacked()) 1474 { 1475 return responseReqDataLenInvalid(); 1476 } 1477 1478 if (threshold == 0) 1479 { 1480 return responseInvalidFieldRequest(); 1481 } 1482 1483 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1484 solInterface, "AccumulateIntervalMS", 1485 interval)) 1486 { 1487 return responseUnspecifiedError(); 1488 } 1489 1490 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1491 solInterface, "Threshold", threshold)) 1492 { 1493 return responseUnspecifiedError(); 1494 } 1495 break; 1496 } 1497 case SolConfParam::Retry: 1498 { 1499 uint3_t countBits; 1500 uint5_t reserved2; 1501 uint8_t interval; 1502 1503 if (req.unpack(countBits, reserved2, interval) != 0 || 1504 !req.fullyUnpacked()) 1505 { 1506 return responseReqDataLenInvalid(); 1507 } 1508 1509 uint8_t count = static_cast<uint8_t>(countBits); 1510 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1511 solInterface, "RetryCount", count)) 1512 { 1513 return responseUnspecifiedError(); 1514 } 1515 1516 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1517 solInterface, "RetryIntervalMS", 1518 interval)) 1519 { 1520 return responseUnspecifiedError(); 1521 } 1522 break; 1523 } 1524 case SolConfParam::Port: 1525 { 1526 return response(ipmiCCWriteReadParameter); 1527 } 1528 case SolConfParam::NonVbitrate: 1529 case SolConfParam::Vbitrate: 1530 case SolConfParam::Channel: 1531 default: 1532 return response(ipmiCCParamNotSupported); 1533 } 1534 return responseSuccess(); 1535 } 1536 1537 RspType<message::Payload> getSolConfParams(Context::ptr ctx, 1538 uint4_t channelBits, 1539 uint3_t /*reserved*/, bool revOnly, 1540 uint8_t parameter, uint8_t /*set*/, 1541 uint8_t /*block*/) 1542 { 1543 message::Payload ret; 1544 constexpr uint8_t current_revision = 0x11; 1545 ret.pack(current_revision); 1546 if (revOnly) 1547 { 1548 return responseSuccess(std::move(ret)); 1549 } 1550 1551 const uint8_t channel = convertCurrentChannelNum( 1552 static_cast<uint8_t>(channelBits), ctx->channel); 1553 1554 if (!isValidChannel(channel)) 1555 { 1556 log<level::ERR>("Get Sol Config - Invalid channel in request"); 1557 return responseInvalidFieldRequest(); 1558 } 1559 1560 std::string solService{}; 1561 std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel); 1562 1563 if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService)) 1564 { 1565 log<level::ERR>("Set Sol Config - Invalid solInterface", 1566 entry("SERVICE=%s", solService.c_str()), 1567 entry("OBJPATH=%s", solPathWitheEthName.c_str()), 1568 entry("INTERFACE=%s", solInterface)); 1569 return responseInvalidFieldRequest(); 1570 } 1571 1572 switch (static_cast<SolConfParam>(parameter)) 1573 { 1574 case SolConfParam::Progress: 1575 { 1576 uint8_t progress; 1577 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1578 solInterface, "Progress", progress)) 1579 { 1580 return responseUnspecifiedError(); 1581 } 1582 ret.pack(progress); 1583 return responseSuccess(std::move(ret)); 1584 } 1585 case SolConfParam::Enable: 1586 { 1587 bool enable{}; 1588 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1589 solInterface, "Enable", enable)) 1590 { 1591 return responseUnspecifiedError(); 1592 } 1593 ret.pack(enable, uint7_t{}); 1594 return responseSuccess(std::move(ret)); 1595 } 1596 case SolConfParam::Authentication: 1597 { 1598 // 4bits, cast when pack 1599 uint8_t privilege; 1600 bool forceAuth = false; 1601 bool forceEncrypt = false; 1602 1603 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1604 solInterface, "Privilege", privilege)) 1605 { 1606 return responseUnspecifiedError(); 1607 } 1608 1609 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1610 solInterface, "ForceAuthentication", 1611 forceAuth)) 1612 { 1613 return responseUnspecifiedError(); 1614 } 1615 1616 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1617 solInterface, "ForceEncryption", 1618 forceEncrypt)) 1619 { 1620 return responseUnspecifiedError(); 1621 } 1622 ret.pack(uint4_t{privilege}, uint2_t{}, forceAuth, forceEncrypt); 1623 return responseSuccess(std::move(ret)); 1624 } 1625 case SolConfParam::Accumulate: 1626 { 1627 uint8_t interval{}, threshold{}; 1628 1629 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1630 solInterface, "AccumulateIntervalMS", 1631 interval)) 1632 { 1633 return responseUnspecifiedError(); 1634 } 1635 1636 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1637 solInterface, "Threshold", threshold)) 1638 { 1639 return responseUnspecifiedError(); 1640 } 1641 ret.pack(interval, threshold); 1642 return responseSuccess(std::move(ret)); 1643 } 1644 case SolConfParam::Retry: 1645 { 1646 // 3bits, cast when cast 1647 uint8_t count{}; 1648 uint8_t interval{}; 1649 1650 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1651 solInterface, "RetryCount", count)) 1652 { 1653 return responseUnspecifiedError(); 1654 } 1655 1656 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1657 solInterface, "RetryIntervalMS", 1658 interval)) 1659 { 1660 return responseUnspecifiedError(); 1661 } 1662 ret.pack(uint3_t{count}, uint5_t{}, interval); 1663 return responseSuccess(std::move(ret)); 1664 } 1665 case SolConfParam::Port: 1666 { 1667 auto port = solDefaultPort; 1668 ret.pack(static_cast<uint16_t>(port)); 1669 return responseSuccess(std::move(ret)); 1670 } 1671 case SolConfParam::Channel: 1672 { 1673 ret.pack(channel); 1674 return responseSuccess(std::move(ret)); 1675 } 1676 case SolConfParam::NonVbitrate: 1677 { 1678 uint64_t baudRate; 1679 uint8_t encodedBitRate = 0; 1680 if (ipmi::getDbusProperty( 1681 ctx, "xyz.openbmc_project.Console.default", 1682 "/xyz/openbmc_project/console/default", 1683 "xyz.openbmc_project.Console.UART", "Baud", baudRate)) 1684 { 1685 return ipmi::responseUnspecifiedError(); 1686 } 1687 switch (baudRate) 1688 { 1689 case 9600: 1690 encodedBitRate = 0x06; 1691 break; 1692 case 19200: 1693 encodedBitRate = 0x07; 1694 break; 1695 case 38400: 1696 encodedBitRate = 0x08; 1697 break; 1698 case 57600: 1699 encodedBitRate = 0x09; 1700 break; 1701 case 115200: 1702 encodedBitRate = 0x0a; 1703 break; 1704 default: 1705 break; 1706 } 1707 ret.pack(encodedBitRate); 1708 return responseSuccess(std::move(ret)); 1709 } 1710 case SolConfParam::Vbitrate: 1711 default: 1712 return response(ipmiCCParamNotSupported); 1713 } 1714 1715 return response(ccParamNotSupported); 1716 } 1717 1718 } // namespace transport 1719 } // namespace ipmi 1720 1721 void register_netfn_transport_functions() __attribute__((constructor)); 1722 1723 void register_netfn_transport_functions() 1724 { 1725 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1726 ipmi::transport::cmdSetLanConfigParameters, 1727 ipmi::Privilege::Admin, ipmi::transport::setLan); 1728 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1729 ipmi::transport::cmdGetLanConfigParameters, 1730 ipmi::Privilege::Operator, ipmi::transport::getLan); 1731 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1732 ipmi::transport::cmdSetSolConfigParameters, 1733 ipmi::Privilege::Admin, 1734 ipmi::transport::setSolConfParams); 1735 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1736 ipmi::transport::cmdGetSolConfigParameters, 1737 ipmi::Privilege::User, 1738 ipmi::transport::getSolConfParams); 1739 } 1740